<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Go devlog</title>
    <link>https://gobae.tistory.com/</link>
    <description>하루에 한 걸음씩</description>
    <language>ko</language>
    <pubDate>Sun, 28 Jun 2026 22:15:27 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>jiho_bae</managingEditor>
    <image>
      <title>Go devlog</title>
      <url>https://tistory1.daumcdn.net/tistory/4359146/attach/2f1eab7755c94df487bfc4267a7f4b56</url>
      <link>https://gobae.tistory.com</link>
    </image>
    <item>
      <title>[프로그래머스] 리코쳇 로봇 - 자바스크립트</title>
      <link>https://gobae.tistory.com/160</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chHVyP/btr4vowzaZ1/bPZALfggJ28uCYKWMirpv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chHVyP/btr4vowzaZ1/bPZALfggJ28uCYKWMirpv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chHVyP/btr4vowzaZ1/bPZALfggJ28uCYKWMirpv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchHVyP%2Fbtr4vowzaZ1%2FbPZALfggJ28uCYKWMirpv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;786&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1679121025631&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/169199&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bOfKeT/hyRXu9jnNF/bHamfzSYT4juwwZaSy3y80/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/FFLO0/hyRY5NEjR0/QJD4Q9UKsZeYG8MahXokSk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/169199&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/169199&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bOfKeT/hyRXu9jnNF/bHamfzSYT4juwwZaSy3y80/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/FFLO0/hyRY5NEjR0/QJD4Q9UKsZeYG8MahXokSk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1679121129515&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(board) {
  const row = board.length;
  const col = board[0].length;
  const [dx, dy] = [
    [-1, 0, 1, 0],
    [0, 1, 0, -1],
  ];
  const NON_DIR = -10;
  const movedMap = {};
  const BLOCK = {
    empty: '.',
    start: 'R',
    obstacle: 'D',
    goal: 'G',
  };

  function getStartPos(board) {
    for (let i = 0; i &amp;lt; row; i++) {
      for (let j = 0; j &amp;lt; col; j++) {
        if (board[i][j] === BLOCK.start) return [i, j];
      }
    }
  }

  function endOfBoard(x, y) {
    if (x &amp;lt; 0 || x &amp;gt;= row || y &amp;lt; 0 || y &amp;gt;= col) return true;
    if (board[x][y] === BLOCK.obstacle) return true;
    return false;
  }

  function getReverseDir(dir) {
    return (dir + 2) % 4;
  }

  function checkGoal(x, y) {
    return board[x][y] === BLOCK.goal;
  }

  const startPos = getStartPos(board);
  board[startPos[0]][startPos[1]] = BLOCK.empty;

  function bfs() {
    const arr = [[...startPos, NON_DIR, 0]];
    let idx = 0;

    while (1) {
      if (arr.length - 1 &amp;lt; idx) return;

      const [x, y, dir, cnt] = arr[idx++];
      const movedPath = `${x}/${y}/${dir}`;
      if (movedMap[movedPath]) continue;
      movedMap[movedPath] = true;

      for (let i = 0; i &amp;lt; 4; i++) {
        let [nx, ny] = [x + dx[i], y + dy[i]];

        if (dir !== NON_DIR) {
          if (dir === i) continue;
          if (getReverseDir(dir) === i) continue;
        }
        if (endOfBoard(nx, ny)) continue;

        while (!endOfBoard(nx + dx[i], ny + dy[i])) {
          [nx, ny] = [nx + dx[i], ny + dy[i]];
        }

        if (checkGoal(nx, ny)) return cnt + 1;
        arr.push([nx, ny, i, cnt + 1]);
      }
    }
  }

  return bfs() ?? -1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;문제 조건&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;리코쳇 로봇&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;격자모양 게임판 위에서 말을 움직이는 게임.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;시작 -&amp;gt; 목표까지 최소 도달수를 구해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;상하좌우로 움직이며 장애물/맨끝에 부딪힐 때 까지 미끄러지는게 1번의 이동이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;게임판에서 .은 빈공간, R은 처음 위치, D는 장애물, G는 목표지점&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;게임판 행렬 board가 주어졌을 때, 최소 몇번 이동해야하는지 구하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;목표위치에 도달 불가능하다면 -1 을 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;제한사항&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;3&amp;lt;=board.length&amp;lt;=100&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;3&amp;lt;=board element &amp;lt;= 100&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;board element(string)의 크기 동일&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이동방법 : 상하좌우&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;풀이방향&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;코드가 굉장히 난잡해졌음을 고려하면, 더 좋은 풀이방법이 있을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이렇게도 무식하게 풀 수 있구나 정도만 참고하기를 바란다. ^^;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;필자가 생각한 풀이 시나리오는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;1. 끝까지 미끄러져야 하므로 이동방향이 빈공간(.)이거나 목표지점(G)이라면 계속 이동한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;2. 더이상 이동할 수 없을 때 현재 위치를 파악한다. 현재 위치가 G이면 정답이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;3. 다음 이동을 정할 때 미끄러진 방향과 미끄러진 반대방향을 제외한 2가지 방향중 선택한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;4. 다시 1번으로 돌아가 이동방향으로 미끄러져 나간다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;우선 가장 짧게 도착한 경우에 바로 끝내기 위해 bfs로 접근하기로 했다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;자바스크립트로 bfs를 활용할 때 주의할 점은 제대로 동작할 큐를 구현해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;아니면 배열에 다음 좌표를 쌓아가면서 인덱스를 변경시키는 방법을 쓰는게 좋아보인다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;레벨2짜리 문제에 큐를 구현해 넣기엔 민망해서, 배열 인덱스 방법을 활용하기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;문제에는 &quot;도달할 수 없는 경우&quot;라는 변수가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;도달할 수 없는 경우 탈출을 위해 2가지 탈출방법을 떠올렸다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;1) 최대 미끄러지는 횟수를 정해보자. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;board가 100x100이므로 어느정도 반복했다면 이미 지나온 길을 반복하고 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;놀라운 예지력으로 100만~1000만정도의 반복수를 정해보면 되지 않을까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;2) memoizatoin을 활용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;만약 &lt;b&gt;&quot;(1,2) 좌표에서 왼쪽방향으로 이동&quot; &lt;/b&gt;이라는 정보를 기록해두었다고 가정하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이 정보가 한번 더 출현한 경우라면 더이상 미끄러져야할 이유가 없는 경우다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이 경우에는 더이상 상하좌우의 탐색을 진행하지 말고 다음 큐의 원소로 이동하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;1번 케이스로 찍신이 되는 방법도 좋지만, 2번이 조금 더 합리적으로 보이니 2번으로 탈출조건을 설정해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;요약하면 다음과 같다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;가장 짧은 경로를 구하기 위해 bfs를 활용해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;단 Array.prototype.shift를 쓰기보다는 배열에 쌓인 순서대로 idx를 1씩 증가시키며 검증하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이동방향대로 미끄러 질 때는 루프를 돌아준다. board의 끝이거나, 장애물(D)일 때 현 위치에서 멈춘다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;현 위치에서 목표지점인지 검증한다. 목표지점이 아니라면, 큐(배열)에 [현재 좌표, 이동했던 방향, 이동횟수] 를 넣어준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도달할 수 없는 경우를 검출하기 위해 &quot;현재좌표/이동방향&quot; 정보를 기록한다. 이 정보가 반복되는 경우는 더이상 미끄러질 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bfs의 결과가 나왔다면 그대로 출력하면 되고, 결과가 나오지 않았다면 -1을 반환하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 의식의 흐름대로 코드를 다시 이해해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주석 첨부 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1679122332992&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(board) {
  const row = board.length;
  const col = board[0].length;
  const [dx, dy] = [
    [-1, 0, 1, 0],
    [0, 1, 0, -1],
  ]; // 이동방향에 따른 상하좌우 이동크기 dx,dy
  const NON_DIR = -10; // 맨 처음 시작점에선 방향이 없다.
  const movedMap = {}; // 좌표,이동방향을 기록할 memoization Map이다.
  const BLOCK = {
    empty: '.',
    start: 'R',
    obstacle: 'D',
    goal: 'G',
  }; // 상수 값은 모아두면 좋다.

  function getStartPos(board) {
    for (let i = 0; i &amp;lt; row; i++) {
      for (let j = 0; j &amp;lt; col; j++) {
        if (board[i][j] === BLOCK.start) return [i, j];
      } // 시작지점을 찾는 함수.
    }
  }

  function endOfBoard(x, y) {
    if (x &amp;lt; 0 || x &amp;gt;= row || y &amp;lt; 0 || y &amp;gt;= col) return true;
    if (board[x][y] === BLOCK.obstacle) return true;
    return false;
  } // 다음 이동할 위치가 board의 끝이거나, 장애물인지 판단하는 함수.

  function getReverseDir(dir) {
    return (dir + 2) % 4;
  } // 현재 진행방향과 반대방향의 dir를 반환하는 함수.

  function checkGoal(x, y) {
    return board[x][y] === BLOCK.goal;
  } // 현재 위치가 목표지점인지 검증하는 함수

  const startPos = getStartPos(board);
  board[startPos[0]][startPos[1]] = BLOCK.empty;
	// 시작지점을 찾고, bfs의 원할한 진행을 위해 시작지점을 빈공간으로 변경했다.

function bfs() {
    const arr = [[...startPos, NON_DIR, 0]];
    let idx = 0;
	// [시작x, 시작y, 방향, 이동횟수]를 담고, 배열idx를 0으로 설정
    
    while (1) {
      if (arr.length - 1 &amp;lt; idx) return;
	  // 더이상 검증할 수 있는 위치가 없다면 리턴

	  const [x, y, dir, cnt] = arr[idx++];
      const movedPath = `${x}/${y}/${dir}`;
      if (movedMap[movedPath]) continue;
      movedMap[movedPath] = true;
	  // 현재위치/이동방향을 기록하고 이미 반복한 정보라면 다음경우로 넘어간다.
        
      for (let i = 0; i &amp;lt; 4; i++) {
        let [nx, ny] = [x + dx[i], y + dy[i]];
		// i(방향)에 따라 다음 이동좌표가 이동가능한지 탐색해야한다.
        
        if (dir !== NON_DIR) {
          if (dir === i) continue;
          if (getReverseDir(dir) === i) continue;
          // 첫출발이 아니고, 이전의 이동방향이거나 이전 이동방향의 반대방향이면 pass.
        }
        if (endOfBoard(nx, ny)) continue;
        // 다음 이동방향이 board를 벗어났거나, 장애물이라면 갈 수 없다.

        while (!endOfBoard(nx + dx[i], ny + dy[i])) {
          [nx, ny] = [nx + dx[i], ny + dy[i]];
        } // 현재 가려는 방향(i)로 최대한 이동한다.
        // 다음 좌표가 board를 벗어났거나 장애물일 때 까지 이동할 것이다.

        if (checkGoal(nx, ny)) return cnt + 1;
        // 그렇게 멈춘 좌표가 목표지점이면 정답이다.
        arr.push([nx, ny, i, cnt + 1]);
        // 현재 좌표가 목표지점이 아니라면, 다음 탐색을 위해 arr에 넣어준다.
      }
    }
  }

  return bfs() ?? -1;
  // bfs가 숫자를 리턴했다면 정답이고, undefined를 리턴했다면 -1을 반환해주면 된다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/programmers</category>
      <category>리코쳇 로봇 javascript</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/160</guid>
      <comments>https://gobae.tistory.com/160#entry160comment</comments>
      <pubDate>Sat, 18 Mar 2023 15:52:54 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 덧칠하기 - 자바스크립트</title>
      <link>https://gobae.tistory.com/159</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1520&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hvm6v/btr3ezSXnWa/Q5KQDNEnKALgqAF7O5WsO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hvm6v/btr3ezSXnWa/Q5KQDNEnKALgqAF7O5WsO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hvm6v/btr3ezSXnWa/Q5KQDNEnKALgqAF7O5WsO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHvm6v%2Fbtr3ezSXnWa%2FQ5KQDNEnKALgqAF7O5WsO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;794&quot; data-origin-width=&quot;1520&quot; data-origin-height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1678524566488&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/161989&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pcG9b/hyRTOy7pm1/lJqUeSHq5vWsLIK7CSUI21/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/rDAj2/hyRTRP9jbK/pidVoxaIOLX41n78OhjBA1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/161989&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/161989&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pcG9b/hyRTOy7pm1/lJqUeSHq5vWsLIK7CSUI21/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/rDAj2/hyRTRP9jbK/pidVoxaIOLX41n78OhjBA1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1678524540563&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(n, m, section) {
   var answer = 0;
   const rollerLen = m - 1;
   let base = section[section.length - 1];
   let cur;

   answer += 1;

   while (section.length) {
     cur = section.pop();

     if (base - cur &amp;lt;= rollerLen) continue;
     base = cur;
     answer += 1;
   }

   return answer;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제조건&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페인트가&amp;nbsp;칠해진&amp;nbsp;길이&amp;nbsp;n미터&amp;nbsp;벽이 있다.&lt;br /&gt;벽&amp;nbsp;전체에&amp;nbsp;페인트칠하는&amp;nbsp;것&amp;nbsp;대신,&amp;nbsp;일부만 칠하기로 한다.&lt;br /&gt;벽을&amp;nbsp;1미터로&amp;nbsp;n개&amp;nbsp;구간으로&amp;nbsp;나누고,&amp;nbsp;칠해야&amp;nbsp;할&amp;nbsp;구역을&amp;nbsp;정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페인트&amp;nbsp;칠하는&amp;nbsp;롤러는&amp;nbsp;m미터이고,&lt;br /&gt;&amp;nbsp;-&amp;nbsp;롤러가&amp;nbsp;벽에서&amp;nbsp;벗어나면&amp;nbsp;안된다.&lt;br /&gt;&amp;nbsp;-&amp;nbsp;구역&amp;nbsp;일부분만&amp;nbsp;포함되도록&amp;nbsp;칠하면&amp;nbsp;안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한&amp;nbsp;구역에&amp;nbsp;페인트를&amp;nbsp;여러번&amp;nbsp;칠해도&amp;nbsp;되지만,&amp;nbsp;페인트칠&amp;nbsp;횟수를&amp;nbsp;최소화하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;1&amp;lt;=m&amp;nbsp;&amp;lt;=&amp;nbsp;n&amp;nbsp;&amp;lt;= 100,000&lt;br /&gt;&amp;nbsp;1&amp;lt;=&amp;nbsp;section(페인트&amp;nbsp;칠해야&amp;nbsp;할&amp;nbsp;구역의&amp;nbsp;번호가&amp;nbsp;담긴&amp;nbsp;정수배열)&amp;nbsp;&amp;lt;=&amp;nbsp;n&lt;br /&gt;&amp;nbsp;1&amp;lt;=&amp;nbsp;section&amp;nbsp;원소&amp;nbsp;&amp;lt;=&amp;nbsp;n&lt;br /&gt;section의&amp;nbsp;원소는&amp;nbsp;중복 x,&amp;nbsp;오름차순&amp;nbsp;정렬이 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;풀이방향&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게&amp;nbsp;칠하는게&amp;nbsp;효과적일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 머리를 굴려본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재&amp;nbsp;롤러&amp;nbsp;m크기를&amp;nbsp;기준으로,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;section을 제일 왼쪽위치 혹은 오른쪽 위치에서 반대방향으로 이동하면서&lt;br /&gt;현재&amp;nbsp;위치부터&amp;nbsp;롤러m&amp;nbsp;크기에&amp;nbsp;있는&amp;nbsp;모든&amp;nbsp;가능한&amp;nbsp;구역을 칠해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 롤러크기가&amp;nbsp;1이면,&amp;nbsp;현재위치만&amp;nbsp;칠할&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;- 롤러크기가&amp;nbsp;3이면,&amp;nbsp;현재위치 +&amp;nbsp;최대&amp;nbsp;2거리만큼&amp;nbsp;칠해줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 고민할 게 없다. &lt;b&gt;그리디 문제&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 왼쪽/오른쪽에서 시작해서 더이상 칠할 section이 없으면 최소로 칠한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;while문을 통해 section에 원소가 없어질 때 까지 순회한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색의 첫 시작이 왼쪽인지 오른쪽인지는 중요하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;while을 활용하기로 했으니, Array.prototype.pop 메서드로 오른쪽을 칠하는 기준으로 삼아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- for문을 활용했다면, 왼쪽으로 기준을 삼아도 되고, 오른쪽을 삼아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사실 while문을 활용해도, 내림차순 sort하고 pop을 하여 왼쪽을 기준삼아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그러나 while문을 활용하면서, 오름차순 sort 상태에서 shift 메서드로 할 생각은 접어두길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; (왜인지 잘 모르겠다면 shift 메서드의 시간복잡도를 생각해보길 바란다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주석 포함 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1678525455027&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// while문을 활용할거라 변수 n은 쓸데가 없다.

function solution(n, m, section) {
   var answer = 0;
   // 거리(m)과 while문 내의 부등호는 연관이 깊다.
   // 나는 m=1일 때 기준 값만 칠해지므로 1을 빼준 값을 rollerLen으로 정했다.
   const rollerLen = m - 1;
   let base = section[section.length - 1];
   let cur;

   answer += 1;

   while (section.length) {
     cur = section.pop();
     
	 // base-cur &amp;lt;= rollerLen이면 아직 더 칠할 수 있다.
     // 만약 여기서 continue에 부합되지 않았다면, 현재 cur부터 base를 잡아 칠해야하므로
     // base를 cur로 바꿔주고 answer를 증가시켜준다.
     // 이런 문제들은, 항상 진입점과 탈출점(section 원소가 1개 남았을 때)에서
     // answer를 1 증가시키거나 증가시키지 않는 경우들을 잘 생각해서 코드를 짜야한다.
     if (base - cur &amp;lt;= rollerLen) continue;
     base = cur;
     answer += 1;
   }

   return answer;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/programmers</category>
      <category>덧칠하기 javascript</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/159</guid>
      <comments>https://gobae.tistory.com/159#entry159comment</comments>
      <pubDate>Sat, 11 Mar 2023 18:05:11 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 대충 만든 자판 - 자바스크립트</title>
      <link>https://gobae.tistory.com/158</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8fn3F/btr3effXyUf/QKf4S3ppfIc5ncgkhBqBlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8fn3F/btr3effXyUf/QKf4S3ppfIc5ncgkhBqBlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8fn3F/btr3effXyUf/QKf4S3ppfIc5ncgkhBqBlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8fn3F%2Fbtr3effXyUf%2FQKf4S3ppfIc5ncgkhBqBlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;788&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1678522939327&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/160586?language=javascript&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kUIoR/hyRTJ5C0oZ/bap4n2PwWFdvgBJuxPwUG1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/diTpyM/hyRTTApakM/A10BWz8SQeIcqBEECdYWC0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/160586?language=javascript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/160586?language=javascript&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kUIoR/hyRTJ5C0oZ/bap4n2PwWFdvgBJuxPwUG1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/diTpyM/hyRTTApakM/A10BWz8SQeIcqBEECdYWC0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 프로그래머스에 들어갔는데, 새로운 문제들이 추가되었더라.&lt;br /&gt;레벨1의 난이도 치고는 생각할 부분이 조금 있어 보여서, 포스팅하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function solution(keymap, targets) {
   var answer = [];
   const keyPos = {};

   keymap.forEach((km) =&amp;gt; {
     [...km].forEach((alphabet, pos) =&amp;gt; {
       if (!(alphabet in keyPos)) {
         keyPos[alphabet] = pos + 1;
       } else {
         keyPos[alphabet] = Math.min(keyPos[alphabet], pos + 1);
       }
     });
   });

   targets.forEach((target) =&amp;gt; {
     let click = 0;

     [...target].forEach((alphabet) =&amp;gt; {
       if (click === -1) return;

       if (!(alphabet in keyPos)) {
         click = -1;
       } else {
         click += keyPos[alphabet];
       }
     });

     answer.push(click);
   });

   return answer;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제조건&lt;/b&gt;&lt;br /&gt;휴대폰 자판은 하나의 자판에 여러 문자가 할당된다.&lt;br /&gt;키 하나에 여러 문자가 있으면, 빠르게 누르면서 문자를 바꾼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴대폰 자판마다 할당된 키는 1~100개까지 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 키를 눌렀을 때 입력되는 문자도 무작위다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 문자가 자판 전체에 여러번 할당될 수도 있고,&lt;br /&gt;키 하나에 같은 문자가 여러 번 할당될 수도 있다.&lt;br /&gt;아예 할당되지 않았을수도 있다.(= 몇몇 문자열은 작성 불가능하다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴대폰 자판을 이용해 특정 문자열을 작성할 때,&lt;br /&gt;키를 최소 몇번 눌러야 그 문자열을 작성할 수 있는지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;&lt;br /&gt;keymap : 1번키부터 차례대로 할당된 문자들이 순서대로 담긴 문자열 배열.&lt;br /&gt;targets : 입력하려는 문자열들이 담긴 문자열배열&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 문자열 작성을 위해 &quot;키를 최소한 몇번 눌러야하는지&quot; 순서대로 배열에 담아 리턴.&lt;br /&gt;목표달성이 불가능하면 -1 저장.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1&amp;lt;=keymap.length&amp;lt;=100&lt;br /&gt;1&amp;lt;=keymap 원소길이&amp;lt;=100&lt;br /&gt;keymap[i]는 i+1번 키를 눌렀을 때 바뀌는 문자이다.&lt;br /&gt;keymap 원소 길이는 서로 다르고, 알파벳 대문자로만 이루어짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1&amp;lt;=targets.length&amp;lt;=100&lt;br /&gt;1&amp;lt;=targets 원소 길이&amp;lt;=100&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;풀이방향&lt;/b&gt;&lt;br /&gt;자판 클릭으로 targets에 있는 문자열을 완성시켜야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러기 위해선 targets 문자열들의 각 문자들이 keymap 어디에 위치하는지 알아야 하는데,&lt;br /&gt;keymap, targets 배열의 크기나 원소길이가 최대 100인것을 감안하면, 무지성으로 풀어도 풀릴 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 우리는 keymap에 담긴 버튼 정보를 가지고 문자열들의 최소 클릭횟수를 알아두고 한번에 풀어보자.&lt;br /&gt;이것을 keyPosition이라는 객체에 저장하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;targets 문자열마다 문자열의 각 문자들의 클릭 횟수를 keyPosition 객체를 통해 알아낼 수 있다.&lt;br /&gt;문자열을 완성하기 위한 각 문자들의 클릭 횟수를 더한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keyPosition 객체의 값들은 최소 클릭수이므로 위에서 구한 값이 해당 문자열의 클릭 최소값이고,&lt;br /&gt;keyPosition에 없는 문자를 포함한다면 -1을 리턴해주면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;if문을 줄여본 코드&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function solution(keymap, targets) {
   var answer = [];
   const keyPos = {};

   keymap.forEach((km) =&amp;gt; {
     [...km].forEach((alphabet, pos) =&amp;gt; {
       const curPos = keyPos[alphabet] ?? pos + 1;
       keyPos[alphabet] = Math.min(curPos, pos + 1);
     });
   });

   targets.forEach((target) =&amp;gt; {
     let click = 0;

     for (let alphabet of target) {
       const curPos = keyPos[alphabet] ?? -1;
       if (curPos === -1) {
         click = -1;
         break;
       }
       click += curPos;
     }

     answer.push(click);
   });

   return answer;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 if/else을 &amp;nbsp;forEach를 break로 탈출할 수 없어 문자가 존재하지 않아도 배열을 끝까지 돌아야 하는 문제를 개선했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;그외... 주절주절&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;??&lt;/b&gt; 라는 문법은 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;nullish coalescing operator&lt;/a&gt;&amp;nbsp;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부정값(ex. 0이라던가)일 때 사용하는 것 처럼 보이지만, undefined/null 값일 때만 후자의 값으로 할당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유용한 연산자이니 mdn을 잘 읽어보고 체득하면 좋다.(현재 코드에서는 key값이 없으면 undefined를 반환하므로 사용했다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;forEach문은 배열을 끝까지 탐색해야만 한다. 그래서 break문을 통해 forEach문을 탈출할 수가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 최상단의 코드(이전 코드)에서 &lt;span style=&quot;background-color: #99cefa;&quot;&gt;if(click === -1) return;&lt;/span&gt; 이라는 조건을 달아줬었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 for문에서는 break가 있는데, 비슷하게 동작하는 forEach에서는 break가 통하지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for문과 forEach는 비슷해보이지만, 목적이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파고 들어가면 꽤 복잡하지만, 맛보기 느낌으로 생각만 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for문은 절차지향적으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 생각하는 흐름의 알고리즘을 for문 내부에 구현해둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정한 조건에 걸리면, break문으로 즉시 탈출시키는 등의 &quot;절차&quot;를 서술하는 것에 집중할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;forEach문은 함수지향적으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형으로 동작한다는 것은, 절차보다는 &quot;입력&quot;과 &quot;결과&quot;에 집중한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 내부에서는 어떻게 돌아가야 할지에 대한 &quot;절차&quot;가 기술되어 있지만 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 forEach문은 &quot;콜백함수&quot;를 인자로 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열의 모든 원소에 대해 이 함수를 실행한다. 가 목표다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 프로그래밍에서는 몇가지 원칙에 의해 동일한 입력에 대해서 동일한 출력을 반환해야 하므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;break문 등을 통해 변칙적인 상황등을 만드는 상황 자체가 함수형에 적절하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 말하다가 끊은 느낌인데,,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 궁금증이 생겼다면 절차지향/객체지향/함수지향 등의 키워드로 각 방법들에 대해 관심을 가져보는 것도 좋겠다.&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/programmers</category>
      <category>대충만든자판 javascript</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/158</guid>
      <comments>https://gobae.tistory.com/158#entry158comment</comments>
      <pubDate>Sat, 11 Mar 2023 17:43:26 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] sort함수는 왜 숫자를 이상하게 정렬할까?</title>
      <link>https://gobae.tistory.com/157</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이상한 자바스크립트의 sort&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;sort-thumbnail.webp&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSBTmO/btrW56pMaTa/EaRl9Ce1Fa6MCWC5UrYL81/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSBTmO/btrW56pMaTa/EaRl9Ce1Fa6MCWC5UrYL81/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSBTmO/btrW56pMaTa/EaRl9Ce1Fa6MCWC5UrYL81/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSBTmO%2FbtrW56pMaTa%2FEaRl9Ce1Fa6MCWC5UrYL81%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;392&quot; data-filename=&quot;sort-thumbnail.webp&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 개발자라면, 당연히 알고있는 이상함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &quot;자바스크립트 숫자 정렬&quot;과 같은 키워드로 인터넷을 검색해보면,&lt;/p&gt;
&lt;pre id=&quot;code_1674635057454&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 오름차순 정렬을 하고 싶으세요?
arr.sort((a,b)=&amp;gt;a-b);

// 내림차순 정렬을 하고 싶으세요?
arr.sort((a,b)=&amp;gt;b-a);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 딱 답이 나와있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;우리는 평소처럼 숫자 정렬에서는 a-b는 오름차순, b-a는 내림차순이구나! 하고 써먹고 끝낸다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것만 알아도 사실 전혀 문제는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 한번쯤은 이유도 궁금해해보자. 꽤나 배울 것이 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는, 왜 저렇게 이상한 함수를 넣어줘야 하는지를 함께 알아보도록 하자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Sort의 근원지 MDN를 탐색하자.&lt;/h3&gt;
&lt;figure id=&quot;og_1674635209169&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Array.prototype.sort() - JavaScript | MDN&quot; data-og-description=&quot;sort() 메서드는 배열의 요소를 적절한 위치에 정렬한 후 그 배열을 반환합니다. 정렬은 stable sort가 아닐 수 있습니다. 기본 정렬 순서는 문자열의 유니코드 코드 포인트를 따릅니다.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/caHc8D/hyRoyp2NNM/x9jBMtTfPtGbyrqkktODR1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/caHc8D/hyRoyp2NNM/x9jBMtTfPtGbyrqkktODR1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Array.prototype.sort() - JavaScript | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;sort() 메서드는 배열의 요소를 적절한 위치에 정렬한 후 그 배열을 반환합니다. 정렬은 stable sort가 아닐 수 있습니다. 기본 정렬 순서는 문자열의 유니코드 코드 포인트를 따릅니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 개발자의 필수덕목 MDN이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;1234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfFjd0/btrW4Ef6GPw/gvgJwkIYQp4g6pPuizaR7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfFjd0/btrW4Ef6GPw/gvgJwkIYQp4g6pPuizaR7K/img.png&quot; data-alt=&quot;출처: Mdn&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfFjd0/btrW4Ef6GPw/gvgJwkIYQp4g6pPuizaR7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfFjd0%2FbtrW4Ef6GPw%2FgvgJwkIYQp4g6pPuizaR7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;662&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;1234&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: Mdn&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 보이는 글이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친절하게도 [1,30,4,21,100000]을 정렬하면, 여러분 마음대로 되지 않을겁니다. 라는 예시도 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 가장 먼저 보이는 글에 이유가 담겨있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;&quot;기본 정렬 순서는 문자열의 유니코드 포인트를 따릅니다.&quot;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 배열 정렬시 원소들을 &quot;문자열&quot; 취급하여 &quot;유니코드&quot;를 따른다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/유니코드_0000~0FFF&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;유니코드&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1658&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Klca/btrW9oXO0e4/7gn8i2042TcIawZypdMALK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Klca/btrW9oXO0e4/7gn8i2042TcIawZypdMALK/img.png&quot; data-alt=&quot;출처 : 위키피디아&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Klca/btrW9oXO0e4/7gn8i2042TcIawZypdMALK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Klca%2FbtrW9oXO0e4%2F7gn8i2042TcIawZypdMALK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;528&quot; data-origin-width=&quot;1658&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : 위키피디아&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자를 정렬하고 있으므로, 유니코드 표에서 숫자를 찾아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 = &quot;U+0031&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2 = &quot;U+0032&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9 = &quot;U+0039&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니코드에 따르면, 1이 2보다 먼저 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 정렬시 1-&amp;gt;2-&amp;gt;3... 순으로 오름차순 정렬되는 것이 당연하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 [1, 2, 10]을 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 = &quot;U+0031&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2 = &quot;U+0032&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10 = &quot;U+0031U+0030&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자를 유니코드로 변환하면, 10의 문자열이 2보다 앞선다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;u&gt;&lt;b&gt;&quot;기본 정렬 순서는 문자열의 유니코드 포인트를 따릅니다.&quot;&lt;/b&gt;&lt;/u&gt; 에 따르면 10이 먼저오게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MDN에서 다음 문구를 읽어도 동일한 내용을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;936&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JLJwg/btrW2uY82mD/AVJqZ9ZkjrNBoAFFpyFZok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JLJwg/btrW2uY82mD/AVJqZ9ZkjrNBoAFFpyFZok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JLJwg/btrW2uY82mD/AVJqZ9ZkjrNBoAFFpyFZok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJLJwg%2FbtrW2uY82mD%2FAVJqZ9ZkjrNBoAFFpyFZok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;936&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;936&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sort의 매개변수로 넣어주는 compareFunction은 Optional이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생략 시 &quot;각 문자의 유니코드 값&quot;에 따라 정렬된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심지어 설명에 더 친절하게 적혀있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;바나나&quot;가 &quot;체리&quot;앞에 오고, 80이 9보다 먼저오는 예시를 들어줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심지어는 &lt;u&gt;&lt;b&gt;&quot;숫자는 문자열로 변환되기 때문에!&quot; &lt;/b&gt;&lt;/u&gt;라고 이유를 한번 더 알려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 MDN을 정독하면 메서드 및 api들의 사용법 뿐 아니라, 의문도 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;sort((a,b)=&amp;gt;a-b)는 왜&lt;span&gt;&amp;nbsp;&lt;/span&gt;숫자 오름차순 정렬일까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;&lt;b&gt;(a,b)=&amp;gt;a-b&lt;/b&gt;&lt;/span&gt;가 바로 compareFunction이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 더 설명할 것도 없이 MDN을 더 읽어보면 답이 나와있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l0rVj/btrW7UQhMQB/Jpo8umDskWwVnqngkm8SWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l0rVj/btrW7UQhMQB/Jpo8umDskWwVnqngkm8SWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l0rVj/btrW7UQhMQB/Jpo8umDskWwVnqngkm8SWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl0rVj%2FbtrW7UQhMQB%2FJpo8umDskWwVnqngkm8SWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;694&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;compareFunction를 통해 sort에 어떤식으로 원소들을 정렬할 것인지의 기준을 알려주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴값 &amp;lt; 0 이라면,&amp;nbsp;a가 b보다 낮은 색인(앞선 인덱스)로 정렬된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴값 = 0 이라면, 정렬을 수행하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴값 &amp;gt; 0 이라면, a가 b보다 큰 색인(뒤 인덱스)로 정렬된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴값 = a-b이므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴값이 음수라면, a&amp;lt;b이다. 그러므로 a가 b보다 앞서야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴값이 0이라면, a=b이다. 그러므로 정렬이 수행되지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴값이 양수라면, a&amp;gt;b이다. 그러므로 b가 a보다 앞서야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;(a,b)=&amp;gt;a-b&lt;/span&gt;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;라는 compareFunction이 숫자 오름차순의 기능을 수행하게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 (a,b)=&amp;gt;b-a를 대입해보면, 왜 내림차순 정렬되는지 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자주 등장하는 정렬 사례 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 자주 등장하는 사례 1가지만 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674638063994&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const list = [
  { name: 'Tom', amount: 5000000 },
  { name: 'Paul', amount: 320000 },
  { name: 'Mark', amount: 80000 },
  { name: 'Jane', amount: 5000000 },
  { name: 'Nela', amount: 5100000 },
];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;은행의 입장에서, VIP 고객의 명단을 관리한다고 가정하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예금액을 기준으로 내림차순, 예금액이 같다면 이름순으로 오름차순 정렬하라는 지시가 내려졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 떠오르고 해결까지 했다면 글을 닫아도 된다. ㅎㅎ.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 조금만 어려워져도 막막한 상황이 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 아래 스텝을 차근차근 따라와보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674638271077&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function compareFunction(a, b) {
  if (a.amount === b.amount) {
    return a.name - b.name;
  }
  return b.amount - a.amount;
}

// sort에 인자로 넣어줄 compareFunction를 따로 분리했다.

// 이 코드는 금액이 같을 때 name을 오름차순으로 먼저 분기시키고, 
// 나머지 경우에 대해서는 내림차순 정렬을 수행한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;해치웠나?&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MA8ui/btrW4F7fyDj/htXToNrZd4pZGuycZKGhj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MA8ui/btrW4F7fyDj/htXToNrZd4pZGuycZKGhj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MA8ui/btrW4F7fyDj/htXToNrZd4pZGuycZKGhj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMA8ui%2FbtrW4F7fyDj%2FhtXToNrZd4pZGuycZKGhj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;724&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 정렬된 것 같아 보였으나, Tom과 Jane이 &quot;오름차순&quot;에 위배된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;a-b가 오름차순 정렬이 맞는데, 왜 안될까?&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 이상하면 부딪혀봐야지. &lt;b&gt;직접 빼보자.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;84&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs4Gb6/btrW6qaRW2a/pHfK4muPBnbb0J0keAdlKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs4Gb6/btrW6qaRW2a/pHfK4muPBnbb0J0keAdlKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs4Gb6/btrW6qaRW2a/pHfK4muPBnbb0J0keAdlKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs4Gb6%2FbtrW6qaRW2a%2FpHfK4muPBnbb0J0keAdlKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;63&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NaN&lt;/b&gt;이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연하다. 문자끼리 빼는데, 연산을 수행할 수 없으니 NaN(Not a Number)이 나와야지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지만 지시는 수행해야 할텐데 어떻게 해결하면 좋을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대소를 비교하는 방법 중, 빼는 방법은 숫자를 비교하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자를 비교할 때는 부등호(&amp;gt;, &amp;lt; 등)을 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674638803607&quot; class=&quot;ada&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function compareFunction(a, b) {
  if (a.amount === b.amount) {
        return a.name &amp;gt; b.name ? 1 : (a.name &amp;lt; b.name ? -1 : 0);
  }
  return b.amount - a.amount;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSDhTT/btrXcdVbzPZ/xT6OJOvIPxZqkJyIixOB90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSDhTT/btrXcdVbzPZ/xT6OJOvIPxZqkJyIixOB90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSDhTT/btrXcdVbzPZ/xT6OJOvIPxZqkJyIixOB90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSDhTT%2FbtrXcdVbzPZ%2FxT6OJOvIPxZqkJyIixOB90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;152&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔에서의 결과를 바탕으로 왜 각각 1, -1, 0을 리턴해줬을까? 정도는 그림을 그려보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 비교함수는&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dNFdEE/btrXcoWEpMU/VGTgAaPzBAhTtgut22zKek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dNFdEE/btrXcoWEpMU/VGTgAaPzBAhTtgut22zKek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dNFdEE/btrXcoWEpMU/VGTgAaPzBAhTtgut22zKek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdNFdEE%2FbtrXcoWEpMU%2FVGTgAaPzBAhTtgut22zKek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;686&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;686&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;드디어 원하는 결과를 반환해준다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;짧은 후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &quot;어떤 방법&quot;을 아는 것은 좋지만, &quot;이유&quot;를 아는 것이 무척 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오름차순, 내림차순 &quot;방법&quot;을 알지만, 한가지 조건이 추가되면 손을 못쓰는 상황이 발생할수도 있으니 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;(실제로 이렇게 &quot;같을 때는 오름차순으로 정렬해주세요!&quot; 문제가 코딩테스트에도 종종 등장한다.)&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅을 접하는 동료분들은 꼭 평소 궁금증이 해결되었길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 해결이 되지 않을 때나 이유를 모른다면, 한번 쯤 그 이유를 알아보려 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 나에게 어려운 문제는 다른 동료들도 똑같이 겪는 문제이며, 사실 &quot;공식문서&quot;에 정리되어 있는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이렇게 적어둔 필자도 FM대로 공식문서만을 참고하는 것은 아니고 &lt;span&gt;이곳 저곳의 레퍼런스를 참고한다&lt;/span&gt;ㅎㅎ,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, 결국 &quot;공식&quot;문서가 왜 공식이겠는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 개념의 가장 믿을만한 지식이며, 많은 고민들을 제안하고 &lt;span&gt;나누며&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;해결하고자 하는 곳이 공식문서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어렵거나 답이 나오지 않는 문제를 겪으면, 공식문서에 가서 차근차근 살펴보는 것을 추천한다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 sort는 왜 그모양일까</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/157</guid>
      <comments>https://gobae.tistory.com/157#entry157comment</comments>
      <pubDate>Wed, 25 Jan 2023 17:59:05 +0900</pubDate>
    </item>
    <item>
      <title>[Debug] IDE로 리액트 프로젝트를 디버깅해보자.</title>
      <link>https://gobae.tistory.com/156</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;디버깅...?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E3voJ/btrTseSt8fO/1PD85bD2Mu9Zc5UC0SL7A0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E3voJ/btrTseSt8fO/1PD85bD2Mu9Zc5UC0SL7A0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E3voJ/btrTseSt8fO/1PD85bD2Mu9Zc5UC0SL7A0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE3voJ%2FbtrTseSt8fO%2F1PD85bD2Mu9Zc5UC0SL7A0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;292&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 프로젝트에서 videos의 자료형을 보고 싶다면 어떻게 할 것인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 생각나는 방법은 &lt;b&gt;console.log&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ppdbo/btrTvkX8LjQ/FWTgBVjuHUc27kHWERe9Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ppdbo/btrTvkX8LjQ/FWTgBVjuHUc27kHWERe9Dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ppdbo/btrTvkX8LjQ/FWTgBVjuHUc27kHWERe9Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPpdbo%2FbtrTvkX8LjQ%2FFWTgBVjuHUc27kHWERe9Dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;788&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리들의 강력한 디버깅 친구는 &lt;span&gt;console.log다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console.log와 구글크롬으로 사실 충분할지도 모른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머리 아픈 디버깅 방법을 알 필요도 없고 바로 크롬 개발자도구에서 결과를 볼 수 있으니까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, console.log는 딱 개발자가 지정한 변수만을 출력해준다는 단점이 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 인과관계를 알기도 힘들고, 특히 this binding과 같이 조금만 어려워져도 추적이 힘들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수가 3-4개만 연관되어 있어도, console.log(a,b,c,d); 와 같이 모두 담아줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때로는 특정 시점 이전과 이후로 console.log를 추가하여 관찰해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;심지어는 종종 console.log를 지우는 것을 까먹어서, 커밋에 포함되어 원격저장소에 푸시되기도 한다.&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 ide에서 제공하는 디버깅 방법을 배워두면 쓸만하지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버깅 덕에 &amp;ldquo;특정 지점&amp;rdquo;에서의 데이터 관찰 뿐 아니라 문제 해결의 논리적 추리력도 키울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디버깅 절차&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 vscode IDE를 사용하므로 vsode 기준으로 설명하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절차는 무척 간단하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관찰하려는 시점에서 중단점(break point)를 걸어준다.&lt;/li&gt;
&lt;li&gt;디버그 명령을 실행한다.&lt;/li&gt;
&lt;li&gt;IDE가 자동으로 중단점에서 프로그램 실행을 중단해주며, 그 때 보여주는 데이터들을 관찰하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 디버깅의 처음이자 끝이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보려는 실행시점을 체크하고, 실행하고, 보면 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리액트 프로젝트에서 디버깅하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 우리의 리액트 앱을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRA로 만든 프로젝트라면 &lt;span style=&quot;background-color: #000000; color: #eb5757;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;npm start,&lt;/span&gt; 직접 설정한 프로젝트라면 개발모드를 켜준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dheBn9/btrTthnTcQ5/z1D4F0aREEytqAOAMWQsy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dheBn9/btrTthnTcQ5/z1D4F0aREEytqAOAMWQsy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dheBn9/btrTthnTcQ5/z1D4F0aREEytqAOAMWQsy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdheBn9%2FbtrTthnTcQ5%2Fz1D4F0aREEytqAOAMWQsy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;394&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 우리 리액트 프로젝트의 포트(3000번)을 기억해둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eCPjmX/btrTtF2366T/ozD9qGkCn5L7xYtjmc0px0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eCPjmX/btrTtF2366T/ozD9qGkCn5L7xYtjmc0px0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eCPjmX/btrTtF2366T/ozD9qGkCn5L7xYtjmc0px0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeCPjmX%2FbtrTtF2366T%2FozD9qGkCn5L7xYtjmc0px0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;774&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고는 디버그 탭을 열어주는데,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좌측 메뉴 4번째 탭 실행 및 디버그&lt;/li&gt;
&lt;li&gt;shift + option + D&lt;/li&gt;
&lt;li&gt;fn + F5&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셋 중에 아무거나 실행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;1190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z0cwK/btrTvXVZHYY/qfNv6sjmtM6oVkH0uxSI3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z0cwK/btrTvXVZHYY/qfNv6sjmtM6oVkH0uxSI3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z0cwK/btrTvXVZHYY/qfNv6sjmtM6oVkH0uxSI3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz0cwK%2FbtrTvXVZHYY%2FqfNv6sjmtM6oVkH0uxSI3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;1190&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;1190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트를 디버깅해야 하므로, 디버거로 chrome을 지정해주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 및 디버그 &amp;gt; chrome을 검색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 루트에 .vscode 디렉토리가 생성되고, 내부에 launch.json이 추가된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일에는 디버그 설정들이 담기게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPKaol/btrTtmvTVX1/W4YmluKcWYoZkPgtNfYzB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPKaol/btrTtmvTVX1/W4YmluKcWYoZkPgtNfYzB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPKaol/btrTtmvTVX1/W4YmluKcWYoZkPgtNfYzB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPKaol%2FbtrTtmvTVX1%2FW4YmluKcWYoZkPgtNfYzB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;191&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;url에는 우리 개발서버의 포트(필자는 3000번)로 수정하여 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7EIuG/btrTt2jkwoO/DKEcGU5AUp12dtWykEVRAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7EIuG/btrTt2jkwoO/DKEcGU5AUp12dtWykEVRAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7EIuG/btrTt2jkwoO/DKEcGU5AUp12dtWykEVRAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7EIuG%2FbtrTt2jkwoO%2FDKEcGU5AUp12dtWykEVRAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;532&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 Launch Chrome against localhost라는 실행 명령이 생겼을 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 시점에 중단점을 걸어 디버그 실행을 해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX3lfH/btrTtkrlaYV/ykUSRALjJqlRgJKQUzeaL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX3lfH/btrTtkrlaYV/ykUSRALjJqlRgJKQUzeaL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX3lfH/btrTtkrlaYV/ykUSRALjJqlRgJKQUzeaL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX3lfH%2FbtrTtkrlaYV%2FykUSRALjJqlRgJKQUzeaL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;302&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 관찰하려는 시점에 중단점(break point)를 걸어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측 빨간 점이 중단점에 해당하며, 여러 곳을 관찰하려면 여러 곳을 지정하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FpzUd/btrTuk5eNqn/VgrnHnqmZnexKjlzOUC5Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FpzUd/btrTuk5eNqn/VgrnHnqmZnexKjlzOUC5Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FpzUd/btrTuk5eNqn/VgrnHnqmZnexKjlzOUC5Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFpzUd%2FbtrTuk5eNqn%2FVgrnHnqmZnexKjlzOUC5Tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;430&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버그 실행 탭에서, 실행 및 디버그의 우측 화살표를 누르면 디버그가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 실행되면서, 개발자가 걸어둔 중단점에서의 데이터들을 보여주게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;(이 때 npm start로 개발서버를 먼저 띄워둔 상태에서 디버그를 실행해준다. 구동중인 개발서버 기반으로 디버그가 실행된다.)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k4P9A/btrTvWW5L0v/BACuMQvKscgD1QxvqYlEk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k4P9A/btrTvWW5L0v/BACuMQvKscgD1QxvqYlEk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k4P9A/btrTvWW5L0v/BACuMQvKscgD1QxvqYlEk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk4P9A%2FbtrTvWW5L0v%2FBACuMQvKscgD1QxvqYlEk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;412&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중단점에서의 데이터를 관찰하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;videos의 자료구조인 배열에 담긴 모든 데이터도 볼 수 있고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this에 어떤 객체가 바인딩되어있는지,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이론으로만 배워왔던 클로저, 전역변수에는 어떤 변수들이 있는지도 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 문제 해결을 위해 개발자가 디버깅을 하면서, 문제의 인과관계를 파악하기 더 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(= 결과적으로 개발자 의도에 따라 소프트웨어가 완벽하게 동작할 확률이 높아진다는 것이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비동기함수도 관찰 가능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 특이한 점은, 중단점이 걸린 위치가 useEffect + 내부의 즉시실행함수도 async 함수 내부라는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 비동기적으로 실행되는 코드도 감지할 수 있다고 보여진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 쉬운 예시를 보면,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn5TwR/btrTu15qYqZ/uneKSrkWKTNFsyRwOUzshk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn5TwR/btrTu15qYqZ/uneKSrkWKTNFsyRwOUzshk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn5TwR/btrTu15qYqZ/uneKSrkWKTNFsyRwOUzshk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn5TwR%2FbtrTu15qYqZ%2FuneKSrkWKTNFsyRwOUzshk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;518&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 이벤트에 의해 이벤트리스너가 실행되는 시점에 중단점을 걸어둘 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event 객체의 내용을 보거나, 이벤트리스너가 의도치 않은 동작을 하는 경우 분석할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;1260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NJxs7/btrTt1kr8wV/Q14H2XDDkxR3Mx31z9lqjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NJxs7/btrTt1kr8wV/Q14H2XDDkxR3Mx31z9lqjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NJxs7/btrTt1kr8wV/Q14H2XDDkxR3Mx31z9lqjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNJxs7%2FbtrTt1kr8wV%2FQ14H2XDDkxR3Mx31z9lqjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;1260&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;1260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 이벤트리스너가 의도대로 동작하지 않는 대표적인 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 클릭하면 console.log(&quot;Tom&quot;)을 출력하도록 설계되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EZgmQ/btrTt2cz14e/ujlEv4o5PCKOdlap4taDCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EZgmQ/btrTt2cz14e/ujlEv4o5PCKOdlap4taDCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EZgmQ/btrTt2cz14e/ujlEv4o5PCKOdlap4taDCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEZgmQ%2FbtrTt2cz14e%2FujlEv4o5PCKOdlap4taDCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;240&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 없어 보였으나, Cannot read properties of undefined (reading 'name') 라는 에러를 뿜는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this가 undefined라서 undefined의 참조할 수 없다. 가 오류의 원인이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J6han/btrTqDSrKQR/yeP85LzZFhJoLdrQ6agTkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J6han/btrTqDSrKQR/yeP85LzZFhJoLdrQ6agTkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J6han/btrTqDSrKQR/yeP85LzZFhJoLdrQ6agTkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ6han%2FbtrTqDSrKQR%2FyeP85LzZFhJoLdrQ6agTkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;440&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일하게 이벤트리스너 sayName에 중단점을 걸어주면, this에 어떤 객체도 바인딩되어있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이제 자신이 아는 방법대로 this를 바인딩해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ8FkZ/btrTuzH0taR/kGgyLNONzNluucNAl31ulk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ8FkZ/btrTuzH0taR/kGgyLNONzNluucNAl31ulk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ8FkZ/btrTuzH0taR/kGgyLNONzNluucNAl31ulk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ8FkZ%2FbtrTuzH0taR%2FkGgyLNONzNluucNAl31ulk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;650&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 상황에서, 우선 생각나는 방법으로는 화살표함수를 활용해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화살표함수로 바꿔서 이벤트를 발생시키니, this에 Customer의 인스턴스가 바인딩 된 것이 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 의도한대로 say name 버튼 클릭시 console.log(&quot;Tom&quot;)이 출력된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;짧게 소개를 마치며..&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버깅을 배우면서 글의 내용 외에도&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디버깅 시 나타나는 버튼들은 어떤 역할들을 하는지&lt;/li&gt;
&lt;li&gt;closure가 무엇인지&lt;/li&gt;
&lt;li&gt;this가 왜 사라지는지&lt;/li&gt;
&lt;li&gt;event 객체는 무엇인지&lt;/li&gt;
&lt;li&gt;클래스의 인스턴스가 무엇인지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등등 궁금한 것이 많이 생겨났을 수도 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 궁금한 것을 학습하거나, 프로젝트에서 디버깅을 잘하는 개발자인 척 하러 가보자~&lt;/p&gt;</description>
      <category>Error</category>
      <category>리액트 프로젝트 디버깅하기</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/156</guid>
      <comments>https://gobae.tistory.com/156#entry156comment</comments>
      <pubDate>Mon, 12 Dec 2022 23:34:40 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 억억단을 외우자 - 자바스크립트</title>
      <link>https://gobae.tistory.com/154</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1678522107643&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/138475&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dP6EoC/hyRTVdVyaU/L73Xo77TWMRHCi6WxsJCh1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bq7a0e/hyRTV53joQ/IeS5K8yFjB3KLKOKqiSxz1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/138475&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/138475&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dP6EoC/hyRTVdVyaU/L73Xo77TWMRHCi6WxsJCh1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bq7a0e/hyRTV53joQ/IeS5K8yFjB3KLKOKqiSxz1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 문제 조건을 재해석해야하고, 시간 초과도 신경써야 하는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 시간 초과를 잘 생각하면서 설계해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e의 범위가 500만인데, 각 케이스마다 모두 조회를 해야한다면.. 기계가 힘들어한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1669366163266&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(e, starts) {
  const denominators = getAllDenominators(e);
  const lowestMaxNumArr = getLowestMaxNumArr(denominators, e);

  return starts.map((s) =&amp;gt; lowestMaxNumArr[s]);
}

function getAllDenominators(size) {
  const result = new Array(size + 1).fill(2);
  result[0] = 0;
  result[1] -= 1;

  for (let i = 2; i &amp;lt;= size; i++) {
    for (let j = 2, end = Math.floor(size / i); j &amp;lt;= end; j++) {
      result[i * j]++;
    }
  }

  return result;
}

function getLowestMaxNumArr(arr, size) {
  const lowestMaxNumArr = new Array(size + 1).fill(0);
  lowestMaxNumArr[size] = size;

  for (let i = size - 1; i &amp;gt; 0; i--) {
    const cur = arr[i];
    const prev = arr[lowestMaxNumArr[i + 1]];

    lowestMaxNumArr[i] = cur &amp;gt;= prev ? i : lowestMaxNumArr[i + 1];
  }

  return lowestMaxNumArr;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_포스팅사진.webp&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;413&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOUCSZ/btrR9jGwmdJ/nNiOjCQAL0ZepK84eEDMfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOUCSZ/btrR9jGwmdJ/nNiOjCQAL0ZepK84eEDMfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOUCSZ/btrR9jGwmdJ/nNiOjCQAL0ZepK84eEDMfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOUCSZ%2FbtrR9jGwmdJ%2FnNiOjCQAL0ZepK84eEDMfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;413&quot; data-filename=&quot;edited_포스팅사진.webp&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;413&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러번의 시행착오 끝에 코드가 짧아져 다행이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 조건&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;천하제일&amp;nbsp;암산대회. &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;억억단은&amp;nbsp;1억*1억의&amp;nbsp;행렬이다. &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적당한수&amp;nbsp;e를&amp;nbsp;먼저&amp;nbsp;알려주고,&amp;nbsp;e이하의&amp;nbsp;수&amp;nbsp;s를&amp;nbsp;여러번&amp;nbsp;말해준다. &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 s마다의 정답 x를&amp;nbsp;구해야&amp;nbsp;하는데&amp;nbsp;각&amp;nbsp;s에&amp;nbsp;대해&amp;nbsp;s&amp;lt;=x&amp;lt;=e인&amp;nbsp;수&amp;nbsp;중,&amp;nbsp;억억단에&amp;nbsp;가장&amp;nbsp;많이&amp;nbsp;등장한&amp;nbsp;수가 x이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장&amp;nbsp;많이&amp;nbsp;등장한&amp;nbsp;수가&amp;nbsp;여러개면,&amp;nbsp;가장&amp;nbsp;작은&amp;nbsp;수를&amp;nbsp;답한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 &amp;lt;= e &amp;lt;= 5,000,000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 &amp;lt;= starts.length &amp;lt;= min(e, 100,000)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 &amp;lt;= starts 원소 &amp;lt;= e&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;풀이방향 요약&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. [1 ~ e] 구간 각 숫자 별로 빈도수를 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. starts 배열을 순회하면서 [s ~ e] 구간에서 각 구간별로 빈도수가 최대이면서, 가장 작은 값을 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;풀이방향 상세설명&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e가 최대 500만이므로, 시간복잡도를 가장 신경써야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 숫자 b가 억억단에서 몇번 등장했는지 어떻게 카운트할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 예시에 힌트가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번등장 : 1, 2번등장 : 2,3,5,7, 3번등장 : 4&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들의 공통점은 등장횟수 = 약수의 갯수다. 억억단에서의 등장 횟수를 &lt;span&gt;b의 약수의 갯수로 취급할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 우선 1~e까지의 약수의 갯수를 각자의 방법대로 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(= 약수 갯수 구하는 방법은 많지만, 시간초과를 생각하며 문제에 적당한 것을 찾아보자.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음엔 s~e 범위에서 약수의 갯수가 가장 많으면서, 가장 작은 숫자를 찾아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e가 작은 숫자라면 &lt;b&gt;slice + Math.max&lt;/b&gt;를 활용해도 되겠지만, 시간초과가 발생하지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서도 약수의 갯수를 미리 구해둔것 처럼, 무언가를 미리 구한 뒤 바로 써먹으면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 구간 [s ~ e]에서, s는 변하지만 e는 고정되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 [1 ~ e]의 정답은 1번 인덱스에, [25700 ~ e]는 25700번 인덱스에 저장해두면 쓰기 좋아보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해선 배열의 끝(e)부터 1번 인덱스까지 역순으로 순회하면서, 약수의 갯수가 가장 많은 최솟값을 기록하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이정도 이해했다면 코드를 짜러가면 되겠다. 물론 밑에 주석을 첨부한 코드도 첨부해뒀다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주석을 첨부한 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1669367410024&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(e, starts) {
  // 약수의 갯수들, 구간별 최대약수갯수인 최솟값을 구한뒤에
  // s에 대응하는 최솟값들을 출력하고 끝난다.
  const denominators = getAllDenominators(e);
  const lowestMaxNumArr = getLowestMaxNumArr(denominators, e);
  const answer = starts.map((s) =&amp;gt; lowestMaxNumArr[s]);

  return answer;
}

/**
 * 모든 약수를 구한다. 각자 찾은 방법대로 구하자.
 * (e에 따른 시간복잡도는 고려해야한다.)
 * 약수 카운팅중에 [1*자기자신]을 건너뛰고 싶어서 기본값을 2로 설정했다.
 */
function getAllDenominators(size) {
  const result = new Array(size + 1).fill(2);
  result[0] = 0;
  result[1] -= 1;

  for (let i = 2; i &amp;lt;= size; i++) {
    for (let j = 2, end = Math.floor(size / i); j &amp;lt;= end; j++) {
      result[i * j]++;
    }
  }

  return result;
}

/**
 * lowestMaxNumArr의 인덱스 k는 [k ~ e] 구간에서의 약수의 갯수가 가장 많은 최솟값이다.
 * 인덱스 e부터 순회하면서, 현재의 약수의 갯수가 앞선 인덱스의 약수의 갯수보다 크거나 같다면 갱신한다.
 * 같을 때는 현재 인덱스가 앞선 최솟값 인덱스보다 더 최솟값이므로, 꼭 갱신해줘야한다.
 */
function getLowestMaxNumArr(arr, size) {
  const lowestMaxNumArr = new Array(size + 1).fill(0);
  lowestMaxNumArr[size] = size;

  for (let i = size - 1; i &amp;gt; 0; i--) {
    const cur = arr[i];
    const prev = arr[lowestMaxNumArr[i + 1]];

    lowestMaxNumArr[i] = cur &amp;gt;= prev ? i : lowestMaxNumArr[i + 1];
  }

  return lowestMaxNumArr;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/programmers</category>
      <category>억억단을 외우자 javascript</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/154</guid>
      <comments>https://gobae.tistory.com/154#entry154comment</comments>
      <pubDate>Fri, 25 Nov 2022 18:14:44 +0900</pubDate>
    </item>
    <item>
      <title>[뻘글] 2022년이 끝나가는 시점에서의 근황</title>
      <link>https://gobae.tistory.com/153</link>
      <description>&lt;h3 id=&quot;%EC%--%A-%EB%-E%-C%EB%A-%-C%EC%--%--%--%ED%-F%AC%EC%-A%A-%ED%-C%--%ED%--%B-%EC%--%-C%--%EA%B-%BC%ED%--%A-%--%EC%A-%BC%EC%A-%--%EB%A-%AC%EC%A-%BC%EC%A-%--%EB%A-%AC%--%EC%B-%A-%EB%B-%--&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 현 시점에서 내 상황을 정리하고 남은 40일을 잘 보내기 위해 짧게 글을 써볼 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;뭐하고 계신가요?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 취업준비와 우아한테크코스 웹프론트엔드 5기 프리코스를 병행하느라 통 포스팅을 못했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;오늘로 프리코스가 끝난덕에&lt;span&gt;&amp;nbsp;코딩테스트 감을 살리려고&amp;nbsp;&lt;/span&gt;&lt;/span&gt;프로그래머스에 접속했었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 간 새롭게 나온 알고리즘 문제를 접했고, 풀자마자 주저없이 포스팅하러 달려왔다. ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자바스크립트로 코테 준비하시는 분들 화이팅입니다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 앞으로 계속 다른 채용과정들도 열심히 준비하면서, 우테코 본코스에 대한 고민(과제전형 응시 여부)도 함께 할 예정이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그래서 현재 상황은?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상했던 취준 기간보다 더 길어지고 있는 상황이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 취업한 대학 동기들과 소통하면서 지금 내 상황에 대해 많은 생각들을 하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 우테코 광고를 보고 바로 신청했고, 프리코스에서 새로운 개발자 동료분들을 만나면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년 이맘때 부스트캠프에 열중했던 기억도 나고 하더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 &quot;그래서 나는 2022년에 얼마나 달라진거지?&quot;라는 생각으로 지나온 길을 돌아보니,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입구컷(코딩테스트 탈락) 빈도도 줄었고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;나름 알게된 것들도 많고,&lt;span&gt;&amp;nbsp;여러 불합격 덕에 내면을 좀 더 돌아볼 수 있었고.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 모든 사람이 겪는 과정이라고 인정하니, &quot;얼마 남지 않았다!&amp;nbsp;&lt;span&gt;더 열심히 해야겠다!&quot;는 생각이 들더라. ㅎㅎ&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;내가 좋아서 선택한 만큼, 좀 더 인내심을 가지고 도전해보자. 분명히 성장하고 있다.&quot; 라는게 결국 결론이었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;우테코 프리코스의 짧은 후기?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것저것 준비하고 떨어지고 공부하면서 텐션이 꽤 떨어져있던 와중에 우테코 모집 광고를 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;우선 넣어보자!&quot;&amp;nbsp;라고 생각하고 웹 프론트엔드 5기 지원페이지로 들어갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 어떻게 학습해왔는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 왜 프로그래머가 되고 싶은지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 끌리는 무언가에 몰입한 적 있는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 왜 우테코에 참여하고 싶은지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 문항들에 대한 생각을 정리하면서, 부스트캠프6기를 지원하던 때가 떠오르더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;며칠이 지나고, 4주의 프리코스 과정에서 몰입할 준비가 되었는지 스스로 테스트해보라는 메일이 왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 4주 과정이 시작되었고, 슬랙에서 모이게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슬랙의 첫인상은 부스트캠프때랑 역시 같더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다들 하고자 하는 열의가 뛰어나고, 치열하게 고민하고 좋은 것들을 서로 나누고, 피드백을 요청하고 등등.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격의 공간이지만, 과정을 함께하는 동료들이 있다는 점 만으로도 동기부여를 주기에 충분했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 진행중인 채용 과정들과 일들 때문에 완전하게 집중할 순 없었으나, 나름&amp;nbsp;열심히 고민해서 과제를 내고 개선하고 테스트까지 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성한 코드 기반으로 테스트를 통과하지 못한 주차는 없어서 무척 다행이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프리코스 과정을 통해 몇 가지 지식 및 기술들을 더 보충할 수 있어 무척 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항 분석 및 설계, 객체지향 고민, 클린코드, 리팩터링, 테스트코드 습관화하기 등등.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 가장 어려웠던 것은 기능 단위로 나누기와 커밋 메시지였다 &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수도, 컴포넌트도, 클래스도 항상 어떻게 잘 나눠야 할까?라는 고민이 생길 수 밖에 없으니까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 잘 나눠줘야 단위 테스트 작성도 편하고, 리팩터링 할 일도 줄어들고..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구사항 기반 기능 분석 및 설계하는 챕터가 가장 기본적인 일이지만 사실 가장 어려운 일이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재밌는 점은 슬랙에서 동료분들도 동일한 문제를 겪으면서 힘들어하고 계셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;결국 모든 경우에 좋은 방법이란 없기 때문에 다양한 방법론도 디자인패턴들도 등장하게 되었구나.&quot; 생각이 들더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;상황에 맞춰서 판단하여 어떤 것이 제일 최고의 도구인지 고민하는 것.&quot; 결국 이 능력을 키우는 것이 경쟁력인 듯 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각해보니 부스트캠프 6기 멤버십 후기를 빼먹은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멤버십 후기를 써달라는 댓글을 보고, &quot;취업하고 바로 써야지! 금방 쓰겠구만!&quot; 이라고 생각했던 것 같은데...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하하. 이제 후기를 쓰고 싶어서라도, 더 힘내야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧게 글을 끝내려고 소제목도 달지 않으면서 썼는데, 주저리주저리 쓰고 보니 길어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 나만 볼 근황을 블로그에 올리는게 맞나 생각이 들었는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글을 쓰면서 지금 동기부여도 되고 더 열심히 해야지라는 생각이 드는 걸 보니 쓰길 잘했다! ㅎㅎ &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 취준생분들 화이팅입니다!&lt;/p&gt;</description>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/153</guid>
      <comments>https://gobae.tistory.com/153#entry153comment</comments>
      <pubDate>Tue, 22 Nov 2022 22:42:21 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 숫자 카드 나누기 - 자바스크립트</title>
      <link>https://gobae.tistory.com/152</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/135807&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/135807&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩테스트 대비 겸 오랜만에 프로그래머스에 들어갔는데, 새로운 문제가 나와서 정리하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;webp.webp&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Yj2XA/btrRR8TfiYR/KULqTKo7OFMRs9yExCtrWK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Yj2XA/btrRR8TfiYR/KULqTKo7OFMRs9yExCtrWK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Yj2XA/btrRR8TfiYR/KULqTKo7OFMRs9yExCtrWK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYj2XA%2FbtrRR8TfiYR%2FKULqTKo7OFMRs9yExCtrWK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;275&quot; data-filename=&quot;webp.webp&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1669120433152&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(arrayA, arrayB) {
  const cd1 = getCd(...arrayA.slice(0, 2));
  const cd2 = getCd(...arrayB.slice(0, 2));
  const allCdOfArrA = getAllCd(cd1, arrayA);
  const allCdOfArrB = getAllCd(cd2, arrayB);
  const deDuplicatedAllCd = deDuplicate(allCdOfArrA, allCdOfArrB);

  return findAnswer(deDuplicatedAllCd, arrayA, arrayB);
}

function getCd(num1 = 0, num2 = 0) {
  const cd = [];
  const max = Math.max(num1, num2);

  for (let i = 2; i &amp;lt;= max; i++) {
    if (isEqual(num1 % i, 0) &amp;amp;&amp;amp; isEqual(num2 % i, 0)) {
      cd.push(i);
    }
  }

  return cd;
}

function getAllCd(cdArr, target) {
  const allCD = [];

  while (cdArr.length) {
    const pop = cdArr.pop();
    let flag = 1;

    for (let i = 0, len = target.length; i &amp;lt; len; i++) {
      if (!isEqual(target[i] % pop, 0)) {
        flag = 0;
        break;
      }
    }

    if (flag) allCD.push(pop);
  }

  return allCD;
}

function deDuplicate(arr1, arr2) {
  return [...new Set([...arr1, ...arr2])].sort((a, b) =&amp;gt; b - a);
}

function findAnswer(cd, arr1, arr2) {
  const arr1Len = arr1.length;
  const arr2Len = arr2.length;

  for (let i = 0, len = cd.length; i &amp;lt; len; i++) {
    const cntArr1 = arr1.filter((el) =&amp;gt; isCD(el, cd[i])).length;
    const cntArr2 = arr2.filter((el) =&amp;gt; isCD(el, cd[i])).length;

    if ((isEqual(cntArr1, arr1Len) &amp;amp;&amp;amp; isEqual(cntArr2, 0)) || (isEqual(cntArr2, arr2Len) &amp;amp;&amp;amp; isEqual(cntArr1, 0))) {
      return cd[i];
    }
  }

  return 0;
}

function isCD(num, divider) {
  return !(num % divider);
}

function isEqual(a, b) {
  return a === b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 조건&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;철수, 영희는 숫자가 적힌 카드들을 절반씩 나눈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음을 만족하는 가장 큰 양의정수 a의 값을 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 철수의 모든 숫자를 나눌 수 있고, 영희가 가진 숫자는 하나도 나눌 수 없는 a&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 영희의 모든 숫자를 나눌 수 있고, 철수가 가진 숫자는 하나도 나눌 수 없는 a&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적합한 a가 없다면 0이 정답이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 &amp;lt;= 숫자카드 array 크기 &amp;lt;= 500,000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1&amp;lt;= 숫자 array 원소 &amp;lt;= 100,000,000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;arrayA, arrayB에는 중복된 원소가 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 숫자카드 배열이 50만개까지 있으므로, 단순한 반복을 해서는 시간초과가 날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 배열 반복은 어쩔 수 없는 부분이므로, 반복해야 하는 횟수 자체를 줄여보도록 하는 편이 좋겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 생각한 방법은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 철수의 모든 숫자의 공약수를 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 영희의 모든 숫자의 공약수를 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 두 공약수 배열을 중복을 없앤 하나의 배열로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 3번의 배열에서 가장 큰 공약수부터 철수/영희의 모든 숫자를 나누고, 조건1 or 조건2가 만족하면 정답이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 모든 공약수가 조건1 or 조건2를 만족하지 못한다면 0이 정답이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;***&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 조건에 유의한다. 처음 a,b의 공약수를 구할 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 숫자카드 배열 원소 갯수가 1개밖에 주어지지 않는다면 b가 undefined일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;***&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주석을 첨부한 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1669121117875&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(arrayA, arrayB) {
  const cd1 = getCd(...arrayA.slice(0, 2));
  const cd2 = getCd(...arrayB.slice(0, 2));
  const allCdOfArrA = getAllCd(cd1, arrayA);
  const allCdOfArrB = getAllCd(cd2, arrayB);
  const deDuplicatedAllCd = deDuplicate(allCdOfArrA, allCdOfArrB);

  return findAnswer(deDuplicatedAllCd, arrayA, arrayB);
}

/**
 * getCd 함수는 num1, num2를 받아 공약수의 배열을 반환해준다.
 * array의 원소가 1개일 때는 num2=undefined이므로, 기본값(0 or 1)을 지정해준다.
 */
function getCd(num1 = 0, num2 = 0) {
  const cd = [];
  const max = Math.max(num1, num2);

  for (let i = 2; i &amp;lt;= max; i++) {
    if (isEqual(num1 % i, 0) &amp;amp;&amp;amp; isEqual(num2 % i, 0)) {
      cd.push(i);
    }
  }

  return cd;
}

/**
 * getAllCd 함수는 cdArr(공약수배열), target(카드 array)를 받아
 * target 전체의 공약수 배열을 반환한다.
 */
function getAllCd(cdArr, target) {
  const allCD = [];

  while (cdArr.length) {
    const pop = cdArr.pop();
    let flag = 1;

    for (let i = 0, len = target.length; i &amp;lt; len; i++) {
      if (!isEqual(target[i] % pop, 0)) {
        flag = 0;
        break;
      }
    }

    if (flag) allCD.push(pop);
  }

  return allCD;
}

/**
 * deDuplicate 함수는 두개의 배열을 받아 중복을 없앤 뒤 내림차순 정렬한 배열을 반환한다.
 */
function deDuplicate(arr1, arr2) {
  return [...new Set([...arr1, ...arr2])].sort((a, b) =&amp;gt; b - a);
}

/**
 * findAnswer 함수는 공약수배열, arrayA, arrayB를 받아
 * 가장 큰 공약수부터 조회하면서 조건1 or 조건2를 만족한다면 정답으로 반환해준다.
 * 모든 공약수가 조건을 만족하지 못한다면, 0을 반환한다.
 */
function findAnswer(cd, arr1, arr2) {
  const arr1Len = arr1.length;
  const arr2Len = arr2.length;

  for (let i = 0, len = cd.length; i &amp;lt; len; i++) {
    const cntArr1 = arr1.filter((el) =&amp;gt; isCD(el, cd[i])).length;
    const cntArr2 = arr2.filter((el) =&amp;gt; isCD(el, cd[i])).length;

    if ((isEqual(cntArr1, arr1Len) &amp;amp;&amp;amp; isEqual(cntArr2, 0)) || (isEqual(cntArr2, arr2Len) &amp;amp;&amp;amp; isEqual(cntArr1, 0))) {
      return cd[i];
    }
  }

  return 0;
}

function isCD(num, divider) {
  return !(num % divider);
}

function isEqual(a, b) {
  return a === b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히, 더 효율적인 방법이 있을 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;이렇게도 푸는구나.&quot;정도로만 참고해주시길 바랍니다!.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/programmers</category>
      <category>프로그래머스 숫자카드나누기 javascript</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/152</guid>
      <comments>https://gobae.tistory.com/152#entry152comment</comments>
      <pubDate>Tue, 22 Nov 2022 22:04:04 +0900</pubDate>
    </item>
    <item>
      <title>[Array] 일반적인 배열과 자바스크립트의 배열 알아보기</title>
      <link>https://gobae.tistory.com/151</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;배열(Array)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-10-13 오후 4.30.25.webp&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NqpmY/btrOwxB4oRG/arKgZ2Ux8PuqIjlI0ajvK0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NqpmY/btrOwxB4oRG/arKgZ2Ux8PuqIjlI0ajvK0/img.webp&quot; data-alt=&quot;배열&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NqpmY/btrOwxB4oRG/arKgZ2Ux8PuqIjlI0ajvK0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNqpmY%2FbtrOwxB4oRG%2FarKgZ2Ux8PuqIjlI0ajvK0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;244&quot; data-filename=&quot;스크린샷 2022-10-13 오후 4.30.25.webp&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;배열&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 배열에 대해 가볍게 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열은 &lt;b&gt;동일한 크기&lt;/b&gt;의 메모리 공간을 &lt;b&gt;연속적&lt;/b&gt;으로 나열한 구조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 크기여야 하므로 하나의 통일된 타입의 요소들이, 연속적으로 인접해있는 형태를 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 &quot;데이터가 연속으로 인접한 배열&quot;을 밀집배열(dense array)라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;밀집배열의 경우, 인덱스를 이용해 한번의 연산으로 접근이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연산 횟수가 1회이므로, O(1)의 시간복잡도를 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배열의 단점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스(찾으려는 데이터의 위치)를 모르면 선형탐색을 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 선형탐색은 O(n) 시간복잡도.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열 마지막 요소가 아닌 위치에 추가/삭제 시 배열 요소들의 연속적인 이동이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 삽입과 삭제에서 O(1) ~ O(N)의 시간복잡도.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;tmi) 왜 장점과 단점들을 따지는가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트를 만들 때 보통 2가지 자료구조를 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 배열기반 리스트 (Array)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 연결 리스트 (Node, Vertex)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배열기반 리스트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 참조에 O(1)의 놀라운 성능을 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색에 O(1) ~ O(N), 삽입과 삭제에는 O(1) ~ O(N)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색 + 삽입/삭제가 빈번하게 일어나야한다면 좋지 않은 선택이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터의 인덱스를 알고있는 상황이라면 좋은 선택일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;연결 리스트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드들을 포인터로 연결한 구조로, 인덱스 참조가 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 노드로의 포인터가 필요하므로, 추가 메모리가 필요하다.(데이터 + 다음 노드정보 2가지를 기억)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색에 O(N), 삽입과 삭제에는 O(1)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색 속도는 배열기반과 같으나, 삽입과 삭제에서 강점을 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삽입/삭제가 빈번하게 일어나야 한다면 좋은 선택일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;각 장단점을 먼저 파악하고, 해결해야 할 문제에 적합한 자료구조를 선택하는 것이 매우 중요하다고 할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바스크립트의 배열 알아보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 자바스크립트 배열은 위에서 알아본 배열과 꽤 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 우선, 동일한 크기일 필요가 없다. 그래서 다른 타입의 요소를 넣어도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 연속적으로 이어지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 밀집배열(dense array)를 언급했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 배열은 sparse array(희소 배열)이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(ex. Array를 선언하고 0,1번을 건너뛰고 2번 인덱스에 바로 원소를 넣어줄 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서의 배열은 일반적인 배열이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;배열의 동작을 흉내내는 특수 객체&lt;/b&gt;&lt;/u&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfVykN/btrOwPifIkE/BJKnTNCjG6zxN2XmOq1OC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfVykN/btrOwPifIkE/BJKnTNCjG6zxN2XmOq1OC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfVykN/btrOwPifIkE/BJKnTNCjG6zxN2XmOq1OC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfVykN%2FbtrOwPifIkE%2FBJKnTNCjG6zxN2XmOq1OC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;438&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 자료구조를 넣을 수 있으며, typeof의 결과로 object가 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/29l5U/btrOxlucSHI/sRHercycFWB3BZUNVJrT10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/29l5U/btrOxlucSHI/sRHercycFWB3BZUNVJrT10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/29l5U/btrOxlucSHI/sRHercycFWB3BZUNVJrT10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F29l5U%2FbtrOxlucSHI%2FsRHercycFWB3BZUNVJrT10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;160&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열의 프로토타입이 Array.prototype이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array.prototype의 상위 프로토타입이 Object.prototype이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eLbRbe/btrOwjjQBRO/qjJdsg2Rk0ppneoHZPeGuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eLbRbe/btrOwjjQBRO/qjJdsg2Rk0ppneoHZPeGuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eLbRbe/btrOwjjQBRO/qjJdsg2Rk0ppneoHZPeGuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeLbRbe%2FbtrOwjjQBRO%2FqjJdsg2Rk0ppneoHZPeGuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;342&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심지어는 배열에 숫자 인덱스가 아닌 'hello'라는 프로퍼티도 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 자바스크립트 배열에서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;arr[1] = 2가 의미하는 것이,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;연속된 공간에서의 주소값 기준 인덱스만큼의 크기 뒤에 있는 주소값에 담긴 값&quot;의 의미가 아니게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0,1,2번 인덱스는 객체에서 key에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0,1,2번 인덱스에 담긴 12, 'string', false 값은 객체에서 value에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신, 아래의 Array.prototype으로 배열로서 필요한 기능들을 지원하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;1324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgEzNf/btrOxkvkXBt/VWTyltMNkKIr1nv5tCtoZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgEzNf/btrOxkvkXBt/VWTyltMNkKIr1nv5tCtoZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgEzNf/btrOxkvkXBt/VWTyltMNkKIr1nv5tCtoZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgEzNf%2FbtrOxkvkXBt%2FVWTyltMNkKIr1nv5tCtoZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;588&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;1324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 그렇다고 해서 Object로 만든 배열과 Array로 만든 배열의 성능이 동일하진 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 배열과 객체를 만든 뒤에, 프로퍼티에 삽입해주면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4vxPH/btrOs2b31fb/UpY1RsDV1apScwmnmxYXk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4vxPH/btrOs2b31fb/UpY1RsDV1apScwmnmxYXk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4vxPH/btrOs2b31fb/UpY1RsDV1apScwmnmxYXk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4vxPH%2FbtrOs2b31fb%2FUpY1RsDV1apScwmnmxYXk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;341&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object에 비해 Array가 약 2배정도 빠른 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 자바스크립트 엔진에서, 배열을 좀 더 배열처럼 동작할 수 있게 인덱스 접근을 최적화한 결과라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;객체로 배열을 만들면 뭐가 다른데...?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;우선, 일반 배열보다 인덱스접근의 성능이 떨어진다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 배열의 인덱스 접근은 메모리주소 + 위치참조로 O(1)의 성능이 기대된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서는 해시함수로 위치를 찾아야 하므로, 해시함수의 성능에 따라 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 일반적으로, 인덱스 접근보다 성능이 떨어질 수 밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;대신 삽입/삭제에서는 더 성능이 좋다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 배열에서는 중간에서 삽입/삭제가 일어나면 메모리에 데이터들을 직접 옮겨가야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 삽입/삭제에서 성능이 좋아졌다곤 해도, 노드기반 삽입/삭제에 비할바는 못된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삽입/삭제가 빈번하다면, 배열리스트보다는 연결 리스트가 유리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;객체이기 때문에 메모리, 자료구조의 유연함을 가진다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열은 배열 크기만큼 메모리공간을 미리 확보해둬야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;미리 확보&quot;해야 하기 때문에, 동일한 원소크기 + 정해진 배열크기를 요구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 기반으로 한다면, 공간을 미리 확보할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 배열에 보관할 자료구조에 대한 제약이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(사실 원소를 넣기 전에는 크기를 알 수 없으니, 메모리를 미리 확보할 수도 없다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 그래서 자바스크립트의 Array를 객체처럼 써도 상관없는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 자바스크립트 엔진에서는 Array가 배열스러운 동작을 할 수 있도록 최적화되어있기 때문.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 배열을 다루면서 무조건적으로 알아야 하는 내용은 아닌 것 같지만, 알아둬도 나쁠 것은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열 인스턴스의 length 프로퍼티를 항상 이상하게 생각해왔던 의문을 풀고자 공부하게 된 내용이다. ㅎㅎ..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 개발 or 코딩테스트시 좀 더 도움이 될 내용을 적어보면,&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배열에서 push,pop이 아닌&amp;nbsp;unshift, shift, splice 메서드를 많이 사용해야 한다면?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시간복잡도를 먼저 생각하자. 배열리스트보다 연결리스트가 더 나을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 위 상황에 대비하기 위해, 연결리스트정도는 스스로 만들어보는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;typeof 배열이 'object'인데 그럼 어떻게 배열인지 타입검사하나요?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스태틱 메서드인 Array.isArray 활용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;일정한 크기의 0으로 초기화된 Array를 어떻게 선언하나요?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 나는 Array.from({length: n}, () =&amp;gt; 0); 의 방법을 선호한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 좀 더 간단한 new Array(n).fill(0)도 있으나, 원시값이 아닌 참조값을 각 배열에 선언할 때 문제가 되기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 2차원 배열을 만들고자 할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- Array.from({length: n}, () =&amp;gt; new Array(m).fill(0)); 은 n행에 담긴 m크기의 배열이 각각 선언된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- new Array(n).fill(new Array(m).fill(0)); 로 선언하면, 동일한 new Array(m).fill(0)이 n행에 담기는 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 물론 원시값을 가지면 new Array(n).fill(0)이 제일 편하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 각각의 스타일대로 선언하자. []로 만들고 for문으로 하나씩 넣어줘도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 배열의 특수함</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/151</guid>
      <comments>https://gobae.tistory.com/151#entry151comment</comments>
      <pubDate>Thu, 13 Oct 2022 17:36:43 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 25632번 소수부르기게임 - 자바스크립트</title>
      <link>https://gobae.tistory.com/150</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1664254813556&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;25632번: 소수 부르기 게임&quot; data-og-description=&quot;용태가 부를 수 있는 소수는 $11, 13, 17$이고, 유진이가 부를 수 있는 소수는 $13, 17, 19$이다. 둘 다 최선을 다해서 플레이한다면 $13 &amp;rarr; 17 &amp;rarr; 11 &amp;rarr; 19$로 진행될 수 있다. 용태가 더 이상 부를 소수가 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/25632&quot; data-og-url=&quot;https://www.acmicpc.net/problem/25632&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/B6bAb/hyPWH2xcFZ/hDk2qOSofyhm4U6fOrIabk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/25632&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/25632&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/B6bAb/hyPWH2xcFZ/hDk2qOSofyhm4U6fOrIabk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;25632번: 소수 부르기 게임&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;용태가 부를 수 있는 소수는 $11, 13, 17$이고, 유진이가 부를 수 있는 소수는 $13, 17, 19$이다. 둘 다 최선을 다해서 플레이한다면 $13 &amp;rarr; 17 &amp;rarr; 11 &amp;rarr; 19$로 진행될 수 있다. 용태가 더 이상 부를 소수가&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbYegK/btrNbZzsHgd/zzdCzcisQwymsKk5XdrKHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbYegK/btrNbZzsHgd/zzdCzcisQwymsKk5XdrKHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbYegK/btrNbZzsHgd/zzdCzcisQwymsKk5XdrKHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbYegK%2FbtrNbZzsHgd%2FzzdCzcisQwymsKk5XdrKHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;448&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 나온 따끈따끈한 문제에,, 실버4 난이도 치고는 생각할 게 조금 있어서 가져오게 되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1664254963287&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function sol(input) {
  const [A, B] = input[0].split(' ').map(Number);
  const [C, D] = input[1].split(' ').map(Number);

  const yt = eratosThenes(A, B);
  const yj = eratosThenes(C, D);
  const ytPrimes = {};
  let intersection = 0;

  yt.forEach((prime) =&amp;gt; (ytPrimes[prime] = 1));
  yj.forEach((prime) =&amp;gt; {
    if (ytPrimes[prime]) intersection++;
  });

  const cnt = { yt: yt.length, yj: yj.length };

  while (cnt.yt &amp;gt; 0 &amp;amp;&amp;amp; cnt.yj &amp;gt; 0) {
    if (intersection &amp;gt;= 2) {
      intersection -= 2;
      cnt.yt -= 2;
      cnt.yj -= 2;
    } else if (intersection &amp;gt;= 1) {
      intersection--;
      cnt.yt -= 1;
      cnt.yj -= 2;
    } else {
      cnt.yt -= 1;
      cnt.yj -= 1;
    }
  }

  return cnt.yt &amp;gt; cnt.yj ? 'yt' : 'yj';
}

function eratosThenes(start, end) {
  const arr = Array.from({ length: 1001 }, (_, i) =&amp;gt; i);
  const sqrt = Math.floor(arr.length);
  arr[1] = 0;

  for (let i = 2; i &amp;lt;= sqrt; i++) {
    if (arr[i] === 0) continue;

    for (let j = 2 * i; j &amp;lt;= 1000; j += i) {
      arr[j] = 0;
    }
  }

  return arr.slice(start, end + 1).filter((elem) =&amp;gt; elem);
}



const input = [];
require('readline')
  .createInterface(process.stdin, process.stdout)
  .on('line', (line) =&amp;gt; {
    input.push(line);
  })
  .on('close', () =&amp;gt; {
    console.log(sol(input));
    process.exit();
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;조건&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용태는 범위 A,B 내에 있는 소수를 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유진이는 범위 C,D 내에 있는 소수를 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용태부터 시작해 서로 번갈아가며 부를 수 있는 범위의 소수를 부른다. 그러나 중복되선 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더이상 소수를 부를 수 없다면 패배한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2 &amp;lt;= A &amp;lt;= B &amp;lt;= 1000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2 &amp;lt;= C &amp;lt;= D &amp;lt;= 1000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Q. &quot;용태와 유진이가 모두 &lt;b&gt;최선을 다해&lt;/b&gt; 게임을 플레이 했을 때 누가 이길까?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;풀이방향&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;keypoint : &quot;최선을 다해야 하므로&quot;&lt;/b&gt;&amp;nbsp;용태/유진이의 &lt;b&gt;소수&amp;nbsp;교집합&lt;/b&gt;을 먼저 불러야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 각자의 소수 구하는 방법을 이용해서 A,B범위, C,D 범위의 소수들을 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 각자의 방법으로 용태와 유진이의 소수 교집합을 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 나는 용태 소수들을 먼저 기록하고, 유진이도 동일한 소수를 가지고 있다면 카운팅했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 먼저 소수 교집합을 소진시키고, 각자의 소수들을 소진시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 용태, 유진이가 각각 소수를 한번씩 부르는 것을 1개의 사이클로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 둘 다 부를 소수가 없다면, 용태 순서이므로 용태가 패배함을 유의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주석포함&lt;/h3&gt;
&lt;pre id=&quot;code_1664255529744&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function sol(input) {
  const [A, B] = input[0].split(' ').map(Number);
  const [C, D] = input[1].split(' ').map(Number);

  const yt = eratosThenes(A, B); // 용태의 소수
  const yj = eratosThenes(C, D); // 유진이의 소수
  const ytPrimes = {};
  let intersection = 0;

  yt.forEach((prime) =&amp;gt; (ytPrimes[prime] = 1));
  yj.forEach((prime) =&amp;gt; {
  	// 유진이의 소수가 용태의 소수면 소수교집합 카운트
    if (ytPrimes[prime]) intersection++;
  });

  const cnt = { yt: yt.length, yj: yj.length };

  while (cnt.yt &amp;gt; 0 &amp;amp;&amp;amp; cnt.yj &amp;gt; 0) {
  	// 1개 사이클에 각자 번갈아가며 숫자를 불러줘야 함.
    if (intersection &amp;gt;= 2) {
    	// 교집합이 있다면 먼저 소진.
      intersection -= 2;
      cnt.yt -= 2;
      cnt.yj -= 2;
    } else if (intersection &amp;gt;= 1) {
        // 교집합이 1개 남았다면, 용태만 소진하고 유진이는 자신의 소수 부름.
      intersection--;
      cnt.yt -= 1;
      cnt.yj -= 2;
    } else {
	    // 	교집합이 없다면 각자의 소수 부름.
      cnt.yt -= 1;
      cnt.yj -= 1;
    }
  }
	
    // == 을 포함하면 용태가 부를차례에 남은 수가 없음에도 이기게 되어 실패
  return cnt.yt &amp;gt; cnt.yj ? 'yt' : 'yj';
}


function eratosThenes(start, end) {
	// 각자의 방법으로 구간 내의 소수를 구하는 공식을 써보자.
  const arr = Array.from({ length: 1001 }, (_, i) =&amp;gt; i);
  const sqrt = Math.floor(arr.length);
  arr[1] = 0;

  for (let i = 2; i &amp;lt;= sqrt; i++) {
    if (arr[i] === 0) continue;

    for (let j = 2 * i; j &amp;lt;= 1000; j += i) {
      arr[j] = 0;
    }
  }

  return arr.slice(start, end + 1).filter((elem) =&amp;gt; elem);
}



const input = [];
require('readline')
  .createInterface(process.stdin, process.stdout)
  .on('line', (line) =&amp;gt; {
    input.push(line);
  })
  .on('close', () =&amp;gt; {
    console.log(sol(input));
    process.exit();
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 쉽고 간단한 방법이 존재할 수 있습니다!&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/baekjoon</category>
      <category>25632 소수 부르기 게임</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/150</guid>
      <comments>https://gobae.tistory.com/150#entry150comment</comments>
      <pubDate>Tue, 27 Sep 2022 14:15:48 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1753번 최단경로 - 자바스크립트</title>
      <link>https://gobae.tistory.com/149</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1663838776612&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1753번: 최단경로&quot; data-og-description=&quot;첫째 줄에 정점의 개수 V와 간선의 개수 E가 주어진다. (1 &amp;le; V &amp;le; 20,000, 1 &amp;le; E &amp;le; 300,000) 모든 정점에는 1부터 V까지 번호가 매겨져 있다고 가정한다. 둘째 줄에는 시작 정점의 번호 K(1 &amp;le; K &amp;le; V)가 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1753&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1753&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bp3KOA/hyPSRxX5qW/WPifbnykP9kO5okQEVE3jk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1753&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1753&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bp3KOA/hyPSRxX5qW/WPifbnykP9kO5okQEVE3jk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;1753번: 최단경로&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 정점의 개수 V와 간선의 개수 E가 주어진다. (1 &amp;le; V &amp;le; 20,000, 1 &amp;le; E &amp;le; 300,000) 모든 정점에는 1부터 V까지 번호가 매겨져 있다고 가정한다. 둘째 줄에는 시작 정점의 번호 K(1 &amp;le; K &amp;le; V)가&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2098&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bluSId/btrNbbz1wrR/PKcK3sOdNuBoDVLTwBSUk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bluSId/btrNbbz1wrR/PKcK3sOdNuBoDVLTwBSUk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bluSId/btrNbbz1wrR/PKcK3sOdNuBoDVLTwBSUk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbluSId%2FbtrNbbz1wrR%2FPKcK3sOdNuBoDVLTwBSUk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;169&quot; data-origin-width=&quot;2098&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1663838832651&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const MinHeap = (function () {
  function MinHeap() {
    this.heap = [-Infinity];
  }

  MinHeap.prototype.size = function () {
    return this.heap.length - 1;
  };

  MinHeap.prototype.push = function (val) {
    this.heap.push(val);
    this._upheap(this.size());
  };

  MinHeap.prototype._upheap = function (pos) {
    let pushed = this.heap[pos];
    let parentPos = Math.floor(pos / 2);

    while (pushed.dist &amp;lt; this.heap[parentPos].dist) {
      this.heap[pos] = this.heap[parentPos];
      pos = parentPos;
      parentPos = Math.floor(pos / 2);
    }

    this.heap[pos] = pushed;
  };

  MinHeap.prototype.pop = function () {
    if (this.size() === 1) return this.heap.pop();

    let popped = this.heap[1];
    this.heap[1] = this.heap.pop();
    this._downheap(1, this.size());
    return popped;
  };

  MinHeap.prototype._downheap = function (pos, lastPos) {
    const target = Math.floor(lastPos / 2);
    let lastVal = this.heap[pos];
    let childPos;

    while (pos &amp;lt;= target) {
      childPos = pos * 2;
      if (childPos &amp;lt; lastPos &amp;amp;&amp;amp; this.heap[childPos].dist &amp;gt; this.heap[childPos + 1].dist) childPos += 1;
      if (lastVal.dist &amp;lt;= this.heap[childPos].dist) break;
      this.heap[pos] = this.heap[childPos];
      pos = childPos;
    }
    this.heap[pos] = lastVal;
  };

  return MinHeap;
})();

function sol(input) {
  const [V, E] = input[0].split(' ').map(Number);
  const k = +input[1];

  const graphs = Array.from({ length: V + 1 }, () =&amp;gt; new Array());

  for (let i = 2, iter = input.length; i &amp;lt; iter; i++) {
    const [from, to, w] = input[i].split(' ').map(Number);
    graphs[from].push({ node: to, dist: w });
  }

  const visits = new Array(V + 1).fill(0);
  const distance = new Array(V + 1).fill(Infinity);
  distance[k] = 0;

  const pq = new MinHeap();
  pq.push({ node: k, dist: 0 });

  while (pq.size()) {
    const { node, dist } = pq.pop();

    if (visits[node]) continue;
    visits[node] = 1;

    const nexts = graphs[node];
    if (!nexts.length) continue;

    for (let i = 0, iter = nexts.length; i &amp;lt; iter; i++) {
      const { node: nextNode, dist: nextDist } = nexts[i];

      if (distance[nextNode] &amp;gt; dist + nextDist) {
        distance[nextNode] = dist + nextDist;
        pq.push({ node: nextNode, dist: distance[nextNode] });
      }
    }
  }

  return distance
    .slice(1)
    .map((e) =&amp;gt; (e === Infinity ? 'INF' : e))
    .join('\n');
}

const input = [];
require('readline')
  .createInterface(process.stdin, process.stdout)
  .on('line', (line) =&amp;gt; {
    input.push(line);
  })
  .on('close', () =&amp;gt; {
    console.log(sol(input));
    process.exit();
  });&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;조건&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정점 V, 간선 E 개수가 주어진다.&amp;nbsp;(범위는 1 &amp;lt;= V &amp;lt;= 2만, &amp;nbsp;1 &amp;lt;= E &amp;lt;= 30만)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 모든 정점은 1~V까지의 번호가 매겨진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 간선 E는 연결+거리값을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;K는 출발할 정점의 번호다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방향그래프 정보는 &quot;u v w&quot;의 순서대로 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 정점 u에서 정점 v까지 거리가 w임을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- u와 v는 서로 다르며, w는 10 이하의 자연수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서로 다른 두 정점 사이에, 여러 간선이 존재할 수 있다.(예를 들면, &quot;1 2 3&quot;, &quot;1 2 4&quot; 가 주어질 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;K부터 시작해서 1~V번까지 각각 가장 빠른 최소거리를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;풀이방향을 생각해보면..&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 정점을 기준으로, 다른 정점으로 가는 최단거리를 구해야 하는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다익스트라 최단경로 알고리즘을 이용하면 될 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다익스트라에서는 3가지 배열이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 각 정점과 바로 연결된 노드의 정보를 담은 2차원 리스트. = 변수명 graphs&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. K 정점에서 1~V번까지의 최단거리를 담은 1차원 리스트. = 변수명 distance&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 각 정점의 방문여부를 조회할 1차원 리스트. = 변수명 visits&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 다익스트라대로 대충 시나리오를 짜보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. distance 배열에서 K 정점 -&amp;gt; K 정점으로의 거리를 0으로 갱신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. distance 최소거리인 current 정점(처음에는 K정점)과 연결된 graphs[current] 정점들을 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; current&amp;nbsp;정점의 visits 배열을 1(방문했음)으로 표시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 다음 next 정점까지가 최소거리(distance[current] +연결거리 &amp;lt; distance[next]) 라면 distance[next] 값을 갱신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 2~3번의 과정을 반복한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 병목이 될 구간은, 2번이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;distance 배열의 크기가 V(2만)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 방문하지않은 + 최소거리를 찾을 때 마다 O(N)이 소요된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 최소거리를 바로 가져올 수 있도록 Priority Queue(최소힙)를 만들어 쓰면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;visits 배열 원소가 모두 1이면 distance 배열이 모두 갱신되었을 것이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;distance 배열에서 Infinity값만 &quot;INF&quot;로 바꿔준 뒤 출력하면 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주석 포함 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1663840408613&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// PQ(inputValue.dist 기준으로 정렬되는 최소힙).
const MinHeap = (function () {
  function MinHeap() {
    this.heap = [-Infinity];
  }

  MinHeap.prototype.size = function () {
    return this.heap.length - 1;
  };

  MinHeap.prototype.push = function (val) {
    this.heap.push(val);
    this._upheap(this.size());
  };

  MinHeap.prototype._upheap = function (pos) {
    let pushed = this.heap[pos];
    let parentPos = Math.floor(pos / 2);

    while (pushed.dist &amp;lt; this.heap[parentPos].dist) {
      this.heap[pos] = this.heap[parentPos];
      pos = parentPos;
      parentPos = Math.floor(pos / 2);
    }

    this.heap[pos] = pushed;
  };

  MinHeap.prototype.pop = function () {
    if (this.size() === 1) return this.heap.pop();

    let popped = this.heap[1];
    this.heap[1] = this.heap.pop();
    this._downheap(1, this.size());
    return popped;
  };

  MinHeap.prototype._downheap = function (pos, lastPos) {
    const target = Math.floor(lastPos / 2);
    let lastVal = this.heap[pos];
    let childPos;

    while (pos &amp;lt;= target) {
      childPos = pos * 2;
      if (childPos &amp;lt; lastPos &amp;amp;&amp;amp; this.heap[childPos].dist &amp;gt; this.heap[childPos + 1].dist) childPos += 1;
      if (lastVal.dist &amp;lt;= this.heap[childPos].dist) break;
      this.heap[pos] = this.heap[childPos];
      pos = childPos;
    }
    this.heap[pos] = lastVal;
  };

  return MinHeap;
})();

function sol(input) {
  const [V, E] = input[0].split(' ').map(Number);
  const k = +input[1];
	
    // Edge의 정보들을 담은 2차원리스트
  const graphs = Array.from({ length: V + 1 }, () =&amp;gt; new Array());

  for (let i = 2, iter = input.length; i &amp;lt; iter; i++) {
  	// from-&amp;gt;to까지 w의 정보를 graphs에 넣어준다.
    const [from, to, w] = input[i].split(' ').map(Number);
    graphs[from].push({ node: to, dist: w });
  }

	// 방문 정보가 담긴 visits, 거리 정보가 담긴 distance
  const visits = new Array(V + 1).fill(0);
  const distance = new Array(V + 1).fill(Infinity);
  distance[k] = 0;

	// pq에서 현재탐색노드(K)부터 탐색을 시작할 예정. K-&amp;gt;K는 거리 0
  const pq = new MinHeap();
  pq.push({ node: k, dist: 0 });

  while (pq.size()) {
  	// pq에 더이상 노드가 없을때까지 탐색.
    const { node, dist } = pq.pop();

	// 방문(최소값 갱신)한 노드는 패스.
    if (visits[node]) continue;
    visits[node] = 1;

	// 방문했는데, 이 노드에서 갈 수 있는 다른 노드가 없다면 패스.
    const nexts = graphs[node];
    if (!nexts.length) continue;

    for (let i = 0, iter = nexts.length; i &amp;lt; iter; i++) {
      const { node: nextNode, dist: nextDist } = nexts[i];
		// 다음노드의 최소거리 &amp;gt; 현재노드최소거리+연결된거리 이면, 최소값 갱신 가능.
      if (distance[nextNode] &amp;gt; dist + nextDist) {
        distance[nextNode] = dist + nextDist;
        // 최소값이 갱신되었다면 정보를 pq에 넣어준다.
        // 왜?. 기존 pq에 3번노드가 4인 정보가 있다면, 갱신에 의해 3번노드거리가 2가 될수도.
        // 어차피 3번:2가 먼저 pq로부터 나올거고, 다음 3번:4는 visits 배열에 의해 필터링된다.
        pq.push({ node: nextNode, dist: distance[nextNode] });
      }
    }
  }

	// INF 필터링 후 출력코드.
  return distance
    .slice(1)
    .map((e) =&amp;gt; (e === Infinity ? 'INF' : e))
    .join('\n');
}


// 입력받기
const input = [];
require('readline')
  .createInterface(process.stdin, process.stdout)
  .on('line', (line) =&amp;gt; {
    input.push(line);
  })
  .on('close', () =&amp;gt; {
    console.log(sol(input));
    process.exit();
  });&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;마지막으로 코드가 난잡해서 조금 예쁘게? 함수단위로 묶어보았다.&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1663840449978&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// PQ
const MinHeap = (function () {
  function MinHeap() {
    this.heap = [-Infinity];
  }

  MinHeap.prototype.size = function () {
    return this.heap.length - 1;
  };

  MinHeap.prototype.push = function (val) {
    this.heap.push(val);
    this._upheap(this.size());
  };

  MinHeap.prototype._upheap = function (pos) {
    let pushed = this.heap[pos];
    let parentPos = Math.floor(pos / 2);

    while (pushed.dist &amp;lt; this.heap[parentPos].dist) {
      this.heap[pos] = this.heap[parentPos];
      pos = parentPos;
      parentPos = Math.floor(pos / 2);
    }

    this.heap[pos] = pushed;
  };

  MinHeap.prototype.pop = function () {
    if (this.size() === 1) return this.heap.pop();

    let popped = this.heap[1];
    this.heap[1] = this.heap.pop();
    this._downheap(1, this.size());
    return popped;
  };

  MinHeap.prototype._downheap = function (pos, lastPos) {
    const target = Math.floor(lastPos / 2);
    let lastVal = this.heap[pos];
    let childPos;

    while (pos &amp;lt;= target) {
      childPos = pos * 2;
      if (childPos &amp;lt; lastPos &amp;amp;&amp;amp; this.heap[childPos].dist &amp;gt; this.heap[childPos + 1].dist) childPos += 1;
      if (lastVal.dist &amp;lt;= this.heap[childPos].dist) break;
      this.heap[pos] = this.heap[childPos];
      pos = childPos;
    }
    this.heap[pos] = lastVal;
  };

  return MinHeap;
})();

// 다익스트라
function dijkstra(V, k, graphs) {
  const distance = new Array(V + 1).fill(Infinity);
  const visits = new Array(V + 1).fill(0);
  distance[k] = 0;

  const pq = new MinHeap();
  pq.push({ node: k, dist: 0 });

  while (pq.size()) {
    const { node, dist } = pq.pop();

    if (visits[node]) continue;
    visits[node] = 1;

    const nextNodes = graphs[node];
    if (!nextNodes.length) continue;

    for (let { node: nextNode, dist: nextDist } of nextNodes) {
      if (distance[nextNode] &amp;gt; dist + nextDist) {
        distance[nextNode] = dist + nextDist;
        pq.push({ node: nextNode, dist: distance[nextNode] });
      }
    }
  }

  return distance;
}

// 닿을수 없는 노드 필터링 함수
function filterDist(dist) {
  return dist === Infinity ? 'INF' : dist;
}


// 본격 실행해야 할 함수
function sol(input) {
  const [V, E] = input[0].split(' ').map(Number);
  const k = +input[1];

  const graphs = Array.from({ length: V + 1 }, () =&amp;gt; new Array());

  for (let inputStr of input.slice(2)) {
    const [from, to, w] = inputStr.split(' ').map(Number);
    graphs[from].push({ node: to, dist: w });
  }

  const distance = dijkstra(V, k, graphs);

  return distance.slice(1).map(filterDist).join('\n');
}

// 백준 입력받는 부분
const input = [];
require('readline')
  .createInterface(process.stdin, process.stdout)
  .on('line', (line) =&amp;gt; {
    input.push(line);
  })
  .on('close', () =&amp;gt; {
    console.log(sol(input));
    process.exit();
  });&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DS &amp;amp; Algorithm/baekjoon</category>
      <category>1753 최단경로 javascript</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/149</guid>
      <comments>https://gobae.tistory.com/149#entry149comment</comments>
      <pubDate>Thu, 22 Sep 2022 18:56:13 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 섬 연결하기 - 자바스크립트</title>
      <link>https://gobae.tistory.com/148</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1662986680124&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42861&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/enzh4U/hyPLhRaa6q/1g6k6YMV38GUjdwkAC2PC1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bemeGv/hyPLfMyrai/wkaAhV6ZDSBdmU4BDV54L1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42861&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42861&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/enzh4U/hyPLhRaa6q/1g6k6YMV38GUjdwkAC2PC1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bemeGv/hyPLfMyrai/wkaAhV6ZDSBdmU4BDV54L1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;섬과 섬을 잇는 비용이 주어지고, 모든 섬끼리 통행이 가능한 최소의 비용을 구하는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-12 오후 9.43.02.webp&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;1014&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AmDYp/btrLWmvXnex/jwkLVT0bk3is5nSgjt8mlk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AmDYp/btrLWmvXnex/jwkLVT0bk3is5nSgjt8mlk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AmDYp/btrLWmvXnex/jwkLVT0bk3is5nSgjt8mlk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAmDYp%2FbtrLWmvXnex%2FjwkLVT0bk3is5nSgjt8mlk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;1014&quot; data-filename=&quot;스크린샷 2022-09-12 오후 9.43.02.webp&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;1014&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1662986786114&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(n, costs) {
  let answer = 0;
  const length = costs.length;
  const parent = Array.from({ length }, (_, i) =&amp;gt; i);

  costs.sort((a, b) =&amp;gt; a[2] - b[2]);

  for (let i = 0; i &amp;lt; length; i++) {
    const [from, to, cost] = costs[i];
    if (findParent(parent, from) !== findParent(parent, to)) {
      unionParent(parent, from, to);
      answer += cost;
    }
  }

  return answer;
}

function findParent(parent, x) {
  if (parent[x] !== x) {
    parent[x] = findParent(parent, parent[x]);
  }
  return parent[x];
}

function unionParent(parent, x, y) {
  x = findParent(parent, x);
  y = findParent(parent, y);

  if (x &amp;lt; y) parent[y] = x;
  else parent[x] = y;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제의 조건&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 &amp;lt;= n &amp;lt;= 100, n=섬의 갯수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;costs 길이 &amp;lt;= ((n-1)*n)/2 &amp;lt;= 4950&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;costs[i]에는 [a, b, c]의 배열이 들어있으며, a섬에서 b섬까지의 비용 c를 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 연결은 주어지지 않으며 순서가 바뀌더라도 같은 연결로 본다. ( a섬에서 b섬으로의 비용c는 b섬에서 a섬으로의 비용c와 같다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 섬 사이의 다리 비용이 주어지지는 않으며, 연결할 수 없는 섬은 주어지지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 주어진 입출력을 바탕으로 시뮬레이션하면, 아래 그림의 순서를 거친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0022.jpg&quot; data-origin-width=&quot;1657&quot; data-origin-height=&quot;2326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U9IyF/btrLQnCszET/dEWX1pv1LgfIN9P9R6aOf1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U9IyF/btrLQnCszET/dEWX1pv1LgfIN9P9R6aOf1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U9IyF/btrLQnCszET/dEWX1pv1LgfIN9P9R6aOf1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU9IyF%2FbtrLQnCszET%2FdEWX1pv1LgfIN9P9R6aOf1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;983&quot; data-filename=&quot;IMG_0022.jpg&quot; data-origin-width=&quot;1657&quot; data-origin-height=&quot;2326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 배열에서 parent 0에 모두 연결된다면, 모든 다리가 연결된 것이므로 더이상 다리를 건설할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;최소&quot;의 비용을 구해야하므로, 우선 costs 배열을 비용 오름차순 정렬해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 가장 저렴한 건설비용 순으로 다리건설여부를 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;건설여부 판단을 위해서는 a섬과 b섬의 연결여부를 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a섬과 b섬이 직접적으로 연결되지 않았더라도, c섬이나 d섬을 경유하여 도착한다면 연결된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 각 섬의 연결여부를 판단할 수 있는 배열이 필요하며, &lt;b&gt;parent&lt;/b&gt; 배열로 삼았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parent 배열의 각 인덱스는 각 섬을 의미하며, 처음에는 자신을 원소로 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0번섬의 parent = 0번, 1번섬의 parent = 1번&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;섬이 연결될 때 마다, 더 크거나/작은 섬으로 parent 인덱스의 원소를 갱신해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0번과 1번이 연결되었다면, 1번의 부모를 0으로 바꿔주거나, 0의 부모를 1로 바꾸면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0번과 1번 섬의 부모가 같다면, 이미 연결된 섬이므로 해당 다리는 건설할 필요가 없으니 넘어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 모든 다리 건설 비용을 순회하면서, a섬 -&amp;gt; b섬의 연결을 확인하고, 다리건설을 하거나 하지 않으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 연결할 수 없는 섬은 주어지지 않으므로, 다리 건설 비용을 모두 확인한 것 만으로도 모든 섬은 연결되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘의 유형은 크루스칼이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크루스칼, 최소신장트리, &amp;nbsp;사이클이 존재하지 않는 그래프 등을 공부하면 되겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주석을 포함한 코드&lt;/h3&gt;
&lt;pre id=&quot;code_1662988391195&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(n, costs) {
  let answer = 0;
  const length = costs.length;
  const parent = Array.from({ length }, (_, i) =&amp;gt; i);
  // 처음 parent 배열의 인덱스는 자신을 원소로 가진다.

  costs.sort((a, b) =&amp;gt; a[2] - b[2]);
  // 최소비용을 구해야 하므로, 비용기준 오름차순 정렬한다.

  for (let i = 0; i &amp;lt; length; i++) {
    const [from, to, cost] = costs[i];
    if (findParent(parent, from) !== findParent(parent, to)) {
      // from, to 섬의 부모를 찾는다. 부모가 다르다는 것은, 서로 연결되지 않았다는 의미다.

	  unionParent(parent, from, to);
      answer += cost;
      // 부모가 연결되지 않았다면 다리를 건설한다. 부모를 연결해주고 다리 건설 비용을 더한다.
    }
  }

  return answer;
}

function findParent(parent, x) {
  if (parent[x] !== x) {
  	// x 인덱스의 부모가 x가 아니면, 다른 부모가 있는 것이다. 그 부모를 찾는다.
    // 결국 x 인덱스의 부모가 x 자신일 때, 재귀를 멈추게 된다.
    parent[x] = findParent(parent, parent[x]);
  }
  return parent[x];
}


function unionParent(parent, x, y) {
  // x, y 각각의 최종 부모를 찾는다.
  // 여기서는 낮은 인덱스를 부모로 삼을 예정이므로 x 부모가 더 작다면 y의 부모를 갱신하고
  // y 부모가 더 작다면 x의 부모를 갱신한다.
  x = findParent(parent, x);
  y = findParent(parent, y);
  
  if (x &amp;lt; y) parent[y] = x;
  else parent[x] = y;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/programmers</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/148</guid>
      <comments>https://gobae.tistory.com/148#entry148comment</comments>
      <pubDate>Mon, 12 Sep 2022 22:15:02 +0900</pubDate>
    </item>
    <item>
      <title>[vanillaJS] 채팅창 컴포넌트의 스크롤을 다뤄보자.</title>
      <link>https://gobae.tistory.com/147</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;채팅창 문제의 시작점&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1756&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/870xr/btrKZmYjA5n/QbnBtdcsdvFOQwRfL3ZoW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/870xr/btrKZmYjA5n/QbnBtdcsdvFOQwRfL3ZoW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/870xr/btrKZmYjA5n/QbnBtdcsdvFOQwRfL3ZoW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F870xr%2FbtrKZmYjA5n%2FQbnBtdcsdvFOQwRfL3ZoW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;210&quot; data-origin-width=&quot;1756&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 채팅 컴포넌트와 관련된 코드 중 일부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chats 이라는 state가 업데이트 될 때 마다, 새로운 채팅요소를 생성하게 될 것인데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채팅방 엘리먼트에 &lt;u&gt;&lt;b&gt;채팅들이 넘치기 시작하면&lt;/b&gt;&lt;/u&gt; 문제가 발생한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 발생한 프로젝트는 리액트였지만, &lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;바닐라&lt;/b&gt;&lt;/span&gt;로 차근차근 정복해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바닐라를 이해하면 리액트는 effect, ref 정도만 고려해서 그대로 적용하면 된다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTML을 통한 문제 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;// index.html

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
        body{
            display:flex;
            flex-direction: column;
            justify-content: center;
            align-items:center;
        }
        #chatBox{
            width:250px;
            height:800px;
            background-color:burlywood;
            display:flex;
            flex-direction: column;
            overflow:auto;
        }
        .chat{
            display:flex;
            justify-content: center;
            align-items:center;
            background-color: beige;
            width:200px;
            min-height:100px;
            height:100px;
            border:1px solid black;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;chatBox&quot;&quot;&amp;gt;
        &amp;lt;div class=&quot;chat&quot;&amp;gt;chat 0&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;button id=&quot;add&quot;&amp;gt;채팅추가&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
    
    &amp;lt;script&amp;gt;
        const chatBox = document.querySelector('#chatBox');
        let chatIdx = 1;

        document.querySelector('#add').addEventListener('click',()=&amp;gt;{
            const div = document.createElement('div');
            div.className='chat';
            div.innerHTML = `chat ${chatIdx++}`;
            chatBox.appendChild(div);
        })
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 내용은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;chatBox에 채팅들이 담긴다.&lt;/li&gt;
&lt;li&gt;add button을 누를 때 마다, chatBox에 채팅이 하나씩 추가된다.&lt;/li&gt;
&lt;li&gt;css overflow 속성에 의해 chatBox 크기 이상으로 채팅들이 담겨 넘치면, 자동으로 스크롤이 생성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과를 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;chatting.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;741&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwFlA1/btrKUcQcTJF/hzFX22RfKrpQd5FGjmvKtK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwFlA1/btrKUcQcTJF/hzFX22RfKrpQd5FGjmvKtK/img.gif&quot; data-alt=&quot;채팅추가버튼을 열심히 눌러서 채팅을 생성중입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwFlA1/btrKUcQcTJF/hzFX22RfKrpQd5FGjmvKtK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bwFlA1/btrKUcQcTJF/hzFX22RfKrpQd5FGjmvKtK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;556&quot; data-filename=&quot;chatting.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;741&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;채팅추가버튼을 열심히 눌러서 채팅을 생성중입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 누를 때 마다 채팅이 추가되고 있는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 화면에서는 스크롤만 열심히 춤출 뿐, 추가된 채팅은 직접 스크롤 다운해야 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;분명 뭔가 문제있는 상황이긴 하다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 이것만 해결하고 넘어가지 말고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채팅방에서 사용자 편의를 위해 어떤 기능들이 있으면 좋을지 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 기본적으로는 항상 새로운 채팅을 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 유저가 스크롤을 조절하여 특정 채팅을 보고 있다면, 새로운 채팅을 추가하지만 스크롤은 건드리지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;- 유저가 특정 채팅을 보고 난 뒤 스크롤을 최하위로 내렸다면, 다시 1번 동작을 수행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 스크롤 위치와 관계 없이, 유저 자신이 채팅을 보냈다면 새로운 채팅을 보여주도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이정도를 구현해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 요소의 스크롤 값 속성에 어떻게 접근하면 좋을지 학습할 시간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇다.. 이제는 스크롤 요소에 대해 공부해야 할 시간.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;혹시 어떻게 해결했는지가 궁금하다면 이 챕터는 건너뛰면 되겠다.&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;스크롤에는 수평/수직 스크롤 2가지가 있으며, 수직 스크롤(상하 스크롤) 위주로 다룰 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;1236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z7hs3/btrKY3dn5UR/gYyTGP9VsTCy0dvWW5a1SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z7hs3/btrKY3dn5UR/gYyTGP9VsTCy0dvWW5a1SK/img.png&quot; data-alt=&quot;geometry property&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z7hs3/btrKY3dn5UR/gYyTGP9VsTCy0dvWW5a1SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ7hs3%2FbtrKY3dn5UR%2FgYyTGP9VsTCy0dvWW5a1SK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;429&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;1236&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;geometry property&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결할 문제에 대한 모든 해결책이 담긴 그림이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clientHeight, scrollTop, scrollHeight 값을 중심으로 다룰 예정이니,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 위치 값이 궁금하다면 아래 링크를 확인하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/size-and-scroll&quot;&gt;https://ko.javascript.info/size-and-scroll&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;elem.clientHeight&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;974&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddtmGn/btrKUSqfUJq/6QEILUKjJhoXSqi0ENJmS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddtmGn/btrKUSqfUJq/6QEILUKjJhoXSqi0ENJmS0/img.png&quot; data-alt=&quot;clientHeight&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddtmGn/btrKUSqfUJq/6QEILUKjJhoXSqi0ENJmS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddtmGn%2FbtrKUSqfUJq%2F6QEILUKjJhoXSqi0ENJmS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;429&quot; height=&quot;408&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;974&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;clientHeight&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소의 border를 기준으로, border의 내부 컨텐츠의 높이를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;border의 내부는 padding + content-box 이므로,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clientHeight = padding-top + padding-bottom + content-box width이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;elem.scrollHeight&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2272&quot; data-origin-height=&quot;1064&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FGhoi/btrKZ1lMDbY/4Eu9KzU0XOTivULBig8Cm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FGhoi/btrKZ1lMDbY/4Eu9KzU0XOTivULBig8Cm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FGhoi/btrKZ1lMDbY/4Eu9KzU0XOTivULBig8Cm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFGhoi%2FbtrKZ1lMDbY%2F4Eu9KzU0XOTivULBig8Cm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;1064&quot; data-origin-width=&quot;2272&quot; data-origin-height=&quot;1064&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clientHeight와 같이 border 내부의 컨텐츠 크기를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, scrollHeight은&amp;nbsp;&lt;b&gt;감춰진 영역을 포함한 요소의 전체 크기&lt;/b&gt;를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2226&quot; data-origin-height=&quot;1482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/riEqJ/btrKVWeWvnr/pU3t0QEiIkZvFCcY7yphH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/riEqJ/btrKVWeWvnr/pU3t0QEiIkZvFCcY7yphH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/riEqJ/btrKVWeWvnr/pU3t0QEiIkZvFCcY7yphH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FriEqJ%2FbtrKVWeWvnr%2FpU3t0QEiIkZvFCcY7yphH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;533&quot; data-origin-width=&quot;2226&quot; data-origin-height=&quot;1482&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 높이가 처음 설정한 800px를 넘어가면, scrollHeight 값은 증가하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 clientHeight 값은 설정한 높이 800px를 유지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;elem.scrollTop&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2228&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kwEUP/btrKVxsLncv/NntnmQ3qRPiKutd0rpnK4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kwEUP/btrKVxsLncv/NntnmQ3qRPiKutd0rpnK4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kwEUP/btrKVxsLncv/NntnmQ3qRPiKutd0rpnK4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkwEUP%2FbtrKVxsLncv%2FNntnmQ3qRPiKutd0rpnK4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;760&quot; data-origin-width=&quot;2228&quot; data-origin-height=&quot;760&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scrollTop은 &lt;b&gt;스크롤에 의해&lt;span&gt;&amp;nbsp;숨겨진&lt;/span&gt;&amp;nbsp;윗 부분 영역의 높이&lt;/b&gt;를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 scrollTop과 scrollLeft 속성은 스크롤 속성중에서 읽기전용이 아닌 &lt;b&gt;&lt;u&gt;쓰기도 가능&lt;/u&gt;한 프로퍼티&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 동작하는 스크롤이 scrollTop 값을 기반으로 위치하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;그래서 스크롤의 위치를 건드리려면 scrollTop / scrollLeft을 이용하면 되겠다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;준비운동은 이정도로 마치고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 앞서 정의한 기능들을 위해, 이 속성들을 어떻게 이용하면 좋을지 생각해볼 시간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;채팅방 스크롤 기능을 구현해보자.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 정의한 기능 목록이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 기본적으로는 항상 새로운 채팅을 보여준다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 유저가 스크롤을 조절하여 특정 채팅을 보고 있다면, 새로운 채팅을 추가하지만 스크롤은 건드리지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;- 유저가 특정 채팅을 보고 난 뒤 스크롤을 최하위로 내렸다면, 다시 1번 동작을 수행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 스크롤 위치와 관계 없이, 유저 자신이 채팅을 보냈다면 새로운 채팅을 보여주도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 기본적으로는 항상 새로운 채팅을 보여준다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;966&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pVkcK/btrKZf5VJQi/jm0ka0nks50KSzKKnKo9nK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pVkcK/btrKZf5VJQi/jm0ka0nks50KSzKKnKo9nK/img.png&quot; data-alt=&quot;항상 scrollTop의 크기를 최대한 키워주면, 최하단 채팅이 보이게 된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pVkcK/btrKZf5VJQi/jm0ka0nks50KSzKKnKo9nK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpVkcK%2FbtrKZf5VJQi%2Fjm0ka0nks50KSzKKnKo9nK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;421&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;966&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;항상 scrollTop의 크기를 최대한 키워주면, 최하단 채팅이 보이게 된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;가장 간단한 방법으로는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;새로운 채팅이 추가될 때 마다, elem.scrollTop 값을 키워준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면, 새로운 채팅 렌더링마다 scrollTop = 1e9 와 같은 큰 값을 설정할 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시코드)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1661852541363&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
        body{
            display:flex;
            flex-direction: column;
            justify-content: center;
            align-items:center;
        }
        #chatBox{
            width:250px;
            height:800px;
            background-color:burlywood;
            display:flex;
            flex-direction: column;
            overflow:auto;
        }
        .chat{
            display:flex;
            justify-content: center;
            align-items:center;
            background-color: beige;
            width:200px;
            min-height:100px;
            height:100px;
            border:1px solid black;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;chatBox&quot;&quot;&amp;gt;
        &amp;lt;div class=&quot;chat&quot;&amp;gt;chat 0&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;button id=&quot;add&quot;&amp;gt;채팅추가&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;script&amp;gt;
        const chatBox = document.querySelector('#chatBox');
        let chatIdx = 1;

        document.querySelector('#add').addEventListener('click',()=&amp;gt;{
            const div = document.createElement('div');
            div.className='chat';
            div.innerHTML = `chat ${chatIdx++}`;
            chatBox.appendChild(div);

            chatBox.scrollTop = 1e9;
        })
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 유저가 스크롤을 조절하여 특정 채팅을 보고 있다면, 새로운 채팅을 추가하지만 스크롤은 건드리지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 기능에 의하면, 새로운 채팅이 추가되면 항상 스크롤이 내려가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 사용자는 이미 지나가버린 채팅을 보기가 쉽지 않다.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 개선해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kPxWk/btrKUipkLBr/1G549K7oiXEld5IcOkkaZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kPxWk/btrKUipkLBr/1G549K7oiXEld5IcOkkaZ1/img.png&quot; data-alt=&quot;사용자가 특정 채팅을 보고 있는 예시 그림.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kPxWk/btrKUipkLBr/1G549K7oiXEld5IcOkkaZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkPxWk%2FbtrKUipkLBr%2F1G549K7oiXEld5IcOkkaZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;972&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;972&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사용자가 특정 채팅을 보고 있는 예시 그림.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 스크롤을 조절하여 특정 채팅을 있는 상황에서는,&amp;nbsp;1번의 상황보다&amp;nbsp;scrollTop 값이 작아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 사용자가 특정 채팅을 보면&amp;nbsp;&lt;b&gt;scrollTop + clientHeight &amp;lt; scrollHeight&amp;nbsp;&lt;/b&gt;를 만족해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 스크롤 가능 변수를 도입하고,&amp;nbsp;요소에서 scroll 이벤트를 감지해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 채팅을 보는 상황에서는 스크롤 가능 변수를 비활성화하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 채팅을 보고 있지 않은 상황이라면 스크롤 가능 변수를 활성화해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1661853116670&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
        body{
            display:flex;
            flex-direction: column;
            justify-content: center;
            align-items:center;
        }
        #chatBox{
            width:250px;
            height:800px;
            background-color:burlywood;
            display:flex;
            flex-direction: column;
            overflow:auto;
        }
        .chat{
            display:flex;
            justify-content: center;
            align-items:center;
            background-color: beige;
            width:200px;
            min-height:100px;
            height:100px;
            border:1px solid black;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;chatBox&quot;&quot;&amp;gt;
        &amp;lt;div class=&quot;chat&quot;&amp;gt;chat 0&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;button id=&quot;add&quot;&amp;gt;채팅추가&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;script&amp;gt;
        const chatBox = document.querySelector('#chatBox');
        let chatIdx = 1;
        let isScrollable = true;

        document.querySelector('#add').addEventListener('click',()=&amp;gt;{
            const div = document.createElement('div');
            div.className='chat';
            div.innerHTML = `chat ${chatIdx++}`;
            chatBox.appendChild(div);

            if(isScrollable) chatBox.scrollTop = 1e9;
        })

        chatBox.addEventListener('scroll', (e)=&amp;gt;{
            const {scrollHeight, scrollTop, clientHeight} = e.target;

            if(clientHeight + scrollTop &amp;lt; scrollHeight){
                isScrollable = false;
                return;
            }
            isScrollable = true;
        })
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;scrolling.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDXbp1/btrKZnCQGYj/vlBEUtLOk4TiSDoubwpp40/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDXbp1/btrKZnCQGYj/vlBEUtLOk4TiSDoubwpp40/img.gif&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDXbp1/btrKZnCQGYj/vlBEUtLOk4TiSDoubwpp40/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cDXbp1/btrKZnCQGYj/vlBEUtLOk4TiSDoubwpp40/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;510&quot; height=&quot;482&quot; data-filename=&quot;scrolling.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 스크롤 위치와 관계 없이, 유저 자신이 채팅을 보냈다면 새로운 채팅을 보여주도록 하자.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저 자신이 채팅을 보내면,&amp;nbsp;isScrollable 변수를 활성화시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 1번 기능에 의해, 자동으로 요구사항을 만족하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 코드는 생략&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예시로만 구성해서 바로 적용하기에 무리가 있을수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분 프로젝트에 다른 외부변수들이 있다면, 이것도 열심히 고민해봅시다!. ㅎㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그래서 리액트에는 어떻게 적용하는데..?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 2번기능까지 이해하셨다면, 방향이 보이겠지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이대로 끝내기 아쉬워서 리액트에서 어떻게 적용할지에 대해서도 간단하게 적어보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;채팅방 요소에 ref를 달아둔다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useEffect의 의존성 배열에 채팅 상태(ex. chats)을 추가하고, 1번 기능을 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 ref 변수 하나를 추가한다.(=이건 jsx에 부착하지 않고, isScrollable로 사용할 것)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채팅방 요소의 jsx에 onScroll 이벤트리스너를 달아주고, 2번 기능을 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번 기능에 대해서는, 내가 보낸 채팅은 바로 isScrollabe=true로 바꿔주고 setChats를 실행하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 코드로는 대충 이렇지 않을까?&lt;/p&gt;
&lt;pre id=&quot;code_1661854116010&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// react
// ChattingRoom.jsx

function ChattingRoom(){
	const [myChat, setMyChat] = useState('');
	const [chats, setChats] = useState([]);
	const chatContainerRef = useRef(null);
    const isScrollable = useRef(true);
    
    function onChange(e){
    	setMyChat(e.target.value);
    }
	
    function onKeyPress(e){
    	if(// 누른 키가 enter라면? ){
        	const newChat = {id:chats.length + 1, text: myChat}
        	setchats(prevChats =&amp;gt; {
            	return [...prevChats, newChat];
            });
            setMyChat('');
			
            isScrollable.current=true;
        }
    }
    
    function onScroll(e){
        const {scrollHeight, scrollTop, clientHeight} = e.target;

        if(clientHeight + scrollTop &amp;lt; scrollHeight){
            isScrollable.current = false;
            return;
        }
        isScrollable.current = true;
    }
    
    useEffect(()=&amp;gt;{
		if(isScrollable.current){
            chatContainerRef.current.scrollTop = 1e9;
        }
    }, [chats]);
    
    return (
    	&amp;lt;ChatContainer ref={chatContainerRef} onScroll={onScroll}&amp;gt;
        	{chats.map(chat =&amp;gt; &amp;lt;Chat key={chat.id}&amp;gt;{chat.text}&amp;lt;/Chat&amp;gt;)}
            &amp;lt;input type='text' placeholder=&quot;채팅입력&quot; value={myChat} onChange={onChange} onKeypress={onKeyPress}/&amp;gt;
        &amp;lt;/ChatContainer&amp;gt;
    )

}


// Chat.jsx

export default React.memo(function Chat(){...});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 실행해보진 않아서, 의도한대로 동작하진 않을 수 있다. 아무튼 이런 느낌이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막에는 React.memo를 적용하기에 좋은 사례이기에 작게 추가해뒀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘은 기존의 프로젝트를 복습할 겸, 다시 재구성하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간 채팅 기능을 구현하다가, 여차저차해서 문제를 해결을 하게 된 기념으로..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나와 비슷한 상황을 접한 분들에게 도움이 되었으면 좋겠어서 정리하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅 내용은 학습한 것을 기반으로 구현한 기능이라, 좀 더 효율적인 방법이 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(아마 있을 가능성이 크다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에 스크롤 이벤트나 채팅 렌더링에 대한 최적화라던가..는 또 열심히 고민해봅시다. ㅎㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고한 자료&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/size-and-scroll&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.javascript.info/size-and-scroll&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/vanilla</category>
      <category>자바스크립트 채팅방 스크롤</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/147</guid>
      <comments>https://gobae.tistory.com/147#entry147comment</comments>
      <pubDate>Tue, 30 Aug 2022 19:32:36 +0900</pubDate>
    </item>
    <item>
      <title>[CSS] box-sizing : border-box;</title>
      <link>https://gobae.tistory.com/146</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;box-sizing : border-box!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소의 크기가 원하는대로 보이지 않을 때 사용하는 CSS 프로퍼티다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 뭔가 치트키스럽다는 생각을 가지고 있었다 ㅎㅎ..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 그냥 박스모델 개념을 알면 쉽게 풀린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아주 짧게 박스모델과 관련하여 box-sizing 속성에 대해 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;박스 모델&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1336&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BsJJX/btrKWBu3gGf/CLVKAKdYrm3YgOxEat7w1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BsJJX/btrKWBu3gGf/CLVKAKdYrm3YgOxEat7w1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BsJJX/btrKWBu3gGf/CLVKAKdYrm3YgOxEat7w1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBsJJX%2FbtrKWBu3gGf%2FCLVKAKdYrm3YgOxEat7w1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;506&quot; data-origin-width=&quot;1336&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발자가 크롬에서 자주 만나는 사각형이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;box model은 4가지 부분으로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;margin, border, padding, content box!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;margin은 생략하고, border, padding, content box를 중심으로 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 만들 요소의 크기가 border+pading+content box로 정해지기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, CSS box-sizing 속성의 기본값은 content-box이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;content-box&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;content-box 일 때는 지정한 width 값이 content box의 크기를 의미하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크기 400px짜리 요소를 만드려는 의도로, {width:400px;}를 적었다고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1661848817396&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;style&amp;gt;
      div {
        width: 400px;
        height: 200px;
        margin: 10px;
        padding: 20px;
        border: 1px solid black;
      }
      #content-box {
        background-color: beige;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;content-box&quot;&amp;gt;Content Box&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;1190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3srHY/btrKU7nn7Ao/whUcuFaYkyi7KuyONQjOJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3srHY/btrKU7nn7Ao/whUcuFaYkyi7KuyONQjOJK/img.png&quot; data-alt=&quot;box-sizing:content-box (기본값)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3srHY/btrKU7nn7Ao/whUcuFaYkyi7KuyONQjOJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3srHY%2FbtrKU7nn7Ao%2FwhUcuFaYkyi7KuyONQjOJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;367&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;1190&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;box-sizing:content-box (기본값)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 의도와는 달리&amp;nbsp;width:400px가 content-box의 크기이기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- border : 1px&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- padding : 20px&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- content box width : 400px&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;442px짜리 박스&lt;/span&gt;&lt;/b&gt;가 만들어지는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;border-box&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;box-sizing: border-box로 지정해주면,&amp;nbsp;박스 크기를 설정할 때 border 영역까지 포함하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1661849156089&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;style&amp;gt;
      div {
        width: 400px;
        height: 200px;
        margin: 10px;
        padding: 20px;
        border: 1px solid black;
      }
      #content-box {
        background-color: beige;
      }
      #border-box {
        background-color: coral;
        box-sizing: border-box;
      }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;content-box&quot;&amp;gt;Content Box&amp;lt;/div&amp;gt;
    &amp;lt;div id=&quot;border-box&quot;&amp;gt;Border Box&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;1208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ8dbq/btrKU8s6fsQ/RkDOPki7ki3zLmHjKKVmsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ8dbq/btrKU8s6fsQ/RkDOPki7ki3zLmHjKKVmsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ8dbq/btrKU8s6fsQ/RkDOPki7ki3zLmHjKKVmsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ8dbq%2FbtrKU8s6fsQ%2FRkDOPki7ki3zLmHjKKVmsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;373&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;1208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 의도했던 {width:400px;}가 전체 박스에 적용된 결과로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- border : 1px&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- padding : 20px&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- content box : 400px - 1px - 1px - 20px - 20px = 358px&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;400px짜리 박스&lt;/b&gt;&lt;/span&gt;가 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;요약&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소에 적용한 width 프로퍼티는 box-sizing 옵션에 따라 다음과 같이 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;content-box : border + padding + width(= content-box)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;border-box : width(= border + padding + content-box)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>HTML &amp;amp; CSS</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/146</guid>
      <comments>https://gobae.tistory.com/146#entry146comment</comments>
      <pubDate>Tue, 30 Aug 2022 17:51:46 +0900</pubDate>
    </item>
    <item>
      <title>비동기 작업 취소하기. &amp;quot;AbortController&amp;quot;</title>
      <link>https://gobae.tistory.com/145</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-07-24 오후 8.23.02.webp&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rm7hp/btrH1BER3e0/ntKKzTlxi1uWbpHuDXWqJK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rm7hp/btrH1BER3e0/ntKKzTlxi1uWbpHuDXWqJK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rm7hp/btrH1BER3e0/ntKKzTlxi1uWbpHuDXWqJK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frm7hp%2FbtrH1BER3e0%2FntKKzTlxi1uWbpHuDXWqJK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;185&quot; data-filename=&quot;스크린샷 2022-07-24 오후 8.23.02.webp&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 과제전형 테스트에서 &quot;비동기 요청 *초 전까지 응답이 오지 않는다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 취소 후 재전송&quot;이라는 요구사항이 있었는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후&lt;s&gt;(시원하게 말아먹고 난 이후)&lt;/s&gt; AbortController의 존재에 대해서 알게 되었다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 간단하게 비동기 요청시 사용되는 fetch API의 특징을 간단하게 알아보고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포스팅의 주 대상 AbortController를 공부해보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;fetch : return Promise&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch API의 결과는 Promise이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답이 오기 전 까지 pending(대기) 상태를 유지하게 되며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pending 상태가 끝나면 fullfilled 혹은 rejected로 바뀌게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch 자체만으로는 pending을 취소하는 방법이 따로 존재하지 않으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 pending 상태는 &quot;요청하고 응답을 기다리는중&quot; 이므로, 어쩌면 응답을 &lt;s&gt;무한정 기다려야 될 수도&lt;/s&gt; 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AbortController?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Abort = (도중에) 중단시키다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 요청 취소를 지원하는 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 키워드와 함께 호출하여 생성자로 AbortContoller를 생성하여, 비동기 작업시 이용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알아야 할 프로퍼티는 2가지다. signal, abort!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AbortController.signal&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;signal은 읽기전용 프로퍼티로, fetch 요청 전송시 옵션으로 함께 넣어주게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch api에서 signal 옵션으로 넣어줌으로서, XHR 객체(요청)와 생성한 AbortController 인스턴스가 통신할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AbortController.abort()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abort 메서드를 호출하면 요청 완료전에 취소할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abort 메서드가 signal 객체의 내부 상태를 바꿔준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하면 AbortController 인스턴스를 생성하고, signal로 신호,컨트롤러와 요청간 연결 후 abort 메서드로 취소할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b40FoA/btrH75R1pRC/v2BZxmfWm2mbtl6DQd6Gx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b40FoA/btrH75R1pRC/v2BZxmfWm2mbtl6DQd6Gx1/img.png&quot; data-alt=&quot;abort()에 의해 signal 객체의 상태가 바뀌게 된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b40FoA/btrH75R1pRC/v2BZxmfWm2mbtl6DQd6Gx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb40FoA%2FbtrH75R1pRC%2Fv2BZxmfWm2mbtl6DQd6Gx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;340&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;abort()에 의해 signal 객체의 상태가 바뀌게 된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;pre id=&quot;code_1658659402196&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app.get('/abort', (req, res) =&amp;gt; {
  const targetNumber = Math.random();
  let randomNumber = Math.random();
  let cntOfFailure = 0;

  while (targetNumber !== randomNumber) {
    if (cntOfFailure &amp;gt; 1e8 * 5) res.send('fail');

    randomNumber = Math.random();
    cntOfFailure++;
  }

  res.send('success');
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 5억번의 기회 안에 랜덤 소수를 맞추면 &quot;success&quot; 응답을 주는 endpoint를 만들어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/do8SDP/btrH5e20o8z/naTQdQWc2H0KIFuCLknDJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/do8SDP/btrH5e20o8z/naTQdQWc2H0KIFuCLknDJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/do8SDP/btrH5e20o8z/naTQdQWc2H0KIFuCLknDJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdo8SDP%2FbtrH5e20o8z%2FnaTQdQWc2H0KIFuCLknDJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;260&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어차피 못맞출거다. &lt;s&gt;혹시 &quot;success&quot;를 받았다면 무척 운이 좋은 것이니, 당장 로또사러..&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 해당 요청은 최소 1번에서 최대 5억번의 while문이 돌아가야 하기 때문에 &quot;꽤 오래걸릴 예정이다.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1658659519522&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;button onClick=&quot;request()&quot;&amp;gt;요청하기&amp;lt;/button&amp;gt;
    &amp;lt;button onClick=&quot;cancel()&quot;&amp;gt;요청 취소하기&amp;lt;/button&amp;gt;
    &amp;lt;p id=&quot;status&quot;&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;script&amp;gt;
      const status = document.querySelector('#status');
      let abort = new AbortController();

      async function request() {
        try {
          status.innerHTML = '요청중....';
          const res = await fetch('--요청이 오래걸릴 수 있는 url--', {
            signal: abort.signal,
          });
          status.innerHTML = `요청에 대한 응답이 도착하였습니다!!.`;
        } catch (err) {
          status.innerHTML = `에러 로그 : ${err}`;
        }
      }

      function cancel() {
        status.innerHTML = '요청 취소!';
        abort.abort();
      }
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 간단하게 html파일로 테스트 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 요청하기 버튼을 누르면 &quot;요청중...&quot; 메시지를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- fetch option에 signal을 포함하여 비동기요청을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 응답이 오면 &quot;요청에 대한 응답이 도착하였습니다!!&quot; 메시지를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 응답이 오기 전에 요청 취소하기 버튼을 누르면, &quot;요청 취소!&quot; 메시지를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 에러 발생시에는 &quot;에러로그 : error message&quot; 메시지를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Riwgx/btrH0Sf17GF/TFc6XDnEX7Lj95veinmjG0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Riwgx/btrH0Sf17GF/TFc6XDnEX7Lj95veinmjG0/img.gif&quot; data-alt=&quot;요청 후 응답을 기다리는 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Riwgx/btrH0Sf17GF/TFc6XDnEX7Lj95veinmjG0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Riwgx/btrH0Sf17GF/TFc6XDnEX7Lj95veinmjG0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;176&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요청 후 응답을 기다리는 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rpZxj/btrH2gHfFT5/R4mUoJt9ldyaOlpy1XR63k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rpZxj/btrH2gHfFT5/R4mUoJt9ldyaOlpy1XR63k/img.gif&quot; data-alt=&quot;요청 후 abort() 메서드로 취소한 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rpZxj/btrH2gHfFT5/R4mUoJt9ldyaOlpy1XR63k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/rpZxj/btrH2gHfFT5/R4mUoJt9ldyaOlpy1XR63k/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;176&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요청 후 abort() 메서드로 취소한 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청이 잘 동작하는 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1658660187762&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abort.signal.addEventListener('abort', () =&amp;gt; alert('취소'));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;signal 객체에는 이벤트리스너도 달아줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;abort&quot; 이벤트 발생시, 콜백을 실행하게 되고,&amp;nbsp;alert을 띄워주도록 동작시켜봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LoENe/btrH7QmPPv0/tNu477JRe2oFlyFdDdlE21/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LoENe/btrH7QmPPv0/tNu477JRe2oFlyFdDdlE21/img.gif&quot; data-alt=&quot;signal의 이벤트리스너 콜백이 잘 동작한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LoENe/btrH7QmPPv0/tNu477JRe2oFlyFdDdlE21/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/LoENe/btrH7QmPPv0/tNu477JRe2oFlyFdDdlE21/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;166&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;signal의 이벤트리스너 콜백이 잘 동작한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 너무 오래 기다리다가 결국 취소한 요청을 다시 전송하고 싶을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우를 다뤄보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요청 취소 후 재요청하기.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 취소 후에 &quot;요청하기&quot;버튼을 다시 누르면 요청을 수행할 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;129&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxd5gV/btrH9HDmckq/pAnU5oUPKdVPqYlR0aRAK0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxd5gV/btrH9HDmckq/pAnU5oUPKdVPqYlR0aRAK0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxd5gV/btrH9HDmckq/pAnU5oUPKdVPqYlR0aRAK0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cxd5gV/btrH9HDmckq/pAnU5oUPKdVPqYlR0aRAK0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;129&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;129&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 생각과는 다르게, 다시 요청이 전송되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;AbortError: Failed to execute 'fetch' on 'Window': The user aborted a request.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;AbortController.signal.aborted 값이 true이기 때문이다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그럼 AbortController를 재생성해주면 되겠다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1658661179486&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function request() {
        try {
          abort = new AbortController();
          status.innerHTML = '요청중....';
          ...
          }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;간단하게 request 함수 실행시 객체를 만들어주자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdXXTF/btrH9GLdLoH/CaZfaODlEjWkk2fjIYCud1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdXXTF/btrH9GLdLoH/CaZfaODlEjWkk2fjIYCud1/img.gif&quot; data-alt=&quot;이제 취소 후에 새롭게 AbortController 객체를 생성한 뒤 재요청을 시도한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdXXTF/btrH9GLdLoH/CaZfaODlEjWkk2fjIYCud1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cdXXTF/btrH9GLdLoH/CaZfaODlEjWkk2fjIYCud1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;204&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;204&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이제 취소 후에 새롭게 AbortController 객체를 생성한 뒤 재요청을 시도한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MDN 한국번역본에서는 Experimental이라고 해서, 영어로 바꿔보니 Experimental 문구는 사라졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/AbortController&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/AbortController&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;1164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qwfQM/btrH1A0i1U0/Eh47Zdokkk9HsjNKMKkFu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qwfQM/btrH1A0i1U0/Eh47Zdokkk9HsjNKMKkFu0/img.png&quot; data-alt=&quot;https://developer.mozilla.org/ko/docs/Web/API/AbortController&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qwfQM/btrH1A0i1U0/Eh47Zdokkk9HsjNKMKkFu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqwfQM%2FbtrH1A0i1U0%2FEh47Zdokkk9HsjNKMKkFu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;450&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;1164&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://developer.mozilla.org/ko/docs/Web/API/AbortController&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IE 빼고 모두 지원하고 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/AbortController&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/AbortController&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Browser</category>
      <category>fetch 취소하기</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/145</guid>
      <comments>https://gobae.tistory.com/145#entry145comment</comments>
      <pubDate>Sun, 24 Jul 2022 20:25:10 +0900</pubDate>
    </item>
    <item>
      <title>엄격모드. &amp;quot;use strict&amp;quot;</title>
      <link>https://gobae.tistory.com/144</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-07-20 오후 10.30.41.webp&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4HhtB/btrHQpbWjy7/TRUsfk2e3ik2Jhktppe9Xk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4HhtB/btrHQpbWjy7/TRUsfk2e3ik2Jhktppe9Xk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4HhtB/btrHQpbWjy7/TRUsfk2e3ik2Jhktppe9Xk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4HhtB%2FbtrHQpbWjy7%2FTRUsfk2e3ik2Jhktppe9Xk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;176&quot; data-filename=&quot;스크린샷 2022-07-20 오후 10.30.41.webp&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엄격모드(strict mode)란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES5(ES2009)에서 추가된 스펙으로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암묵적인 느슨한 모드&quot;sloppy mode&quot;를 해제하고, 자바스크립트 문법을 보다 엄격히 적용하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;느슨한 모드에서는 오류를 발생시킬 확률이 높거나, 최적화에 문제를 일으킬 수 있는 작업들을 유발할 수 있으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 엄격모드로 전환함으로써 몇가지 실수에 대해서 명시적인 오류를 발생시키게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄격모드 코드 런타임에서는 몇가지 변화가 생기는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 기존에는 무시되던 몇가지 에러들을 던진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. JavaScript 엔진 최적화를 방해하는 실수들을 바로잡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. ECMAScript의 다음 버전들에서 정의될 문법을 금지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 핵심은?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;평소 자바스크립트는 너무 관대하게 동작하니, 조금 빡빡하게 보면서 잠재적 오류들을 예방하겠다는 것이다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;여기에는 개발자의 실수, 하위호환을 위해 남겨둔 옛날 문법, 성능개선, 미래에 사용될 문법 보존 등등을 포함한다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엄격모드를 어떻게 적용하는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 도입부에 &lt;u&gt;&lt;b&gt;&quot;use strict&quot;;&lt;/b&gt;&lt;/u&gt; 를 적어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Simple!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, 한번 적용한 엄격모드는 취소가 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;use strict&quot;는 있지만, &quot;no use strict&quot;는 없다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엄격모드를 보통 어디에 사용하나??&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 피해야 할 것 부터 보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 전역에는 피한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역의 &quot;use strict&quot;는 서드파티 라이브러리에 영향을 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서드파티 라이브러리가 non-strict-mode를 기반으로 구현되어 있다면..?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 무조건적인 함수 단위의 적용을 피한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 단위로 엄격모드를 적용하면, 일관성이 부족해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 코드 동작 예측이 어려워질 수 있으며, 모든 함수에 엄격모드를 적용하는 것은 또한 적절하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들면, 엄격모드를 적용한 함수의 상위 렉시컬환경(상위 스코프)가 엄격모드가 아닐 때 문제를 발생시킨다.&lt;/p&gt;
&lt;pre id=&quot;code_1658320132721&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(function(){
	var let = 10;
    function fn(){
    	&quot;use strict&quot;;
        let = 20;
    }
    fn();
}());

// syntax Error : Unexpected strict mode reserved word...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 보통, 엄격모드는 &lt;b&gt;즉시실행함수로 감싸는 &quot;스크립트 단위&quot; 적용&lt;/b&gt;하는 것이 바람직하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엄격 모드가 발생시키는 에러에는 어떤 것들이 있을까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 암묵적 전역변수 억제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;암묵적 전역&quot;이라고 불리는 이 현상은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언되어있지 않은 변수에 값을 할당하면, 에러를 일으키지 않고 전역객체의 프로퍼티가 되는 현상이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRPcIg/btrHOlIyCmF/dQlk91MzHwlkAQ1LNfNY10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRPcIg/btrHOlIyCmF/dQlk91MzHwlkAQ1LNfNY10/img.png&quot; data-alt=&quot;non-strict&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRPcIg/btrHOlIyCmF/dQlk91MzHwlkAQ1LNfNY10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRPcIg%2FbtrHOlIyCmF%2FdQlk91MzHwlkAQ1LNfNY10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;208&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;non-strict&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YHnVd/btrHL3oyRfl/rgQBrMtk4v4V3O9ExRl3L0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YHnVd/btrHL3oyRfl/rgQBrMtk4v4V3O9ExRl3L0/img.png&quot; data-alt=&quot;strict&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YHnVd/btrHL3oyRfl/rgQBrMtk4v4V3O9ExRl3L0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYHnVd%2FbtrHL3oyRfl%2FrgQBrMtk4v4V3O9ExRl3L0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;260&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;strict&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 변수 / 함수 / 매개변수 등 삭제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 delete 연산자는 객체의 프로퍼티를 삭제할 수 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제할 수 없는 delete 명령에 대해서는 false만 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Errors/Delete_in_strict_mode&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Errors/Delete_in_strict_mode&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c84OQZ/btrHOQHXTJR/xe2ixEOHCW71ZOnngkRZQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c84OQZ/btrHOQHXTJR/xe2ixEOHCW71ZOnngkRZQ1/img.png&quot; data-alt=&quot;기존 변수 삭제시 false만 반환한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c84OQZ/btrHOQHXTJR/xe2ixEOHCW71ZOnngkRZQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc84OQZ%2FbtrHOQHXTJR%2Fxe2ixEOHCW71ZOnngkRZQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;158&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존 변수 삭제시 false만 반환한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strict mode에서는 명시적인 에러를 일으킨다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmBxB5/btrHPfHDuQ3/8h2KcmsYpbLNkNhLtsqfGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmBxB5/btrHPfHDuQ3/8h2KcmsYpbLNkNhLtsqfGK/img.png&quot; data-alt=&quot;strict mode syntax error&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmBxB5/btrHPfHDuQ3/8h2KcmsYpbLNkNhLtsqfGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmBxB5%2FbtrHPfHDuQ3%2F8h2KcmsYpbLNkNhLtsqfGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;146&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;strict mode syntax error&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 중복된 함수 매개변수 이름 방지&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서는 매개변수가 중복된다면, 먼저 지정된 인수가 숨겨지고 나중에 지정된 인수가 해당 인수값을 가지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strict mode에서는 중복된 매개변수 이름에 대해서 Syntax 에러를 일으킨다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nzz7B/btrHPcRzRmP/7FNKPV7Y2ktGjOOZ4AyPlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nzz7B/btrHPcRzRmP/7FNKPV7Y2ktGjOOZ4AyPlk/img.png&quot; data-alt=&quot;일반 모드에서는 이상없이 실행되나, Strict mode에서는 에러가 된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nzz7B/btrHPcRzRmP/7FNKPV7Y2ktGjOOZ4AyPlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnzz7B%2FbtrHPcRzRmP%2F7FNKPV7Y2ktGjOOZ4AyPlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;176&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일반 모드에서는 이상없이 실행되나, Strict mode에서는 에러가 된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 non-strict에서 함수가 받은 인수는 arguments 객체에 담기게 되는 것을 이용하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복된 매개변수를 처리할 수는 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, 보통 이러한 예외처리는 개발자가 의도한 것이 아닐 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btAaTC/btrHOTx26cO/8VOkCj89en4eko1WU7tVK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btAaTC/btrHOTx26cO/8VOkCj89en4eko1WU7tVK1/img.png&quot; data-alt=&quot;함수 평가시 내부적으로 arguments 객체를 생성한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btAaTC/btrHOTx26cO/8VOkCj89en4eko1WU7tVK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtAaTC%2FbtrHOTx26cO%2F8VOkCj89en4eko1WU7tVK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;138&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;함수 평가시 내부적으로 arguments 객체를 생성한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 일반함수에서의 this 바인딩&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반함수에서 this는 보통의 경우에 전역객체가 바인딩된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 strict mode에서는 this 바인딩을 수행하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJLn7o/btrHNWPKUNt/XhVuhmK1k3yqbHW9j2nW71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJLn7o/btrHNWPKUNt/XhVuhmK1k3yqbHW9j2nW71/img.png&quot; data-alt=&quot;평소에는 this에는 전역객체가 바인딩된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJLn7o/btrHNWPKUNt/XhVuhmK1k3yqbHW9j2nW71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJLn7o%2FbtrHNWPKUNt%2FXhVuhmK1k3yqbHW9j2nW71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;135&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;평소에는 this에는 전역객체가 바인딩된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cI84CM/btrHNLgHZVO/GubESwwOzTRmbMdWtURkU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cI84CM/btrHNLgHZVO/GubESwwOzTRmbMdWtURkU0/img.png&quot; data-alt=&quot;엄격모드에서는 this가 없다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cI84CM/btrHNLgHZVO/GubESwwOzTRmbMdWtURkU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcI84CM%2FbtrHNLgHZVO%2FGubESwwOzTRmbMdWtURkU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;136&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;엄격모드에서는 this가 없다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반함수에서는 this가 필요하지 않기 때문이다.(물론 메서드로 호출되는 함수는 다르다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 생성자 함수로 동작해야 한다면 this를 기반으로 인스턴스를 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYjzv8/btrHOtTO4GI/lpFVWmLODAbzTP9Dx9jyLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYjzv8/btrHOtTO4GI/lpFVWmLODAbzTP9Dx9jyLK/img.png&quot; data-alt=&quot;생성자 함수로서 호출되면, this가 생성된 인스턴스를 가리킨다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYjzv8/btrHOtTO4GI/lpFVWmLODAbzTP9Dx9jyLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYjzv8%2FbtrHOtTO4GI%2FlpFVWmLODAbzTP9Dx9jyLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;161&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성자 함수로서 호출되면, this가 생성된 인스턴스를 가리킨다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pdt3K/btrHPeWcpX4/W26hfBHsXcCSPuaJMGQnU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pdt3K/btrHPeWcpX4/W26hfBHsXcCSPuaJMGQnU1/img.png&quot; data-alt=&quot;엄격모드에서도 동일하게 동작해야 한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pdt3K/btrHPeWcpX4/W26hfBHsXcCSPuaJMGQnU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpdt3K%2FbtrHPeWcpX4%2FW26hfBHsXcCSPuaJMGQnU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;171&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;엄격모드에서도 동일하게 동작해야 한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에도,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;with문 사용시 에러를 발생시키거나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예약어를 변수값 / 프로퍼티 등으로 사용하는 행위에 에러를 발생시키거나...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기전용 프로퍼티에 값을 할당하는 행위를 막아준다거나..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;1120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8p6MB/btrHKN67WFH/GX1RNanefKbtXnkrI7TMX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8p6MB/btrHKN67WFH/GX1RNanefKbtXnkrI7TMX0/img.png&quot; data-alt=&quot;그외 여러 경우들..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8p6MB/btrHKN67WFH/GX1RNanefKbtXnkrI7TMX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8p6MB%2FbtrHKN67WFH%2FGX1RNanefKbtXnkrI7TMX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;652&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;1120&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그외 여러 경우들..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-07-20 오후 10.38.06.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1lVUu/btrHOAFvt7C/wJkryryUYQ5KRme8LxPdK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1lVUu/btrHOAFvt7C/wJkryryUYQ5KRme8LxPdK0/img.png&quot; data-alt=&quot;writable:false에는 값을 할당할 수 없어야 한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1lVUu/btrHOAFvt7C/wJkryryUYQ5KRme8LxPdK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1lVUu%2FbtrHOAFvt7C%2FwJkryryUYQ5KRme8LxPdK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;846&quot; data-filename=&quot;스크린샷 2022-07-20 오후 10.38.06.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;846&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;writable:false에는 값을 할당할 수 없어야 한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ES6의 기본 모드?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[ES6에서는 default mode가 &quot;strict mode&quot;](&lt;a href=&quot;https://stackoverflow.com/questions/31685262/not-recommended-to-use-use-strict-in-es6&quot;&gt;https://stackoverflow.com/questions/31685262/not-recommended-to-use-use-strict-in-es6&lt;/a&gt;)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Strict_mode&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Strict_mode&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/31685262/not-recommended-to-use-use-strict-in-es6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/31685262/not-recommended-to-use-use-strict-in-es6&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://bonita-sy.tistory.com/entry/Strict-mode엄격-모드-use-strict-란&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://bonita-sy.tistory.com/entry/Strict-mode엄격-모드-use-strict-란&lt;/a&gt;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>javascript use strict</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/144</guid>
      <comments>https://gobae.tistory.com/144#entry144comment</comments>
      <pubDate>Wed, 20 Jul 2022 22:52:52 +0900</pubDate>
    </item>
    <item>
      <title>이벤트 위임, 버블링, 캡쳐링 (Event Delegation, bubbling, capturing)</title>
      <link>https://gobae.tistory.com/143</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-07-11 오후 10.39.29.webp&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H6N28/btrG1sV50GU/8CJZmFZBQrTsJA4JoTzZe0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H6N28/btrG1sV50GU/8CJZmFZBQrTsJA4JoTzZe0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H6N28/btrG1sV50GU/8CJZmFZBQrTsJA4JoTzZe0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH6N28%2FbtrG1sV50GU%2F8CJZmFZBQrTsJA4JoTzZe0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;193&quot; data-filename=&quot;스크린샷 2022-07-11 오후 10.39.29.webp&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 위임&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유사한 노드들을 다뤄야 할 때 노드마다 핸들러를 할당하지 않고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통 조상에 이벤트 핸들러를 할당하여 여러 노드들을 한꺼번에 다루는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하면, 이벤트를 효율적으로 다루는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트가 전파되는 특징(캡처링과 버블링)을 이용하면 이벤트 위임을 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 위임을 배우기에 앞서, 먼저 노드마다 직접 핸들러를 할당하는 경우를 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1657547117888&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;4&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;5&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;

    &amp;lt;script&amp;gt;
      const lists = document.querySelectorAll('li');

      function handleClick(event) {
        console.log(event.target.innerHTML);
      }

      lists.forEach((li) =&amp;gt; li.addEventListener('click', handleClick));
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rWLNd/btrGZMHQGpZ/IXecyzyFy6aXcYlRKU4cX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rWLNd/btrGZMHQGpZ/IXecyzyFy6aXcYlRKU4cX0/img.png&quot; data-alt=&quot;현재의 모든 li 태그에 이벤트핸들러를 할당했다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rWLNd/btrGZMHQGpZ/IXecyzyFy6aXcYlRKU4cX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrWLNd%2FbtrGZMHQGpZ%2FIXecyzyFy6aXcYlRKU4cX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;235&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;현재의 모든 li 태그에 이벤트핸들러를 할당했다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 나름대로 querySelectorAll + forEach를 통해 그럴싸한 코드를 작성해뒀는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 add 버튼을 통해 li태그가 추가되거나, li태그에 delete 버튼을 생성해 삭제버튼을 도입했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 li태그 추가시에 해당 태그에도 이벤트핸들러를 부여해야 하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 li태그 삭제시에 해당 태그에 있던 이벤트핸들러를 해지해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 지금 같은 단순한 숫자 리스트가 아닌, 쇼핑몰에서 각각의 상품들이 list로서 추가된다면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쇼핑몰의 상품들이 몇십개정도로 끝날까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이런 경우에는 이벤트 위임을 적용해보면 좀 더 좋을 듯 하다.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;event.currentTarget, event.target&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 위임을 위해서는, 이벤트핸들러에서 감지한 이벤트 객체를 이용할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;currentTarget은 현재 타겟(=이벤트를 감지한 현재 위치)를 의미한다.&lt;br /&gt;target은 이벤트가 처음 발생한 위치를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그래서 공통 조상의 이벤트핸들러에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;event.target을 이용하면, 이벤트가 &lt;b&gt;발생한 위치&lt;/b&gt;를 파악할 수 있게 된다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;  &amp;lt;!DOCTYPE html&amp;gt;
  &amp;lt;html lang=&quot;en&quot;&amp;gt;
    &amp;lt;head&amp;gt;
      &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;4&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;5&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;

      &amp;lt;script&amp;gt;
        const ul = document.querySelector('ul');

        ul.addEventListener('click', (event) =&amp;gt; {
          const { currentTarget, target } = event;
          console.log(currentTarget);
          console.log(target);
        });
      &amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
  &amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvVnfh/btrG3JCVzsL/dY9zF7DEcOMlNFM3ZeKWBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvVnfh/btrG3JCVzsL/dY9zF7DEcOMlNFM3ZeKWBk/img.png&quot; data-alt=&quot;순서대로, currentTarget, target을 의미한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvVnfh/btrG3JCVzsL/dY9zF7DEcOMlNFM3ZeKWBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvVnfh%2FbtrG3JCVzsL%2FdY9zF7DEcOMlNFM3ZeKWBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;230&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;순서대로, currentTarget, target을 의미한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제와 비슷하나, li 태그들의 공통 부모 태그인 ul에만 &lt;span&gt;이벤트핸들러를&lt;span&gt;&amp;nbsp;부여했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;그러면 이벤트핸들러에서 event 객체의 target 프로퍼티를 통해 어디서 이벤트가 발생했는지를 알 수 있게 되고&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;위에서 일일히 li 태그에 이벤트핸들러를 부여하던 방식관 달리, li태그의 추가와 삭제에도 유리한 구조를 가지게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;그렇다면 우리가 분명 li를 클릭했으나, 왜 ul의 이벤트핸들러에서 이벤트가 감지된 것일까?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 전파&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 이벤트는 &quot;전파&quot;되는 특성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 방금 우리는 li 태그를 클릭했지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;li 태그 클릭은 ul 태그를 클릭한 것이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ul 태그 클릭은 body 태그 클릭이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;body 태그는 html 태그를 누른 것이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;html 태그를 누른 것은 document 객체에서 감지되며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;document 객체에서 감지된 이벤트는 window 객체에서도 감지되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;??????????????????????응? 아무튼 그렇다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트는 3가지 단계를 거치게 되는데,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ORxUs/btrG4dp7EJ6/J5erepKaK7HBleeoZRWko1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ORxUs/btrG4dp7EJ6/J5erepKaK7HBleeoZRWko1/img.png&quot; data-alt=&quot;이벤트 전파의 3단계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ORxUs/btrG4dp7EJ6/J5erepKaK7HBleeoZRWko1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FORxUs%2FbtrG4dp7EJ6%2FJ5erepKaK7HBleeoZRWko1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;542&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이벤트 전파의 3단계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캡처링 단계, 타겟 단계, 버블링 단계로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림을 통해 위에서 설명했던 li..ul..body..주저리주저리가 타겟 단계 + 버블링 단계를 의미함을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저렇게 설명한 이유는, elem.addEventListener()의 기본 설정이 이벤트 버블링 감지이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해서 확인해봐도 결과는 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1657548189130&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;

    &amp;lt;script&amp;gt;
      const { body } = document;
      const ul = document.querySelector('ul');
      const li = document.querySelector('li');

      function handleClick(pos) {
        return () =&amp;gt; {
          console.log(`I'm ${pos}`);
        };
      }

      document.addEventListener('click', handleClick('document'));
      body.addEventListener('click', handleClick('body'));
      ul.addEventListener('click', handleClick('ul'));
      li.addEventListener('click', handleClick('li'));
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vDvYB/btrG5sgapIx/Y53jAHw7OAmykGKkAkuRM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vDvYB/btrG5sgapIx/Y53jAHw7OAmykGKkAkuRM0/img.png&quot; data-alt=&quot;li를 클릭하면, li &amp;amp;gt; ul &amp;amp;gt; body &amp;amp;gt; document의 이벤트핸들러가 각각 순서대로 실행된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vDvYB/btrG5sgapIx/Y53jAHw7OAmykGKkAkuRM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvDvYB%2FbtrG5sgapIx%2FY53jAHw7OAmykGKkAkuRM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;163&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;li를 클릭하면, li &amp;gt; ul &amp;gt; body &amp;gt; document의 이벤트핸들러가 각각 순서대로 실행된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 버블링과 캡처링이 무엇이며 어떻게 다뤄야 하는 것인지 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 버블링, 이벤트 캡처링&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 버블링&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트가 발생한 위치(target)을 기준으로, 그것을 감싼 상위 엘리먼트까지 올라가면서 이벤트가 전파되는 현상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 target(li)를 기준으로 ul &amp;gt; body &amp;gt; document...로 이벤트가 전파된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 버블링을 감지하기 위해서는, 감지하고 싶은 태그에서 addEventListener로 이벤트핸들러를 달아주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(focus같이 전파되지 않는 이벤트가 있으나, 이는 예외다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 캡처링&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window부터 시작해서 이벤트가 발생한 위치(target)까지 이벤트가 전파되는 현상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 li를 클릭하면, window &amp;gt; document &amp;gt; body &amp;gt; ul &amp;gt; li로 이벤트가 전파된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 캡처링을 감지하기 위해서는, 감지하고 싶은 태그에서 addEventListener의 3번째 인수로 true 값을 부여한다.&lt;/p&gt;
&lt;pre id=&quot;code_1657548583680&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;

    &amp;lt;script&amp;gt;
      const { body } = document;
      const ul = document.querySelector('ul');
      const li = document.querySelector('li');

      function bubbling(pos) {
        return () =&amp;gt; {
          console.log(`bubble ${pos}`);
        };
      }

      document.addEventListener('click', bubbling('document'));
      body.addEventListener('click', bubbling('body'));
      ul.addEventListener('click', bubbling('ul'));
      li.addEventListener('click', bubbling('li'));

      function capturing(pos) {
        return () =&amp;gt; {
          console.log(`capture ${pos}`);
        };
      }

      document.addEventListener('click', capturing('document'), true);
      body.addEventListener('click', capturing('body'), true);
      ul.addEventListener('click', capturing('ul'), true);
      li.addEventListener('click', capturing('li'), true);
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1664&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v6d5f/btrG4TrpmxP/R0KREOpkAIUR3EYI5UPYgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v6d5f/btrG4TrpmxP/R0KREOpkAIUR3EYI5UPYgk/img.png&quot; data-alt=&quot;이벤트 캡처링 및 버블링&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v6d5f/btrG4TrpmxP/R0KREOpkAIUR3EYI5UPYgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv6d5f%2FbtrG4TrpmxP%2FR0KREOpkAIUR3EYI5UPYgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;167&quot; data-origin-width=&quot;1664&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이벤트 캡처링 및 버블링&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 헷갈릴 수 있는데, 정리하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트는 capturing phase -&amp;gt; target phase -&amp;gt; bubbling phase로 전파된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addEventListener에 부여한 핸들러는, 기본적으로 bubbling phase를 감지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addEventListener의 3번째 인수로 true를 부여하면, capturing phase를 감지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이와 같이 이벤트가 전파되는 특성을 통해서, 이벤트 위임을 적용할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;번외) 이벤트 전파를 막는 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;event.stopPropagation&lt;/b&gt; 메서드를 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 캡처링 / 버블링을 감지하고, 현재 감지한 이벤트 이후의 전파를 막게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event.preventDefault와 혼동될 수 있는데, 이것은 해당 이벤트의 기본 동작을 막는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stopPropagation은 부모/자식 엘리먼트로의 이벤트가 전파되는 것을 막는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 만약 document + capturing에서의 핸들러에서 preventDefault를 이용하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트는 기본 동작을 안할 뿐 하위에 전파되어 body, ul 등등에서 감지된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 동일한 조건에서 stopPropagation을 활용하면 capturing 단계에서 document에서 이벤트 전파가 막혀서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;capturing body, ul 등등이 감지되지 않을 뿐 아니라, bubbling li, ul 등등도 감지되지 않게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 전파가 capturing 단계에서 document 레벨에서 막혔기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이벤트 전파는 정말 특별한 이유가 있는게 아니라면 막지 않는 것이 좋다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그 태그를 정확히 클릭하지 않았을 수도 있다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 분명 li를 클릭했다고 생각했지만, 사실 li가 아니라 li태그 안의 img를 눌렀다거나, span을 눌렀을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때는 event.target에 li가 나타나지 않으므로 이벤트위임을 이용하는 핸들러가 예상과 다른 동작을 할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;element.closest(selector)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;closest 메서드는 자신(element)을 기준으로, selector를 찾을 때 까지 부모방향으로 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;selector로 tag, class, id 등등을 이용할 수 있으며, 탐색하며 처음 발견한 selector를 반환하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657550066505&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;1&amp;lt;span&amp;gt;Hi I'm span&amp;lt;/span&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;4&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;5&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;

    &amp;lt;script&amp;gt;
      const ul = document.querySelector('ul');

      ul.addEventListener('click', (event) =&amp;gt; {
        const { target } = event;
        const li = target.closest('li');
        console.log(target);
        console.log(li);
      });
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OMLuQ/btrG2tuaxvd/zOwIDkAViX3YRNZBfdkeik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OMLuQ/btrG2tuaxvd/zOwIDkAViX3YRNZBfdkeik/img.png&quot; data-alt=&quot;span이 li 내부에 존재하므로, &amp;amp;lt;li&amp;amp;gt;1&amp;amp;lt;/li&amp;amp;gt;를 담게 된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OMLuQ/btrG2tuaxvd/zOwIDkAViX3YRNZBfdkeik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOMLuQ%2FbtrG2tuaxvd%2FzOwIDkAViX3YRNZBfdkeik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;147&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;span이 li 내부에 존재하므로, &amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;를 담게 된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 event.target은 span이며, closest로 li를 찾을 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 위임은, 다음과 같은 순서로 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 감지할 노드들의 상위 노드에 핸들러를 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 핸들러에서 event.target을 통해 이벤트가 발생한 위치를 알아낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 원하는 노드에서 이벤트가 발생했다면, 핸들링해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 장점과 단점을 정리해보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통 조상에 핸들러를 할당하여, 초기화가 단순해지며 이벤트핸들러로 인한 메모리도 절약된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하위 노드 추가 혹은 제거시 노드에 할당된 핸들러를 추가/제거할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하위 노드에 부여한 data 속성이나 closest메서드 등등을 이용해 이벤트 핸들링을 응용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 이벤트가 전파(특히 버블링)되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상위 노드에 할당한 이벤트핸들러는, 모든 하위 노드에서 버블링된 이벤트에 응답해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 이벤트 위임</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/143</guid>
      <comments>https://gobae.tistory.com/143#entry143comment</comments>
      <pubDate>Mon, 11 Jul 2022 23:48:40 +0900</pubDate>
    </item>
    <item>
      <title>[Commit] 과거 커밋으로 돌아가서 새로운 브랜치(분기) 만들기</title>
      <link>https://gobae.tistory.com/142</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;목적&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 react 프로젝트에 nextJS를 입혀서 현재 nextJS를 조금씩 학습하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러던 도중 이전의 react 프로젝트에 이것저것 실험해보고 싶은게 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번 포스팅의 목적은, nextJS 적용 이전 커밋으로 돌아가서 새로운 브랜치를 개설하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;안냐쌈.webp&quot; data-origin-width=&quot;1924&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQSP1H/btrD9GCXMTH/1KhNk0lfJDJL4WJiBOqbMK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQSP1H/btrD9GCXMTH/1KhNk0lfJDJL4WJiBOqbMK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQSP1H/btrD9GCXMTH/1KhNk0lfJDJL4WJiBOqbMK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQSP1H%2FbtrD9GCXMTH%2F1KhNk0lfJDJL4WJiBOqbMK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;1000&quot; data-filename=&quot;안냐쌈.webp&quot; data-origin-width=&quot;1924&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약해보면 이 그림을 수행할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;돌아가려는 커밋의 해시 찾기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 돌아가고자 하는 커밋의 해시를 찾아야 하는데,&amp;nbsp;커밋 해시를 보기 위해선 두가지 방법이면 충분하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 깃허브 프로젝트 저장소 이용하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 로컬 프로젝트 폴더에서 터미널 이용하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용을 보고, 둘 중에 하나의 방법을 고르면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 깃허브 프로젝트 저장소 이용하기&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1626&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CkDcz/btrD6x086qg/usFwhpL9pgiqOsGdGrHKs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CkDcz/btrD6x086qg/usFwhpL9pgiqOsGdGrHKs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CkDcz/btrD6x086qg/usFwhpL9pgiqOsGdGrHKs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCkDcz%2FbtrD6x086qg%2FusFwhpL9pgiqOsGdGrHKs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;538&quot; data-origin-width=&quot;1626&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 저장소에서 커밋 로그를 보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHNNl9/btrEczpzRNJ/SMiufMSEakD40aDITZkH30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHNNl9/btrEczpzRNJ/SMiufMSEakD40aDITZkH30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHNNl9/btrEczpzRNJ/SMiufMSEakD40aDITZkH30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHNNl9%2FbtrEczpzRNJ%2FSMiufMSEakD40aDITZkH30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;210&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4640c2b~ 라는 해시가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 전체는&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;4640c2b91fa356467985a8da1fd0c6e53d0a9235 이지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;앞 6글자 4640c2만 기억하면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 로컬 프로젝트 폴더에서 터미널 이용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 폴더의 터미널에서 &lt;b&gt;git log&lt;/b&gt;를 입력하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2338&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zoJo4/btrD8OuwA97/QWHuTYz1NOVYXTQ8LdAlok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zoJo4/btrD8OuwA97/QWHuTYz1NOVYXTQ8LdAlok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zoJo4/btrD8OuwA97/QWHuTYz1NOVYXTQ8LdAlok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzoJo4%2FbtrD8OuwA97%2FQWHuTYz1NOVYXTQ8LdAlok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;322&quot; data-origin-width=&quot;2338&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HEAD가 위치한 커밋부터 이전의 커밋들을 모두 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1872&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eoGiM0/btrD5OVjb76/lEjKKkScC0N4GbqT8iguoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eoGiM0/btrD5OVjb76/lEjKKkScC0N4GbqT8iguoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eoGiM0/btrD5OVjb76/lEjKKkScC0N4GbqT8iguoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeoGiM0%2FbtrD5OVjb76%2FlEjKKkScC0N4GbqT8iguoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;118&quot; data-origin-width=&quot;1872&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 마찬가지로 원하는 커밋을 찾아서, 그 커밋의 해시 앞 6자를 기억하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이제 해당 커밋으로 돌아갈 차례다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;git checkout 4640c2&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2782&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ndC8n/btrEcAaXL4X/o0kfxqAanTl6INBL7qvHNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ndC8n/btrEcAaXL4X/o0kfxqAanTl6INBL7qvHNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ndC8n/btrEcAaXL4X/o0kfxqAanTl6INBL7qvHNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FndC8n%2FbtrEcAaXL4X%2Fo0kfxqAanTl6INBL7qvHNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;291&quot; data-origin-width=&quot;2782&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;switching~ 4640c2 라는 메시지와 함께&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재의 상태에서 어떤 것을 할 수 있고.. 새 브랜치를 어떻게 생성할 수 있으며 삭제는 어떻게 가능한지 친절하게 알려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 4640c2의 커밋으로 이동했기 때문에, 좌측 탐색기에 담긴 파일들이 그 커밋 시점의 파일의 스냅샷으로 변했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 원하던 nextJS가 없는 react 프로젝트 커밋으로 돌아왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;git log&lt;/b&gt;를 입력해보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6AfwR/btrD8jU1o75/kgybHgLKh78BHmtDD5wKh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6AfwR/btrD8jU1o75/kgybHgLKh78BHmtDD5wKh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6AfwR/btrD8jU1o75/kgybHgLKh78BHmtDD5wKh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6AfwR%2FbtrD8jU1o75%2FkgybHgLKh78BHmtDD5wKh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;332&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HEAD가 4640c2 커밋을 바라보고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이제 새로운 브랜치를 만들 차례다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리액트로 이것저것 해볼 생각이니&amp;nbsp;&lt;/span&gt;브랜치 이름은 그냥 react-playground로 정했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;git branch react-playground&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;git switch &lt;span style=&quot;color: #000000;&quot;&gt;react-playground&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;git branch&lt;/b&gt; 혹은 &lt;b&gt;git log&lt;/b&gt; 를 실행해보면&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2362&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/opZYc/btrEcyKZxKU/KVsgAHLwqK2gkZRr60pkP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/opZYc/btrEcyKZxKU/KVsgAHLwqK2gkZRr60pkP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/opZYc/btrEcyKZxKU/KVsgAHLwqK2gkZRr60pkP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FopZYc%2FbtrEcyKZxKU%2FKVsgAHLwqK2gkZRr60pkP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;344&quot; data-origin-width=&quot;2362&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 브랜치 react-playground가 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 브랜치에서 여러 작업들을 하고 나면 커밋들이 쌓일 것이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 커밋을 github에 올려줘서 기록을 남겨보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법은 이전과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git commit -m &amp;ldquo;커밋 메시지&quot; 로 커밋을 기록했다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;git push origin react-playground&lt;/b&gt; 을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;origin(remote 저장소인 github)에 react-playground 라는 브랜치에 push 하겠다는 의미다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 github에는 react-playground 브랜치가 존재하지 않는데, 걱정할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;push 명령어를 수행하면 알아서 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1144&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Elnl8/btrD8NoQ25q/RPVcz1IUDhQTkNjb2qBFO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Elnl8/btrD8NoQ25q/RPVcz1IUDhQTkNjb2qBFO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Elnl8/btrD8NoQ25q/RPVcz1IUDhQTkNjb2qBFO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FElnl8%2FbtrD8NoQ25q%2FRPVcz1IUDhQTkNjb2qBFO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;242&quot; data-origin-width=&quot;1144&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 github 프로젝트에 노란색 문구가 뜨는데, 해당 브랜치로 이동해보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lRaig/btrEbDTluBq/R9GjlmByleOGCFU4mZz571/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lRaig/btrEbDTluBq/R9GjlmByleOGCFU4mZz571/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lRaig/btrEbDTluBq/R9GjlmByleOGCFU4mZz571/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlRaig%2FbtrEbDTluBq%2FR9GjlmByleOGCFU4mZz571%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;270&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 브랜치와 그 브랜치에서 쌓은 커밋이 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4640c2 커밋도 보이고, 그 위에 쌓인 Docs:README 업데이트 커밋이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 과거 시점으로부터 새 브랜치 개설하기는 끝이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TMI&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 브랜치를 잘못 생성했다면?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crz9ai/btrEcRDxHbA/oLkmmJG7YAam8JJ7d6KYK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crz9ai/btrEcRDxHbA/oLkmmJG7YAam8JJ7d6KYK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crz9ai/btrEcRDxHbA/oLkmmJG7YAam8JJ7d6KYK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcrz9ai%2FbtrEcRDxHbA%2FoLkmmJG7YAam8JJ7d6KYK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;308&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃헙 브랜치 목록의 View all branches에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ekVD3P/btrD8MQ3ZBy/4A6rso82sfKhsuif1kOZd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ekVD3P/btrD8MQ3ZBy/4A6rso82sfKhsuif1kOZd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ekVD3P/btrD8MQ3ZBy/4A6rso82sfKhsuif1kOZd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FekVD3P%2FbtrD8MQ3ZBy%2F4A6rso82sfKhsuif1kOZd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;264&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴지통 버튼을 눌러 날려버리자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬의 브랜치는 &lt;b&gt;git switch master&lt;/b&gt; 라던가&lt;b&gt; git checkout 4640c2&lt;/b&gt; 등으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 react-playground를 삭제할 시 영향을 받지 않을 다른 시점으로 옮겨 준 후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;git branch -D react-playground&lt;/b&gt; 를 입력해주면 깔끔하게 삭제된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 브랜치의 최근 시점으로 전환할 때는&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;git switch 브랜치이름&lt;/b&gt;의 switch 명령을 이용하면 된다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &amp;zwj;♀️&lt;/span&gt;&lt;/p&gt;</description>
      <category>Git &amp;amp; Github</category>
      <category>깃 이전 커밋에서 새 브랜치 만들기</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/142</guid>
      <comments>https://gobae.tistory.com/142#entry142comment</comments>
      <pubDate>Tue, 7 Jun 2022 15:52:39 +0900</pubDate>
    </item>
    <item>
      <title>[에러] netlify에서 nextJS의 &amp;quot;getServerSideProps&amp;quot; can not be exported 해결하기</title>
      <link>https://gobae.tistory.com/141</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;에러 내용&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;thumbnail.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tD9O4/btrDWK6F9wP/z4xj4a0YY9pw2YcE44nYs1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tD9O4/btrDWK6F9wP/z4xj4a0YY9pw2YcE44nYs1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tD9O4/btrDWK6F9wP/z4xj4a0YY9pw2YcE44nYs1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtD9O4%2FbtrDWK6F9wP%2Fz4xj4a0YY9pw2YcE44nYs1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;549&quot; data-filename=&quot;thumbnail.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;netlify에 배포한 프론트엔드 앱에서,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Error occured prerendering page &quot;/&quot;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Error for page /: pages with &quot;getServerSideProps&quot; can not be exported ...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 에러가 발생해서 해결하는 과정을 담았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;netlify에서 build시에 [next export]를 실행하고 있다면, 해당 스크립트를 제거하면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;오류 발생까지의 상황&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;netlify에서 Build settings에서의 설정과 밀접한 관련이 있어서 이 부분을 위주로 적을 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;netlify의 Build Setting은 Project - Site Settings - Build &amp;amp; deploy - Build settings 에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;리액트 앱 배포&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 처음 netlify에 배포한 앱은, 리액트 앱이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;netlify에서 프로젝트마다 build setting을 해줘야 하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 프론트 앱은 build의 결과물인 static file들로 실행하기 때문에&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-06 오전 11.26.48.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bph7AT/btrD3xdTkNB/eThKB5QPxcjtesCYGiUfAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bph7AT/btrD3xdTkNB/eThKB5QPxcjtesCYGiUfAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bph7AT/btrD3xdTkNB/eThKB5QPxcjtesCYGiUfAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbph7AT%2FbtrD3xdTkNB%2FeThKB5QPxcjtesCYGiUfAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;454&quot; data-filename=&quot;스크린샷 2022-06-06 오전 11.26.48.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 앱은 이정도 설정이면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 짧게 설명하면, [npm run build]는 package.json을 기반해서 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 여러분 프로젝트의 package.json에 있는 &quot;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;build&quot;:&amp;nbsp;&quot;react-scripts&amp;nbsp;build&quot; 스크립트를 실행하게 되고,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;실행 결과물인 build directory를 netlify에서 public directory로 설정해두면 리액트 앱 배포는 완료된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;nextJS 앱 배포&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 react 앱에 next를 설치하고, 디렉토리를 pages 기반으로 설정했다면 next 앱을 배포하고 싶을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next 앱의 package.json에서 기본 build script는 &quot;build&quot; : &quot;next build&quot; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드와 함께 &quot;next export&quot; 명령을 추가해줘야 하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json에 &quot;netlify-build&quot; : &quot;next build &amp;amp;&amp;amp; next export&quot; 스크립트를 추가한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMvmQy/btrD5TnQGp2/3OehVgkq0cCmjcEBcURWE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMvmQy/btrD5TnQGp2/3OehVgkq0cCmjcEBcURWE1/img.png&quot; data-alt=&quot;next build &amp;amp;amp;&amp;amp;amp; next export 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMvmQy/btrD5TnQGp2/3OehVgkq0cCmjcEBcURWE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMvmQy%2FbtrD5TnQGp2%2F3OehVgkq0cCmjcEBcURWE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;344&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;next build &amp;amp;&amp;amp; next export 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 netlify의 build settings에서&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-06 오전 11.26.54.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;655&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KWscw/btrDWJUejDi/AAq7YSKYDdLRM12Fhkz9QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KWscw/btrDWJUejDi/AAq7YSKYDdLRM12Fhkz9QK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KWscw/btrDWJUejDi/AAq7YSKYDdLRM12Fhkz9QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKWscw%2FbtrDWJUejDi%2FAAq7YSKYDdLRM12Fhkz9QK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;655&quot; data-filename=&quot;스크린샷 2022-06-06 오전 11.26.54.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;655&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 스크립트 netlify-build와 publish dir인 out를 반영해주고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;netlify 사이트에서 프로젝트에 plugin을 설치한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2398&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccAmBU/btrD1fMppjM/0Jsqi3DHz2xqB5iYhUOe0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccAmBU/btrD1fMppjM/0Jsqi3DHz2xqB5iYhUOe0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccAmBU/btrD1fMppjM/0Jsqi3DHz2xqB5iYhUOe0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccAmBU%2FbtrD1fMppjM%2F0Jsqi3DHz2xqB5iYhUOe0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;200&quot; data-origin-width=&quot;2398&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 netlify에서 next 앱을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;드디어 getServerSideProps&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 SSR을 위해&amp;nbsp;getServerSideProps 함수 코드를 작성했다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 포스팅에서 다룰 에러를 마주쳤을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;thumbnail.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tD9O4/btrDWK6F9wP/z4xj4a0YY9pw2YcE44nYs1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tD9O4/btrDWK6F9wP/z4xj4a0YY9pw2YcE44nYs1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tD9O4/btrDWK6F9wP/z4xj4a0YY9pw2YcE44nYs1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtD9O4%2FbtrDWK6F9wP%2Fz4xj4a0YY9pw2YcE44nYs1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;549&quot; data-filename=&quot;thumbnail.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 친절하게 설명해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 CSR 방식만 있던 next App에 SSR 기능을 추가하여 에러가 발생했으니,,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 netlify에서 친절하게 알려주는대로&amp;nbsp;&lt;a href=&quot;https://nextjs.org/docs/messages/prerender-error&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/docs/messages/prerender-error &lt;/a&gt;에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yo3UU/btrD6wTspRc/jMsxGK4epDqaW8eHJqhN3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yo3UU/btrD6wTspRc/jMsxGK4epDqaW8eHJqhN3K/img.png&quot; data-alt=&quot;netlify prerender-error&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yo3UU/btrD6wTspRc/jMsxGK4epDqaW8eHJqhN3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyo3UU%2FbtrD6wTspRc%2FjMsxGK4epDqaW8eHJqhN3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;814&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;netlify prerender-error&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 맨 마지막 줄의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Make sure you are not trying to export (`next export`) pages that have ssr enabled (getServerSideProps)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 어기고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 기존 Build Settings를 다시 한번 수정해줬다.&lt;/p&gt;
&lt;pre id=&quot;code_1654487008951&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 프로젝트의 package.json

&quot;scripts&quot;: {
    &quot;dev&quot;: &quot;PORT=3001 next dev&quot;,
    &quot;start&quot;: &quot;next start&quot;,
    &quot;build&quot;: &quot;next build&quot;,
    &quot;netlify-build&quot;: &quot;next build &amp;amp;&amp;amp; next export&quot;
  },&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-06 오전 11.26.59.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FT6DT/btrD6xkxAyl/CMkmRAqqPJTmGkk1VSAQA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FT6DT/btrD6xkxAyl/CMkmRAqqPJTmGkk1VSAQA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FT6DT/btrD6xkxAyl/CMkmRAqqPJTmGkk1VSAQA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFT6DT%2FbtrD6xkxAyl%2FCMkmRAqqPJTmGkk1VSAQA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;570&quot; height=&quot;367&quot; data-filename=&quot;스크린샷 2022-06-06 오전 11.26.59.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;netlify-build -&amp;gt; build로 바꿔줌으로서 next export 스크립트를 실행하지 않았고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build 명령에 해당하는 next build의 결과가 .next에 담기므로, .next로 publish dir로 변경하면 완료.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 getServerSideProps이 있는 페이지에서는 SSR이 적용된 페이지가 보여지고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 앱처럼 useEffect hook에서 data fetching을 하는 페이지에서는 기존대로 loader가 보이게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/netlify/netlify-plugin-nextjs/tree/v3#readme&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/netlify/netlify-plugin-nextjs/tree/v3#readme&lt;/a&gt;&lt;/p&gt;</description>
      <category>Error</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/141</guid>
      <comments>https://gobae.tistory.com/141#entry141comment</comments>
      <pubDate>Mon, 6 Jun 2022 11:38:11 +0900</pubDate>
    </item>
    <item>
      <title>[꿀팁] 백준 문제를 VSCode + JavaScript로 푼다면, snippets을 활용해보자.</title>
      <link>https://gobae.tistory.com/140</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;백준에서 JavaScript 풀이 시 어떻게 입력을 받는지 모른다면?&lt;/h3&gt;
&lt;figure id=&quot;og_1654169961912&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[백준] 10828번 스택 - JavaScript(NodeJS)&quot; data-og-description=&quot;문제 10828번: 스택 첫째 줄에 주어지는 명령의 수 N (1 &amp;le; N &amp;le; 10,000)이 주어진다.&amp;nbsp;둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. &quot; data-og-host=&quot;gobae.tistory.com&quot; data-og-source-url=&quot;https://gobae.tistory.com/21&quot; data-og-url=&quot;https://gobae.tistory.com/21&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/v9Dq9/hyOCG5QqPB/F0sx3kJQgMgYHkJyEdkaP1/img.png?width=300&amp;amp;height=216&amp;amp;face=0_0_300_216,https://scrap.kakaocdn.net/dn/8mZXV/hyOCCPUm35/ogrfyQXeMW8XEWeOGHZyOk/img.png?width=300&amp;amp;height=216&amp;amp;face=0_0_300_216,https://scrap.kakaocdn.net/dn/bipML0/hyOCENHI2g/9EAfJfUhX30sKKaZXPvkNk/img.png?width=1200&amp;amp;height=787&amp;amp;face=0_0_1200_787&quot;&gt;&lt;a href=&quot;https://gobae.tistory.com/21&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gobae.tistory.com/21&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/v9Dq9/hyOCG5QqPB/F0sx3kJQgMgYHkJyEdkaP1/img.png?width=300&amp;amp;height=216&amp;amp;face=0_0_300_216,https://scrap.kakaocdn.net/dn/8mZXV/hyOCCPUm35/ogrfyQXeMW8XEWeOGHZyOk/img.png?width=300&amp;amp;height=216&amp;amp;face=0_0_300_216,https://scrap.kakaocdn.net/dn/bipML0/hyOCENHI2g/9EAfJfUhX30sKKaZXPvkNk/img.png?width=1200&amp;amp;height=787&amp;amp;face=0_0_1200_787');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[백준] 10828번 스택 - JavaScript(NodeJS)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제 10828번: 스택 첫째 줄에 주어지는 명령의 수 N (1 &amp;le; N &amp;le; 10,000)이 주어진다.&amp;nbsp;둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gobae.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅을 참고하시길 바랍니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백준 문제를 풀 때 우리는!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1번.webp&quot; data-origin-width=&quot;2332&quot; data-origin-height=&quot;1500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1Jjfl/btrD18ES2mV/k9Ko7js4ZtriGkOKohbZC1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1Jjfl/btrD18ES2mV/k9Ko7js4ZtriGkOKohbZC1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1Jjfl/btrD18ES2mV/k9Ko7js4ZtriGkOKohbZC1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1Jjfl%2FbtrD18ES2mV%2Fk9Ko7js4ZtriGkOKohbZC1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;1500&quot; data-filename=&quot;1번.webp&quot; data-origin-width=&quot;2332&quot; data-origin-height=&quot;1500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 주어지는 input을 받는 처리를 함께 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대충 require('readline')~을 이용해서 &quot;input 처리를 하는 코드&quot;와 &quot;문제풀이가 담긴 solution 함수 코드&quot;를 함께 넣어서 제출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 문제를 풀고 백준 사이트에 제출하는 것 뿐 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬(내 컴퓨터)에 스크립트 파일로 저장해두거나, 깃허브에 올리거나 할 것인데,&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;그럼 이제 문제를 풀 때 마다, &quot;input 처리를 하는 코드&quot;를 열심히 복사해서 가져오는 작업이 필요하겠다. ㅎㅎㅎ&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분은 아마 VSCode에서 index.html 파일을 만들고 [! + enter]를 통해 html 기본 골격을 가져와본 경험이 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5CLTH/btrDRhoJAsq/TkluoT9ZEwkK2kir8yKtIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5CLTH/btrDRhoJAsq/TkluoT9ZEwkK2kir8yKtIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5CLTH/btrDRhoJAsq/TkluoT9ZEwkK2kir8yKtIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5CLTH%2FbtrDRhoJAsq%2FTkluoT9ZEwkK2kir8yKtIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;716&quot; height=&quot;194&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 기능을 사용자가 원하는 대로 만들 수는 없을까???.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;당연히 만들 수 있으니까 포스팅을 하는 중이다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금부터 나오는 내용을 한번 따라해서 에디터 명령을 등록해두면!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 더이상 다른 파일에서 백준 입력을 받는 코드들을 복사해서 가져오지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 window 유저라면 구글링을 추천하며, mac 환경을 기준으로 설명하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-02 오후 8.05.16.png&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;1511&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5ZA35/btrDOy6Ay29/2Hg2GYAXKKtPzoHSgj0wOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5ZA35/btrDOy6Ay29/2Hg2GYAXKKtPzoHSgj0wOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5ZA35/btrDOy6Ay29/2Hg2GYAXKKtPzoHSgj0wOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5ZA35%2FbtrDOy6Ay29%2F2Hg2GYAXKKtPzoHSgj0wOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;417&quot; data-filename=&quot;스크린샷 2022-06-02 오후 8.05.16.png&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;1511&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode에서 [cmd + shift + p] 를 누르고, &quot;snippets&quot; 을 검색하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1795&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cubhUa/btrDRN10YW6/Nbg5occtoaaSTCe9disYt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cubhUa/btrDRN10YW6/Nbg5occtoaaSTCe9disYt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cubhUa/btrDRN10YW6/Nbg5occtoaaSTCe9disYt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcubhUa%2FbtrDRN10YW6%2FNbg5occtoaaSTCe9disYt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;536&quot; height=&quot;1081&quot; data-origin-width=&quot;1795&quot; data-origin-height=&quot;1081&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 후 엔터를 누르면 이런 메뉴가 뜨는데, 그냥 편하게 전역에서 사용하도록 &quot;새 전역 코드 조각 파일&quot;을 선택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1811&quot; data-origin-height=&quot;915&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Js1Tc/btrDPjg15Lk/j6XGNL2gxkTl41KnKqLf20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Js1Tc/btrDPjg15Lk/j6XGNL2gxkTl41KnKqLf20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Js1Tc/btrDPjg15Lk/j6XGNL2gxkTl41KnKqLf20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJs1Tc%2FbtrDPjg15Lk%2Fj6XGNL2gxkTl41KnKqLf20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;915&quot; data-origin-width=&quot;1811&quot; data-origin-height=&quot;915&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 자동으로 snippets 파일이 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영어로 써있지만, 어렵지 않기에 대충 설명만 읽어봐도(설명을 안읽고 코드 부분만 봐도) 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crhuQc/btrDQEqUTl6/Pc4KF7u7KTzlEw6uzNGxck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crhuQc/btrDQEqUTl6/Pc4KF7u7KTzlEw6uzNGxck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crhuQc/btrDQEqUTl6/Pc4KF7u7KTzlEw6uzNGxck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrhuQc%2FbtrDQEqUTl6%2FPc4KF7u7KTzlEw6uzNGxck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;639&quot; height=&quot;248&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친절하게 설명하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1819&quot; data-origin-height=&quot;1301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBDdmV/btrDRX4AwHT/44NYchJVmdeKCMgq66RGi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBDdmV/btrDRX4AwHT/44NYchJVmdeKCMgq66RGi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBDdmV/btrDRX4AwHT/44NYchJVmdeKCMgq66RGi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBDdmV%2FbtrDRX4AwHT%2F44NYchJVmdeKCMgq66RGi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;501&quot; height=&quot;1301&quot; data-origin-width=&quot;1819&quot; data-origin-height=&quot;1301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 우선 양식에 맞춰서 등록할 스니펫을 가져왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흰색 글씨로 조금 못생기게 나왔긴 한데, VSCode의 단축키를 하나 설명하고 넘어가기 위해서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1770&quot; data-origin-height=&quot;1295&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bg5kvi/btrDMz5p0F4/0idKmDtldxnbPpcLCj4oN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bg5kvi/btrDMz5p0F4/0idKmDtldxnbPpcLCj4oN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bg5kvi/btrDMz5p0F4/0idKmDtldxnbPpcLCj4oN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbg5kvi%2FbtrDMz5p0F4%2F0idKmDtldxnbPpcLCj4oN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;1295&quot; data-origin-width=&quot;1770&quot; data-origin-height=&quot;1295&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[shift + opt]와 함께 마우스를 범위로 드래그해주면 위와 같이 여러 행들이 선택된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 [cmd + 좌측화살표]로 행들의 맨 왼쪽에 &quot;를 추가하고, [cmd + 우측화살표]로 맨 오른쪽에 &quot;, 를 추가하고 저장을 누르면,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;1296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUmGNv/btrDPBV0wrr/sxYVJY6SEEYT0hG2zikGXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUmGNv/btrDPBV0wrr/sxYVJY6SEEYT0hG2zikGXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUmGNv/btrDPBV0wrr/sxYVJY6SEEYT0hG2zikGXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUmGNv%2FbtrDPBV0wrr%2FsxYVJY6SEEYT0hG2zikGXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;402&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;1296&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짜잔. 이렇게 선택한 행들을 &quot;&quot;로 묶어줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2061&quot; data-origin-height=&quot;1311&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2GT6H/btrDQyEejBB/I0qM33NcbEKlaMxDojkLA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2GT6H/btrDQyEejBB/I0qM33NcbEKlaMxDojkLA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2GT6H/btrDQyEejBB/I0qM33NcbEKlaMxDojkLA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2GT6H%2FbtrDQyEejBB%2FI0qM33NcbEKlaMxDojkLA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;376&quot; data-origin-width=&quot;2061&quot; data-origin-height=&quot;1311&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자는 2개의 스니펫을 등록했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나는 one line으로 주어지는 input을 처리하기 위해서,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 하나는 multi lines로 주어지는 input을 처리하기 위해서.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deNNid/btrDRX4AJ0j/D6oeCikIFbkPfR5lRTUqkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deNNid/btrDRX4AJ0j/D6oeCikIFbkPfR5lRTUqkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deNNid/btrDRX4AJ0j/D6oeCikIFbkPfR5lRTUqkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeNNid%2FbtrDRX4AJ0j%2FD6oeCikIFbkPfR5lRTUqkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;594&quot; height=&quot;156&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 스니펫을 사용할 스크립트 파일에서 &quot;prefix&quot;에 등록한 약어를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;657&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkoDiy/btrDPs5Pdi3/GSMBNdq27tgAgBz2tkRpkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkoDiy/btrDPs5Pdi3/GSMBNdq27tgAgBz2tkRpkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkoDiy/btrDPs5Pdi3/GSMBNdq27tgAgBz2tkRpkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkoDiy%2FbtrDPs5Pdi3%2FGSMBNdq27tgAgBz2tkRpkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;657&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;657&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;multi-input 스니펫을 지정하고 엔터를 누른 뒤 결과이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다른 파일에서 복붙하지 않고, 백준 문제를 열심히 풀 준비가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Small Tips</category>
      <category>백준 자바스크립트 입력 템플릿</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/140</guid>
      <comments>https://gobae.tistory.com/140#entry140comment</comments>
      <pubDate>Thu, 2 Jun 2022 20:59:56 +0900</pubDate>
    </item>
    <item>
      <title>[함수형] 커링 Currying을 배워보자.</title>
      <link>https://gobae.tistory.com/139</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수형 시리즈&lt;/h3&gt;
&lt;figure id=&quot;og_1654076577168&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[함수형] 클로저 Closure에 대해 알아보자.&quot; data-og-description=&quot;클로저 클로저란, 내부 함수가 함수가 참조하고 있는 주변 환경에 접근할 수 있는 것을 의미한다. MDN에서는, 클로저는 독립적인 (자유) 변수를 가리키는 함수이다. 클로저 안에 정의된 함수는 만&quot; data-og-host=&quot;gobae.tistory.com&quot; data-og-source-url=&quot;https://gobae.tistory.com/113&quot; data-og-url=&quot;https://gobae.tistory.com/113&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bUcRoS/hyOBxu2A9a/hOBIE1QpFqPdke7w92jSy1/img.png?width=800&amp;amp;height=267&amp;amp;face=0_0_800_267,https://scrap.kakaocdn.net/dn/ifk5y/hyOCKztI1Q/mSq4ISk5FKrdg124BrnmlK/img.png?width=800&amp;amp;height=267&amp;amp;face=0_0_800_267,https://scrap.kakaocdn.net/dn/cZEDXA/hyOCz5NrhS/sfXOqUR4ugJVx9OKrrofX0/img.png?width=1414&amp;amp;height=500&amp;amp;face=0_0_1414_500&quot;&gt;&lt;a href=&quot;https://gobae.tistory.com/113&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gobae.tistory.com/113&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bUcRoS/hyOBxu2A9a/hOBIE1QpFqPdke7w92jSy1/img.png?width=800&amp;amp;height=267&amp;amp;face=0_0_800_267,https://scrap.kakaocdn.net/dn/ifk5y/hyOCKztI1Q/mSq4ISk5FKrdg124BrnmlK/img.png?width=800&amp;amp;height=267&amp;amp;face=0_0_800_267,https://scrap.kakaocdn.net/dn/cZEDXA/hyOCz5NrhS/sfXOqUR4ugJVx9OKrrofX0/img.png?width=1414&amp;amp;height=500&amp;amp;face=0_0_1414_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[함수형] 클로저 Closure에 대해 알아보자.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;클로저 클로저란, 내부 함수가 함수가 참조하고 있는 주변 환경에 접근할 수 있는 것을 의미한다. MDN에서는, 클로저는 독립적인 (자유) 변수를 가리키는 함수이다. 클로저 안에 정의된 함수는 만&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gobae.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 클로저 포스팅에 이어 이번에는 커링이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커링?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2번.webp&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0B5Dq/btrD1g4eHbO/IChDYF2Fj4qHBrXrv9ljVK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0B5Dq/btrD1g4eHbO/IChDYF2Fj4qHBrXrv9ljVK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0B5Dq/btrD1g4eHbO/IChDYF2Fj4qHBrXrv9ljVK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0B5Dq%2FbtrD1g4eHbO%2FIChDYF2Fj4qHBrXrv9ljVK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;290&quot; data-filename=&quot;2번.webp&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진 속 연산을 이해할 수 있는가?&lt;br /&gt;&lt;b&gt;&lt;u&gt;이해할 수 있다면 이 포스팅은 스킵하면 된다.&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커링(Currying)은 함수형 프로그래밍에서 등장하는 필수 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면, '함수를 반환하는 함수'이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커링이 왜 사용될까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수의 재활용을 위해서
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원하는 함수들을 조합해서 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하나 이상의 인수의 함수를, 하나의 인수를 받는 함수로 축소할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그래서 더 가벼운 함수 제작이 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 함수의 재활용이란 무엇일까?&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;    function add(a,b){
        return a+b;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 add 함수를 재활용 하려면?&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;    function addTwo(a){
        return add(a, 2);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 2 + @를 해주는 addTwo 함수를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 add3, add4, add5 ... 등의 여러 함수가 필요하다고 하자.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;    function addTwo(a){
        return add(a, 2);
    }
    function addThree(a){
        return add(a, 3);
    }
    function addFour(a){
        return add(a, 4);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 add 함수를 재활용하긴 하지만, 뭔가 이상하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;여기에 커링을 적용해보자.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;    function addX(x){
      return function(a){
        return add(a, x);
      }
    }

    const addTwo = addX(2);
    const addThree = addX(3);
    const addFour = addX(4);

    addTwo(2) // 2+2 = 4
    addThree(5) // 3+5 = 8
    addFour(1) // 4+1 = 5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 addX 함수를 이용해서 여러 파생 함수들을 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addX 함수는 이렇게 동작한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인자 x를 받아서 익명함수를 반환한다. 그 함수는 function(a){return add(a,x);} 형태이다.&lt;/li&gt;
&lt;li&gt;반환받은 값인 함수는 인자 a를 요구한다. a를 넣어주면 add(a,x)의 실행 결과를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 함수 실행을 위한 인자를 한번에 받지 않고, 여러 차례에 나눠서 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시를 화살표 함수로 나타내면,&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    function addX(x){
      return function(a){
        return add(a, x);
      }
    }

    const addX = x =&amp;gt; a =&amp;gt; add(a,x);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 더 간단해진다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수를 인자로 받는 커링함수 만들어보기.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서는 함수도 값이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그러므로 함수의 인자로 또 다른 함수도 받을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;    function curry(fn){
      return function(a){
        return function(b){
          return fn(a,b);
        }
      }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 함수를 어떻게 써야할지가 대충 보일 것인데,&lt;br /&gt;정확히 위에서 선언했던 addX와 동일하게도 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;    function add(a,b){
      return a+b;
    }

    const addX = curry(add);
    const addTwo = addX(2);
    console.log(addTwo(5)) // 5+2 = 7&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 자체를 인자로 받아서 활용할 수 있는 것을 알게 되면, 코드를 작성하는 스타일이 더 다양해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 글 맨 앞에서 다뤘던 괴상한 화살표함수의 모습에 대해서 왜 동작하는지를 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const curry = fn =&amp;gt; fn2 =&amp;gt; a =&amp;gt; b =&amp;gt; fn(a,b)+fn2(a,b)

    // 이 함수를 뜯어보면 다음과 같다.

    function curry(fn){
      return function(fn2){
        return function(a){
          return function(b){
            return fn(a,b) + fn2(a,b);
          }
        }
      }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;add, multiply 함수를 차례대로 생성해서 방정식을 만들어보자.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    function add(a,b){
      return a+b;
    }

    function multiply(a,b){
      return a*b;
    }

    const fnIsAdd = curry(add);
    const fnIsAddFn2IsMultiply = fnIsAdd(multiply);
    const fnIsAddFn2IsMultiplyAis3 = fnIsAddFn2IsMultiply(3);
    const add3and5Plusmultiply3and5 = fnIsAddFn2IsMultiplyAis3(5);
    console.log(add3and5Plusmultiply3and5) // (3+5) + (3*5) = 8+15 = 23

    const oneline = curry(add)(multiply)(3)(5);
    console.log(oneline) // (3+5) + (3*5) = 8+15 = 23&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 각각의 절차를 나타내봤고, 한번에 작성한 online 변수를 작성해봤다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커링 응용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 커링의 가벼운 응용을 보고 마칠 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방금 인자로 함수도 받을 수 있다는 것을 알았다.&lt;br /&gt;그렇다면 정말 '다양한 함수들을 조합'하는 길이 열린 셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 조금 더 간결하게 function 키워드 없이 화살표 함수로 커링함수를 표현하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방정식 4x * (x+2)을 수행할 함수를 만들어보자.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const add = (a,b) =&amp;gt; a+b;
    const multiply = (a,b) =&amp;gt; a*b;

    const addX = x =&amp;gt; a =&amp;gt; add(a, x);
    const addTwo = addX(2);

    const multiplyX = x =&amp;gt; a =&amp;gt; multiply(a, x);
    const multiplyFour = multiplyX(4);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 커링을 이용해 addTwo, multiplyFour 함수를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;    const compose = fn =&amp;gt; fn2 =&amp;gt; x =&amp;gt; fn2(x) * fn(x);
    const equation = compose(addTwo)(multiplyFour);
    equation(10) // (4 * 10) * (10 + 2) = 40 * 12 = 480&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;equation 함수에 넣는 값이 x가 되어 4x(x+2)의 다양한 값에 대한 결과를 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 추가적인 요구사항이 생겨서, 5x(x+2)의 결과를 반환하는 함수가 필요하다면?&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;    const multiplyFive = multiplyX(5);
    const equaion2 = compose(addTwo)(multiplyFive);
    equation2(10) // (5 * 10) * (10 + 2) = 600&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;마지막으로 위에서 선언한 equation 함수를 생성하면서 중복되는 부분을 제거하면,&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;    const addTwo = addX(2);
    const addFour = addX(4);
    const multiplyFour = multiplyX(4);
    const multiplyFive = multiplyX(5);

    const compose = fn =&amp;gt; fn2 =&amp;gt; x =&amp;gt; fn2(x) * fn(x);
    const composeAddTwo = compose(addTwo);

    const equation1 = composeAddTwo(multiplyFour); // 4x(x+2)
    const equation2 = composeAddTwo(multiplyFive); // 5x(x+2)
    const equation3 = composeAddTwo(addFour) // (x+4)(x+2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 인자로 받은 함수들을 교체해주면서, 유사한 함수들을 여러개 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아주 간단한 예시들을 통해 여러 함수들을 유용하게 조합할 수 있는 커링을 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커링을 통한 함수 합성은 어려워지려면 한도 끝도 없이 어려워 질 수 있기 때문에...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 프로그래밍을 프로젝트에 적용하면서 함수 조합 및 합성을 고려해보면 커링에 대해 진하게 느끼지 않을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(사실 빡세게 머리를 굴려도 간단한 재활용 함수들을 만들기조차 힘들더라..ㅎㅎ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 커링</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/139</guid>
      <comments>https://gobae.tistory.com/139#entry139comment</comments>
      <pubDate>Wed, 1 Jun 2022 18:39:09 +0900</pubDate>
    </item>
    <item>
      <title>[에러] Safari invalid Date (or NaN) 해결하기</title>
      <link>https://gobae.tistory.com/138</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;에러&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 없이 json 데이터 기반으로 프로젝트를 수행하면서&lt;br /&gt;날짜 데이터를 string 타입의 &lt;b&gt;&quot;yyyy-mm-dd hh:mm:ss&quot;&lt;/b&gt; 로 보관하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) date : &quot;2022-05-27 22:10:18&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문자열을 new Date의 인자로 넣어주면, 바로 날짜 객체로 변환할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;아무튼 이렇게 변환한 날짜를 기반으로 getFullYear, getMonth 등을 하며 이러쿵 저러쿵 했는데,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3번.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kXp3q/btrDXoPYuFM/jbFx66Du0kwnmQd52MLUck/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kXp3q/btrDXoPYuFM/jbFx66Du0kwnmQd52MLUck/img.webp&quot; data-alt=&quot;사파리에선 오류가 난다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kXp3q/btrDXoPYuFM/jbFx66Du0kwnmQd52MLUck/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkXp3q%2FbtrDXoPYuFM%2FjbFx66Du0kwnmQd52MLUck%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;135&quot; data-filename=&quot;3번.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사파리에선 오류가 난다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사파리에서는 new Date()메서드가 &quot;yyyy-mm-dd hh:mm:ss&quot; 입력을 취급하지 않나보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리하여 데이터를 &lt;b&gt;&quot;yyyy/mm/dd hh:mm:ss&quot;&lt;/b&gt;로 바꿔보니,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크롬과 사파리 둘 다 new Date 인자로 활용할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYUwKk/btrDjE6QtWw/294wwMcGXoDztNtRj01yw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYUwKk/btrDjE6QtWw/294wwMcGXoDztNtRj01yw1/img.png&quot; data-alt=&quot;정상 출력된 Date 객체&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYUwKk/btrDjE6QtWw/294wwMcGXoDztNtRj01yw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYUwKk%2FbtrDjE6QtWw%2F294wwMcGXoDztNtRj01yw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;388&quot; height=&quot;272&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;정상 출력된 Date 객체&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Error</category>
      <category>safari Date format NaN</category>
      <category>safari invalid date error</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/138</guid>
      <comments>https://gobae.tistory.com/138#entry138comment</comments>
      <pubDate>Fri, 27 May 2022 22:24:13 +0900</pubDate>
    </item>
    <item>
      <title>[에러] ** and ** are entirely different commit histories. 해결하기</title>
      <link>https://gobae.tistory.com/137</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;에러 내용&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4번.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUP3ea/btrD0rroOFr/r4kLFwjm2LvcvyCNPm0QsK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUP3ea/btrD0rroOFr/r4kLFwjm2LvcvyCNPm0QsK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUP3ea/btrD0rroOFr/r4kLFwjm2LvcvyCNPm0QsK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUP3ea%2FbtrD0rroOFr%2Fr4kLFwjm2LvcvyCNPm0QsK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;387&quot; data-filename=&quot;4번.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;387&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;There isn't anything to compare&lt;br /&gt;main and mybranch are entirely different commit histories.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 이 키워드로 들어왔다면, 다른 프로젝트를 가져와서 이어서 작업을 완료하고..! PR을 보내려다가 응? 한 경우일 듯 하다.&lt;br /&gt;&lt;s&gt;사실 내가 그랬다..ㅎㅎ &lt;/s&gt; 과제 전형에 응시하면서 열심히 작업하고 PR을 보내려고 하니, PR 생성이 활성화되지 않더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR을 보내기 전까지 git push origin *** 까지는 가능했어서, 과제를 마치기 전까지도 커밋이 끊긴 것도 몰랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 현상을 경험하신 &lt;a href=&quot;https://jihyewoo.tistory.com/195&quot;&gt;https://jihyewoo.tistory.com/195&lt;/a&gt; 블로그 포스팅을 참고했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제와 해결&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lylYK/btrDk6HZrr6/BGTHMibYkbxbkLXchiCygk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lylYK/btrDk6HZrr6/BGTHMibYkbxbkLXchiCygk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lylYK/btrDk6HZrr6/BGTHMibYkbxbkLXchiCygk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlylYK%2FbtrDk6HZrr6%2FBGTHMibYkbxbkLXchiCygk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;612&quot; height=&quot;352&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이 화면은 vscode에서 git graph 확장파일을 다운받으면 된다!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림처럼 맨 처음 origin/main 브랜치의 커밋과 내가 생성한 master 브랜치의 커밋이 끊겨있다.&lt;br /&gt;그래서 master 브랜치에서 분기한 task 브랜치의 변경 내용들이 github에 remote branch로 존재하지만,&lt;br /&gt;task 브랜치와 origin/main 브랜치가 연결점이 없다보니, 위에서 보인 에러를 보여주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 간단하다. master 브랜치와 origin/main 브랜치를 연결하면 main과 task도 연결되므로 끝날 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;    git switch master 
    // 마스터 브랜치로 이동(내 로컬에서는 마스터 브랜치 최근 커밋이 &quot;CRA로 리액트 프로젝트 생성&quot;이었다.)
    // 만약 마스터 브랜치의 최근 커밋이 끊긴 커밋이 아니라면, 끊긴 커밋 위치로 이동하면 된다.
    git branch bridge // 끊긴 커밋 위치에서 새로운 브랜치 bridge를 만든다.
    git switch bridge // 새로운 브랜치 bridge로 이동한다.
    git rebase origin/main // 연결할 커밋이 있는 브랜치를 지정한다.

    // 이 때 conflict가 발생할 수도 있다. 적절히 처리하면서 기존 commit에 반영해주고,
    git switch master // master 브랜치로 이동
    git rebase bridge // bridge를 master 브랜치에 rebase한다.
    git push origin main // 깃허브의 main 브랜치에 현재 커밋을 날린다. 그럼 끊긴 부분이 연결된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, 과제를 내야하는 상황이라 origin/main 브랜치를 건드리면 안됐지만..&lt;br /&gt;(main 브랜치를 건드리지 않고 별도의 브랜치 A에서 A -&amp;gt; main 으로 PR을 날렸어야 했다.)&lt;br /&gt;이게 최선이었다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그래서 결국 main 브랜치에 &quot;CRA로 리액트 프로젝트 설정&quot; 이라는 새로운 커밋이 반영되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후로는 origin main 브랜치와 local master 브랜치가 연결되었으므로,&lt;br /&gt;local master 브랜치를 기반으로 하는 task 브랜치는 main 브랜치에 PR을 날릴 수 있었다.&lt;/p&gt;</description>
      <category>Error</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/137</guid>
      <comments>https://gobae.tistory.com/137#entry137comment</comments>
      <pubDate>Fri, 27 May 2022 22:04:16 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 양궁대회 - 자바스크립트</title>
      <link>https://gobae.tistory.com/136</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1651845277668&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코딩테스트 연습 - 양궁대회&quot; data-og-description=&quot;문제 설명 카카오배 양궁대회가 열렸습니다. 라이언은 저번 카카오배 양궁대회 우승자이고 이번 대회에도 결승전까지 올라왔습니다. 결승전 상대는 어피치입니다. 카카오배 양궁대회 운영위원&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/92342&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/92342&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xdEI8/hyOjbc2Vj2/AE1Yr6eExBlyScjY9MtEp0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/8lHjP/hyOhA6AXOU/VevbAo13P7yJ4rAJiayPu0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/BDBMQ/hyOhArZLWM/TNG6kjs2WiafpHGspr64N1/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/92342&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/92342&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xdEI8/hyOjbc2Vj2/AE1Yr6eExBlyScjY9MtEp0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/8lHjP/hyOhA6AXOU/VevbAo13P7yJ4rAJiayPu0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/BDBMQ/hyOhArZLWM/TNG6kjs2WiafpHGspr64N1/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - 양궁대회&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제 설명 카카오배 양궁대회가 열렸습니다. 라이언은 저번 카카오배 양궁대회 우승자이고 이번 대회에도 결승전까지 올라왔습니다. 결승전 상대는 어피치입니다. 카카오배 양궁대회 운영위원&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카카오 기출문제이다.&lt;br /&gt;문제도 상당히 길고 Test case 8, 18이 쉽지 않았던 문제라 level2에서도 상당한 난이도로 느껴진다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function solution(n, info) {
  let answer = new Array(11).fill(0);
  let answerDiff = Number.MIN_SAFE_INTEGER;

  function isSmallScoreArr(scoreArr) {
    for (let i = 10; i &amp;gt;= 0; i--) {
      if (scoreArr[i] &amp;gt; answer[i]) return true;
      else if (scoreArr[i] &amp;lt; answer[i]) return false;
    }
  }

  function dfs(L, idx, ryanInfo) {
    if (idx === 10 &amp;amp;&amp;amp; L &amp;lt; n) {
      ryanInfo[10] = n - L;
      L = n;
    }

    if (L === n) {
      let appeachScore = 0;
      let ryanScore = 0;

      ryanInfo.forEach((elem, i) =&amp;gt; {
        if (!elem &amp;amp;&amp;amp; !info[i]) return;

        const score = 10 - i;
        if (info[i] - elem &amp;gt;= 0) appeachScore += score;
        else if (info[i] - elem &amp;lt; 0) ryanScore += score;
      });

      const diff = ryanScore - appeachScore;

      if (answerDiff &amp;lt; diff) {
        answerDiff = diff;
        answer = ryanInfo;
      } else if (answerDiff === diff) {
        if (isSmallScoreArr(ryanInfo)) answer = ryanInfo;
      }
      return;
    }

    const appeachArrowLength = info[idx];

    if (n - L &amp;gt;= appeachArrowLength + 1) {
      ryanInfo[idx] = appeachArrowLength + 1;
      dfs(L + appeachArrowLength + 1, idx + 1, [...ryanInfo]);
      ryanInfo[idx] = 0;
    }
    dfs(L, idx + 1, [...ryanInfo]);
  }

  dfs(0, 0, new Array(11).fill(0));

  if (answerDiff &amp;lt;= 0) return [-1];
  return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5번.webp&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;1090&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E6Q5A/btrD0s4VR3e/lpvoogrSYxz5QKp5RmvTuK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E6Q5A/btrD0s4VR3e/lpvoogrSYxz5QKp5RmvTuK/img.webp&quot; data-alt=&quot;출처 : 프로그래머스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E6Q5A/btrD0s4VR3e/lpvoogrSYxz5QKp5RmvTuK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE6Q5A%2FbtrD0s4VR3e%2FlpvoogrSYxz5QKp5RmvTuK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;505&quot; data-filename=&quot;5번.webp&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;1090&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : 프로그래머스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제의 조건&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이언, 어피치가 총 n발의 화살을 쏜다.&lt;br /&gt;화살이 맞춘 기록은 배열 0~10 인덱스에 기록되고, 0번 인덱스부터 10점, 10번 인덱스는 0점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이언이 전년도 우승자라, 불리함이 있다.&lt;br /&gt;라이언은 어피치보다 많은 화살을 명중시켜야 해당 인덱스 점수를 가져간다.&lt;br /&gt;반면 맞춘 화살 수가 어피치 &amp;gt;= 라이언 이라면 어피치가 점수를 가져간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;0번 인덱스에 대해 라이언=2, 어피치=1이면 라이언이 10점을 가져간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;0번 인덱스에 대해 라이언=1, 어피치=1이면 어피치가 10점을 가져간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 쏘지 않은 점수에 대해서는, 아무도 가져가지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 배열 info는 어피치가 맞춘 화살의 인덱스 정보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이언은 얻은 점수의 총합이 어피치보다 많아야만 우승할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, 모든 경우에 대해 어피치의 총 점수 &amp;gt;= 라이언의 총 점수라면 어피치가 우승하고 [-1]을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 라이언이 어피치를 가장 큰 점수차로 누르고 우승할 때의 라이언의 화살 정보를 반환해야 한다.&lt;br /&gt;&lt;b&gt;이 때 라이언이 가장 큰 점수 차이로 우승하는 방법이 여러 가지라면, 가장 낮은 점수를 더 많이 맞힌 경우를 반환한다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;이 정보를 해결하지 않으면 테스트케이스 8, 18번에서 실패하게 된다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 범위&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 &amp;lt;= n &amp;lt;= 10&lt;br /&gt;info.length = 11&lt;br /&gt;info의 i번째 원소 = 10-i 점을 맞춘 갯수&lt;br /&gt;라이언은 n발의 화살을 다 쏴야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;풀이 방향&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열이 11개, n이 최대 10개이므로 완전탐색을 수행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n과 배열 인덱스를 기반으로 하는 dfs로 풀이할 계획이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, dfs로 화살을 하나씩 쏴도 된다.&lt;br /&gt;그러나 특정 인덱스에 어피치보다 많은 화살을 쏴서 점수를 탈취할 것인지, 아니면 아예 안쏠 것인지를 따지는게 더 낫겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리하여 기본적으로 현재 인덱스 idx에서 다음과 같은 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 라이언이 어피치보다 많은 화살을 쏜다.&lt;br /&gt;2. 라이언이 화살을 쏘지 않고 넘어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 경우를 가지고 dfs를 수행해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서, &lt;b&gt;10번 인덱스를 판단할 때&lt;/b&gt;를 주의해야 한다.&lt;br /&gt;&lt;b&gt;&quot;라이언은 모든 화살을 쏴야 하기 때문에&quot;&lt;/b&gt; 만약 10번 인덱스를 판단할 때, 화살이 남아있다면 모두 쏴버리자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 정답 처리를 할 때는 다음의 과정을 따른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 인덱스 별로 (어피치 화살 갯수 - 라이언 화살 갯수) 값을 구한다.&lt;br /&gt;해당 값이 0보다 크거나 같다면 어피치의 점수이며, 0보다 작다면 라이언의 값이다.&lt;br /&gt;어피치와 라이언이 모두 화살을 쏘지 않아도, 0-0=0으로 어피치의 점수가 될 수 있다. 이것을 제외한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구한 어피치의 최종 점수와 라이언 최종 점수차이를 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기록해둔 가장 큰 차이보다 방금 구한 점수차이가 크다면 정답 후보(=라이언이 쏜 화살의 배열)를 갱신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점수차이가 같다면 추가적으로 &lt;b&gt;가장 낮은 점수를 더 많이 맞힌 경우를 판단&lt;/b&gt;해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답 후보 배열에 있는 기존 배열과, 현재 라이언이 쏜 화살의 배열 비교한다.&lt;br /&gt;이때는 각 배열의 맨 마지막 원소부터 탐색하면서, 원소가 큰 인덱스를 먼저 가진 배열이 정답 후보 배열이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 위의 설명들을 반영한 주석을 추가한 코드를 첨부하였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주석을 첨부한 코드&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function solution(n, info) { 
  let answer = new Array(11).fill(0); // 정답 후보 배열
  let answerDiff = Number.MIN_SAFE_INTEGER; // 라이언의 총 점수 - 어피치의 총 점수

  function isSmallScoreArr(scoreArr) {
    for (let i = 10; i &amp;gt;= 0; i--) {
      if (scoreArr[i] &amp;gt; answer[i]) return true;
      else if (scoreArr[i] &amp;lt; answer[i]) return false;
    }
  } // 정답 후보 배열을 구하기 위해 인덱스 마지막을 비교하는 함수

  function dfs(L, idx, ryanInfo) {
    if (idx === 10 &amp;amp;&amp;amp; L &amp;lt; n) {
      ryanInfo[10] = n - L;
      L = n;
    } // 판단할 인덱스가 10이면 남은 화살을 모두 소진하면서, L값을 갱신한다.

    if (L === n) {
      let appeachScore = 0;
      let ryanScore = 0;

      ryanInfo.forEach((elem, i) =&amp;gt; {
        if (!elem &amp;amp;&amp;amp; !info[i]) return;

        const score = 10 - i;
        if (info[i] - elem &amp;gt;= 0) appeachScore += score;
        else if (info[i] - elem &amp;lt; 0) ryanScore += score;
      }); // 라이언, 어피치의 총 점수을 구한다.

      const diff = ryanScore - appeachScore;

      if (answerDiff &amp;lt; diff) {
        answerDiff = diff;
        answer = ryanInfo; 
      } else if (answerDiff === diff) {
        if (isSmallScoreArr(ryanInfo)) answer = ryanInfo;
      } // 차이가 더 커졌다면 바로 정답후보배열을 갱신하고, 차이가 같다면 더 작은 점수를 많이 쏜 경우를 정답후보로 갱신한다.
      return;
    }

    const appeachArrowLength = info[idx]; // 인덱스에 현재 어피치가 쏜 화살의 갯수.

    if (n - L &amp;gt;= appeachArrowLength + 1) {
      ryanInfo[idx] = appeachArrowLength + 1;
      dfs(L + appeachArrowLength + 1, idx + 1, [...ryanInfo]);
      ryanInfo[idx] = 0;
    } // 어피치가 화살을 쏜 것 보다 1개 더 많은 화살을 쏴서 점수를 뺏어온다.
    dfs(L, idx + 1, [...ryanInfo]); // 혹은 현재 인덱스의 점수를 포기한다.
  }

  dfs(0, 0, new Array(11).fill(0));

  if (answerDiff &amp;lt;= 0) return [-1]; // 차이가 0 이하라면 어피치의 승리이므로 [-1] 반환
  return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DS &amp;amp; Algorithm/programmers</category>
      <category>카카오 코딩테스트 양궁대회 nodeJS</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/136</guid>
      <comments>https://gobae.tistory.com/136#entry136comment</comments>
      <pubDate>Fri, 6 May 2022 22:54:26 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 17406번 배열 돌리기 4 - 자바스크립트</title>
      <link>https://gobae.tistory.com/135</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1650014307226&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;17406번: 배열 돌리기 4&quot; data-og-description=&quot;크기가 N&amp;times;M 크기인 배열 A가&amp;nbsp;있을때, 배열 A의 값은 각 행에 있는 모든 수의 합 중 최솟값을 의미한다.&amp;nbsp;배열 A가 아래와 같은 경우 1행의 합은 6, 2행의 합은 4, 3행의 합은 15이다. 따라서, 배열 A의 &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/17406&quot; data-og-url=&quot;https://www.acmicpc.net/problem/17406&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KgEoF/hyN38Hu0At/beOlvuoCofOVNoeLYStfbk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17406&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/17406&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KgEoF/hyN38Hu0At/beOlvuoCofOVNoeLYStfbk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;17406번: 배열 돌리기 4&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;크기가 N&amp;times;M 크기인 배열 A가&amp;nbsp;있을때, 배열 A의 값은 각 행에 있는 모든 수의 합 중 최솟값을 의미한다.&amp;nbsp;배열 A가 아래와 같은 경우 1행의 합은 6, 2행의 합은 4, 3행의 합은 15이다. 따라서, 배열 A의&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1650014442790&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function sol(input) {
  const [N, M, K] = input[0].split(' ').map(Number);
  let boards;
  let answer = Number.MAX_SAFE_INTEGER;
  const rotateOperations = input
    .slice(1 + N)
    .map((str) =&amp;gt; str.split(' ').map(Number));

  dfs(0, []);

  return answer;

  function dfs(L, orders) {
    if (orders.length === K) {
      boards = input.slice(1, 1 + N).map((row) =&amp;gt; row.split(' ').map(Number));

      orders.forEach((order) =&amp;gt;
        rotate2DMatrixBy1Step(...rotateOperations[order])
      );

      boards.forEach((row) =&amp;gt; {
        const min = row.reduce((acc, val) =&amp;gt; acc + val, 0);
        answer = Math.min(answer, min);
      });
      return;
    }

    for (let i = 0; i &amp;lt; K; i++) {
      if (orders.includes(i)) continue;
      dfs(L + 1, [...orders, i]);
    }
  }

  function rotate2DMatrixBy1Step(r, c, s) {
    const n = 2 * s + 1;
    const startR = r - s - 1;
    const startC = c - s - 1;

    const result = Array.from({ length: n }, () =&amp;gt; new Array(n).fill(0));
    for (let i = 0; i &amp;lt; s; i++) {
      let sr = i,
        sc = i;
      for (let j = 0; j &amp;lt; 4; j++) {
        while (sr === i &amp;amp;&amp;amp; sc &amp;gt;= i &amp;amp;&amp;amp; sc &amp;lt; n - 1 - i) {
          result[sr][sc + 1] = boards[startR + sr][startC + sc];
          sc++;
        }

        while (sr &amp;gt;= i &amp;amp;&amp;amp; sr &amp;lt; n - 1 - i &amp;amp;&amp;amp; sc === n - 1 - i) {
          result[sr + 1][sc] = boards[startR + sr][startC + sc];
          sr++;
        }

        while (sr === n - 1 - i &amp;amp;&amp;amp; sc &amp;gt; i &amp;amp;&amp;amp; sc &amp;lt;= n - 1 - i) {
          result[sr][sc - 1] = boards[startR + sr][startC + sc];
          sc--;
        }

        while (sr &amp;gt; i &amp;amp;&amp;amp; sr &amp;lt;= n - 1 - i &amp;amp;&amp;amp; sc === i) {
          result[sr - 1][sc] = boards[startR + sr][startC + sc];
          sr--;
        }
      }
    }
    result[s][s] = boards[startR + s][startC + s];

    for (let i = 0; i &amp;lt; n; i++) {
      for (let j = 0; j &amp;lt; n; j++) {
   		 boards[startR + i][startC + j] = result[i][j];
      }
    }
  }
}

const input = [];
require('readline')
  .createInterface(process.stdin, process.stdout)
  .on('line', (line) =&amp;gt; {
    input.push(line);
  })
  .on('close', () =&amp;gt; {
    console.log(sol(input));
    process.exit();
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 해결하기 위한 절차를 파악하고, 사용할 함수를 구현해서 모두 테스트하면 쉽게 풀리는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 위해서는, 2가지 함수가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 회전 연산의 순서를 정하는 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 회전을 수행하는 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 하나씩 어떻게 구현할지 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;회전 연산의 순서를 정하는 함수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회전 연산 갯수 K는 1~6까지의 정수다. 연산의 &quot;순서&quot;를 정해야 하므로 K개의 순열을 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 dfs를 통해서 순서 배열의 경우의 수를 구하고, 순서 배열의 원소의 개수가 K개일 때마다, 회전을 순서대로 수행하고 행 합계의 최솟값을 구하면 되겠다. 우리는&amp;nbsp;모든 경우의 수에 대해서 최솟값을 구해야 하므로 dfs를 끝까지 진행하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;회전을 수행하는 함수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(r,c) = 회전의 기준이 될 중앙 위치, s = 회전을 수행할 한쪽 크기이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(r-s, c-s)가 회전할 행렬의 왼쪽위 시작점이고, (r+s, c+s)가 회전할 행렬의 오른쪽 아래 끝점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce9JZu/btrD1gDbeu1/0Ct9DSALBRgh48ocHdhLak/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce9JZu/btrD1gDbeu1/0Ct9DSALBRgh48ocHdhLak/img.webp&quot; data-alt=&quot;시계방향으로 1칸씩 이동&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce9JZu/btrD1gDbeu1/0Ct9DSALBRgh48ocHdhLak/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce9JZu%2FbtrD1gDbeu1%2F0Ct9DSALBRgh48ocHdhLak%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;295&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시계방향으로 1칸씩 이동&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회전을 수행할 배열을 돌릴 때, 시계방향으로 1칸씩 돌린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회전할 배열은 2*s+1의 크기를 가지며, 항상 홀수개이고 s번 회전을 수행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 마지막 (r,c)는 배열의 중앙이므로 회전의 대상이 되지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nFeA8/btrzuQqdp4u/6EDAKVGTZ1kHAOo8HUKNjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nFeA8/btrzuQqdp4u/6EDAKVGTZ1kHAOo8HUKNjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nFeA8/btrzuQqdp4u/6EDAKVGTZ1kHAOo8HUKNjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnFeA8%2FbtrzuQqdp4u%2F6EDAKVGTZ1kHAOo8HUKNjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;203&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림과 같이 회전을 2번(= s) 수행하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2회의 for문에서 윗변, 아랫변, 좌우측면 변을 이동시키는 4개의 연산을 수행하면 될 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러쿵저러쿵해서 이동시킬 행과 열 위치를 기준으로 모두 이동시켜보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서, 회전연산 K개에 대해서 K! 개의 경우의 수를 구하고, 각 경우의 수마다 정해진 순서대로 회전연산을 수행한 결과 배열의 행의 합 최솟값을 갱신하면 정답을 구할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;간단한 주석 첨부&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650015693817&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function sol(input) {
  const [N, M, K] = input[0].split(' ').map(Number);
  let boards;
  let answer = Number.MAX_SAFE_INTEGER;
  const rotateOperations = input
    .slice(1 + N)
    .map((str) =&amp;gt; str.split(' ').map(Number)); // 회전연산을 담아둔다.

  dfs(0, []);

  return answer;
	// 코드 실행은 여기서 끝난다. 밑에부터는 구현한 함수다.
    
  function dfs(L, orders) {
    if (orders.length === K) {
      boards = input.slice(1, 1 + N).map((row) =&amp;gt; row.split(' ').map(Number));
	  // 경우의 수의 원소 갯수가 K개면 회전 연산을 수행할 것이므로, 배열 A를 복사한다.
      
      orders.forEach((order) =&amp;gt;
        rotate2DMatrixBy1Step(...rotateOperations[order])
      ); // 경우의수 orders의 원소를 순회하며 회전 연산을 수행한다.

      boards.forEach((row) =&amp;gt; {
        const min = row.reduce((acc, val) =&amp;gt; acc + val, 0);
        answer = Math.min(answer, min);
      }); // 배열 A의 행의 합 최솟값을 갱신한다.
      return;
    }

    for (let i = 0; i &amp;lt; K; i++) {
      if (orders.includes(i)) continue;
      dfs(L + 1, [...orders, i]);
    }
  }

  function rotate2DMatrixBy1Step(r, c, s) {
    const n = 2 * s + 1;
    const startR = r - s - 1;
    const startC = c - s - 1;
    // startR, startC는 배열A에서 회전을 시작할 행과 열이다.
	// 순서가 헷갈려서, 새로운 배열 result를 n*n 크기로 선언했다.
    // 여기에 회전 결과를 담아두고, board로 복사할 예정이다.
    
    const result = Array.from({ length: n }, () =&amp;gt; new Array(n).fill(0));
    for (let i = 0; i &amp;lt; s; i++) {
      let sr = i,
        sc = i;
      // 회전 연산 당 s회만큼 반복해야 배열 내부를 모두 회전시킬 수 있다.
      // sr,sc는 시작할 지점이며, boards에서 실행해야 하므로 startR, startC와 연계한다.
      // 또한 result는 n*n크기이므로 (sr,sc)에 회전된 모습을 저장하게 된다.
      for (let j = 0; j &amp;lt; 4; j++) {
        while (sr === i &amp;amp;&amp;amp; sc &amp;gt;= i &amp;amp;&amp;amp; sc &amp;lt; n - 1 - i) {
          result[sr][sc + 1] = boards[startR + sr][startC + sc];
          sc++;
        } // 윗쪽 변 이동

        while (sr &amp;gt;= i &amp;amp;&amp;amp; sr &amp;lt; n - 1 - i &amp;amp;&amp;amp; sc === n - 1 - i) {
          result[sr + 1][sc] = boards[startR + sr][startC + sc];
          sr++;
        } // 오른쪽 변 이동

        while (sr === n - 1 - i &amp;amp;&amp;amp; sc &amp;gt; i &amp;amp;&amp;amp; sc &amp;lt;= n - 1 - i) {
          result[sr][sc - 1] = boards[startR + sr][startC + sc];
          sc--;
        } // 아랫 변 이동

        while (sr &amp;gt; i &amp;amp;&amp;amp; sr &amp;lt;= n - 1 - i &amp;amp;&amp;amp; sc === i) {
          result[sr - 1][sc] = boards[startR + sr][startC + sc];
          sr--;
        } // 왼쪽 변 이동
      }
    }
    result[s][s] = boards[startR + s][startC + s];
    // (r,c) 지점은 회전을 수행하지 않으므로 그대로 복사한다.

    for (let i = 0; i &amp;lt; n; i++) {
      for (let j = 0; j &amp;lt; n; j++) {
        boards[startR + i][startC + j] = result[i][j];
      }
    } // 회전 결과 배열을 boards 배열로 복사해주면 된다.
  }
}

const input = [];
require('readline')
  .createInterface(process.stdin, process.stdout)
  .on('line', (line) =&amp;gt; {
    input.push(line);
  })
  .on('close', () =&amp;gt; {
    console.log(sol(input));
    process.exit();
  });&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DS &amp;amp; Algorithm/baekjoon</category>
      <category>백준 17406 nodeJS</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/135</guid>
      <comments>https://gobae.tistory.com/135#entry135comment</comments>
      <pubDate>Fri, 15 Apr 2022 18:42:04 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] 마이크로 태스크 큐의 비동기 작업 처리와 렌더링 시점을 알아보자.</title>
      <link>https://gobae.tistory.com/134</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이전 글&lt;/h3&gt;
&lt;figure id=&quot;og_1649904207207&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자바스크립트] 마이크로 태스크 큐&quot; data-og-description=&quot;이전 글 [자바스크립트] 프로미스를 이용한 비동기 작업 병렬 처리 이전 글 [자바스크립트] 프로미스 객체 이전 글 [자바스크립트] 비동기로 데이터 가져오기, 콜백 헬 이전 포스팅 [자바스크립&quot; data-og-host=&quot;gobae.tistory.com&quot; data-og-source-url=&quot;https://gobae.tistory.com/105&quot; data-og-url=&quot;https://gobae.tistory.com/105&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/beuMHX/hyN2L0ckPr/8po5NhMxQAET8eqnWKa8v0/img.png?width=778&amp;amp;height=436&amp;amp;face=0_0_778_436,https://scrap.kakaocdn.net/dn/OTeed/hyN2D8UZm5/Kl3BDO6K2ojozkzQDHXfGK/img.png?width=778&amp;amp;height=436&amp;amp;face=0_0_778_436,https://scrap.kakaocdn.net/dn/bcApj4/hyN2F6JJZK/VuRJKgGanO4I2DYEGMJuHK/img.png?width=1136&amp;amp;height=416&amp;amp;face=0_0_1136_416&quot;&gt;&lt;a href=&quot;https://gobae.tistory.com/105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gobae.tistory.com/105&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/beuMHX/hyN2L0ckPr/8po5NhMxQAET8eqnWKa8v0/img.png?width=778&amp;amp;height=436&amp;amp;face=0_0_778_436,https://scrap.kakaocdn.net/dn/OTeed/hyN2D8UZm5/Kl3BDO6K2ojozkzQDHXfGK/img.png?width=778&amp;amp;height=436&amp;amp;face=0_0_778_436,https://scrap.kakaocdn.net/dn/bcApj4/hyN2F6JJZK/VuRJKgGanO4I2DYEGMJuHK/img.png?width=1136&amp;amp;height=416&amp;amp;face=0_0_1136_416');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[자바스크립트] 마이크로 태스크 큐&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이전 글 [자바스크립트] 프로미스를 이용한 비동기 작업 병렬 처리 이전 글 [자바스크립트] 프로미스 객체 이전 글 [자바스크립트] 비동기로 데이터 가져오기, 콜백 헬 이전 포스팅 [자바스크립&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gobae.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 비동기 포스팅에서 알아본 마이크로 태스크 큐에 대해 좀 더 탐색하는 포스팅이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글을 요약하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;비동기 작업에도 우선순위가 있다. 비동기 작업은 마이크로 태스크 큐 혹은 매크로 태스크 큐(=일반적으로 말하는 태스크 큐)에 담기는데, 마이크로 태스크 큐에 있는 태스크가 태스크 큐에 있는 태스크보다 우선순위를 가진다. 그래서 이벤트 루프가 마이크로 태스크 큐에 담긴 태스크를 먼저 콜스택에 옮겨서 자바스크립트 엔진이 실행할 수 있도록 한다.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 지식만을 가지고 있다가, 다음과 같은 의문이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 마이크로 태스크 큐에 태스크가 엄청나게 많으면, 태스크 큐의 태스크는 무한정 대기하는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 태스크 큐의 태스크를 처리하다가, 마이크로 태스크 큐에 또 다른 태스크가 들어온다면??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 루프에서 비동기 작업을 다룰 때 조금 더 디테일한 과정이 있더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 의문들을 하나씩 풀어보자!.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 루프 알고리즘&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 런타임(브라우저 / 노드)에서 이벤트 루프가 비동기 작업을 돕는다는 것은 알 것이다. 혹시나 처음 들어보는 말이라면, 싱글스레드로 동작하는 자바스크립트 엔진이 왜 여러 작업들이 함께 발생하는 것 처럼 느껴지게 하는지, 자바스크립트 엔진과 이벤트 루프가 어떻게 동작하는지부터 알아보길 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 루프는, 콜스택과 태스크 큐의 상태를 끊임없이 감시하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진이 콜스택에 들어오는 태스크들을 처리하는데, 콜스택이 비어있고 태스크 큐에 대기중인 태스크가 있다면 이벤트 루프가 태스크를 엔진으로 밀어넣어 엔진이 실행할 수 있도록 한다. 그래서 &lt;b&gt;엔진은 &quot;콜스택에 들어온 태스크를 처리&quot;&lt;/b&gt;하며 &lt;b&gt;루프는 &quot;큐에 대기중인 태스크를 콜스택에 밀어줌으로서 처리&quot;&lt;/b&gt;하는 것이다. 둘 다 끊임없이 돌아갈 것이고 브라우저에 의해 최적화되어 태스크를 기다리는 동안에는 CPU 자원 소비가 최소화 되도록 설계되어있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 루프가 마이크로 태스크 큐, 태스크 큐, 렌더링 작업을 다루는 순서는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lyvX0/btrzjn2vRHG/KAK7mjGfb4glLasusMNcxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lyvX0/btrzjn2vRHG/KAK7mjGfb4glLasusMNcxk/img.png&quot; data-alt=&quot;https://javascript.info/event-loop&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lyvX0/btrzjn2vRHG/KAK7mjGfb4glLasusMNcxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlyvX0%2Fbtrzjn2vRHG%2FKAK7mjGfb4glLasusMNcxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;397&quot; height=&quot;768&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://javascript.info/event-loop&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 콜스택이 비어있다는 전제하에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;b&gt;마이크로 태스크 큐&lt;/b&gt;에 있는&amp;nbsp;마이크로 태스크를 FIFO로 순차 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 마이크로 태스크 큐가 비면, &lt;b&gt;렌더링 작업을 수행&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 렌더링 작업 후에는 &lt;b&gt;매크로 태스크 큐(=태스크 큐)&lt;/b&gt;에 있는 태스크를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 매크로 태스크 큐의 작업이 1개 실행되고, 다시 1번으로 돌아간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매크로 태스크 1개 =&amp;gt; 마이크로 태스크 전부 =&amp;gt; 렌더링작업 수행 =&amp;gt; 매크로 태스크 1개 =&amp;gt; ...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의 반복이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주목할 점은 마이크로 태스크가 모두 처리되지 않으면, 렌더링이 되지 않는다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 마이크로 태스크 큐에 담길 태스크는 Promise 콜백과 then, catch, finally 콜백, async 함수 등이 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;렌더링 시점 예측하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이크로 태스크와 매크로 태스크를 기반으로 렌더링 시점을 예측할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 코드를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 동기적 작업&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 매크로 태스크의 비동기 작업&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 마이크로 태스크의 비동기 작업&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 나누고, 단순한 counter를 통해 코드 실행과 렌더링을 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649918475963&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
    &amp;lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;div&amp;gt;동기코드 : &amp;lt;span id=&quot;synchronous&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div&amp;gt;매크로 : &amp;lt;span id=&quot;macro&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div&amp;gt;마이크로 : &amp;lt;span id=&quot;micro&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;button id=&quot;run&quot;&amp;gt;실행&amp;lt;/button&amp;gt;
      &amp;lt;button id=&quot;reset&quot;&amp;gt;초기화&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;script&amp;gt;
      const synchronous = document.querySelector('#synchronous');
      const macro = document.querySelector('#macro');
      const micro = document.querySelector('#micro');
      document.querySelector('#reset').addEventListener('click', reset);
      document.querySelector('#run').addEventListener('click', run);

      function heavyTask(elem, iter) {
        for (let i = 1; i &amp;lt;= iter; i++) {
          elem.textContent = i;
        }
      }

      function run() {
        // asynchronous macro task
        setTimeout(() =&amp;gt; {
          heavyTask(macro, 300000);
          console.log('macro task end');
        });

        // synchronous task
        heavyTask(synchronous, 100000);
        console.log('synchronous task end');

        // asynchronous micro task
        queueMicrotask(() =&amp;gt; {
          heavyTask(micro, 200000);
          console.log('micro task end');
        });
      }

      function reset() {
        synchronous.textContent = 0;
        macro.textContent = 0;
        micro.textContent = 0;
      }
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시코드이며 실행하면 아래와 같이 생겼다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNFu4H/btrziShBvWO/0ylW7ZgFC2gcfZF9LI4trK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNFu4H/btrziShBvWO/0ylW7ZgFC2gcfZF9LI4trK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNFu4H/btrziShBvWO/0ylW7ZgFC2gcfZF9LI4trK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNFu4H%2FbtrziShBvWO%2F0ylW7ZgFC2gcfZF9LI4trK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;351&quot; height=&quot;200&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 각 함수들과 작업들을 소개하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;heaveTask 함수&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649918941171&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      function heavyTask(elem, iter) {
        for (let i = 1; i &amp;lt;= iter; i++) {
          elem.textContent = i;
        }
      }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엘리먼트(돔 노드)와 반복수를 받아, for문을 통해 화면 엘리먼트에 현재 반복수를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마도, 개발자는 화면의 숫자가 1-&amp;gt;2-&amp;gt;3-&amp;gt;4-&amp;gt;...-&amp;gt;iter 까지 올라가는 것을 화면에 보여주고싶은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;reset 함수&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649919001856&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      function reset() {
        synchronous.textContent = 0;
        macro.textContent = 0;
        micro.textContent = 0;
      }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 엘리먼트들의 텍스트를 0으로 초기화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;run 함수&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649919048574&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      function run() {
        // asynchronous macro task
        setTimeout(() =&amp;gt; {
          heavyTask(macro, 300000);
          console.log('macro task end');
        });

        // synchronous task
        heavyTask(synchronous, 100000);
        console.log('synchronous task end');

        // asynchronous micro task
        queueMicrotask(() =&amp;gt; {
          heavyTask(micro, 200000);
          console.log('micro task end');
        });
      }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;run 함수는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- setTimeout의 매크로 태스크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- queueMicrotask의 마이크로 태스크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그 외 동기 실행 코드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;run 함수에 각 작업들을 실행하기 전에, 작업 순서를 예측하고 실행한 결과를 함께 살펴볼 예정이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 동기 + 매크로 태스크만 실행하기.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜스택에서 매크로 태스크 setTimeout을 실행하고 webAPI에 타이머를 위임한다. 동기 코드는 즉시 실행된다. iterator가 10만이기 때문에, 화면의 숫자가 1~10만까지 바뀔 것이고, 실행이 완료되면 'synchromous task end'를 출력하고 콜스택이 비워진다. 이 때 매크로 태스크 큐에 있던 setTimeout 콜백이 실행되어 화면 매크로의 숫자가 30만까지 바뀌는 것이 예상 흐름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m87jV/btrzlexPO7y/WxStjMdyX7suYS5qlR8XoK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m87jV/btrzlexPO7y/WxStjMdyX7suYS5qlR8XoK/img.gif&quot; data-alt=&quot;동기+ 매크로 태스크 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m87jV/btrzlexPO7y/WxStjMdyX7suYS5qlR8XoK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/m87jV/btrzlexPO7y/WxStjMdyX7suYS5qlR8XoK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;292&quot; height=&quot;216&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;동기+ 매크로 태스크 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 개발자의 의도와 상당히 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 for 루프를 통해 계속 카운터 렌더링을 수행하려는 의도는 말을 듣질 않는다. 자바스크립트 엔진이 코드를 실행하는 동안에는 별도의 렌더링을 수행할 수 없다. 이것은 브라우저가 렌더링 엔진이 아닌 자바스크립트 엔진에 제어권을 넘겨준 것이기 때문이기도 하고, 앞서 마이크로태스크 -&amp;gt; 렌더링 -&amp;gt; 매크로태스크의 절차를 거친다고 배운것을 바탕으로, 현재 엔진에서 코드를 실행중이라면 엔진에 실행컨텍스트가 비워졌을 때 마이크로 태스크 큐를 살펴보고, 렌더링 작업이 수행되겠구나.를 예측할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 결국 elem.textContent를 변경시키는 작업은, 모든 코드가 실행된 뒤 i를 반영하여 한번에 렌더링이 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점을 바탕으로 현재 코드의 동작을 살펴보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜스택에서 매크로 태스크 setTimeout을 실행하고 webAPI에 타이머를 위임한다. 동기 코드는 즉시 실행된다. iterator가 10만이기 때문에, i가 1~10만까지 바뀔 것이고, 실행이 완료되면 'synchromous task end'를 출력하고 콜스택이 비워진다. 이 때 마이크로 태스크 큐에 작업은 존재하지 않으므로, 렌더링이 수행된다.(당연히 수행될 렌더링 작업이 없다면 별도로 수행되진 않는다.) 수행해야 할 렌더링은 화면의 동기코드를 100,000으로 보여주는 것. 그러므로 먼저 &quot;동기코드: 100000&quot;이 렌더링된다. 그리고 매크로 태스크 큐에 있던 setTimeout 콜백이 콜스택에서 실행되어 i=30만까지 증가한다. 콜스택이 비워지면 마이크로 태스크 큐에 작업이 존재하지 않으므로, &quot;매크로 : 300000&quot;가 렌더링된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 작업이라 자세한 설명과 함께 살펴봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음부터는 큰 흐름만 이해하면서 예측해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 동기 + 마이크로 태스크 실행&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 &lt;b&gt;queueMicrotask&lt;/b&gt;는 마이크로 태스크 큐에 콜백을 밀어 넣어주는 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기와 마이크로 태스크의 조합을 예측해보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기코드가 실행되어 i=10만의 렌더링을 발생시킨 뒤, 마이크로 태스크가 실행되어 i=20만의 렌더링을 발생시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링은 마이크로 태스크 처리 이후 수행된다. 그러므로 아직 렌더링이 수행되지 않은 시점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 렌더링을 수행하는 시점에는, 동기+마이크로 2가지의 변경사항을 한꺼번에 반영하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TgYiZ/btrzncsPnfD/tI0F4aK3COQbALvLmBNZbK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TgYiZ/btrzncsPnfD/tI0F4aK3COQbALvLmBNZbK/img.gif&quot; data-alt=&quot;동기 + 마이크로 태스크 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TgYiZ/btrzncsPnfD/tI0F4aK3COQbALvLmBNZbK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/TgYiZ/btrzncsPnfD/tI0F4aK3COQbALvLmBNZbK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;292&quot; height=&quot;216&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;동기 + 마이크로 태스크 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 매크로 태스크 + 마이크로 태스크 실행&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이정도야 거뜬하다!.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 쭉 실행시켜서, setTimeout, queueMicrotask 함수를 순차적으로 실행한다. 이제 콜스택은 비어있다. 마이크로 태스크를 먼저 실행해서 20만 렌더링을 발생시키고, 렌더링 과정에 의해 즉시 &quot;마이크로 : 200000&quot;가 렌더링된다. 그리고 매크로 태스크를 실행하여 30만 렌더링을 발생시킨다. &amp;nbsp;마이크로 태스크 큐가 비었으므로, 바로 &quot;매크로 : 300000&quot; 렌더링을 수행하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOJmF3/btrzjaJc3FH/hHaWKHSdxsJkGRM5gv2tb0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOJmF3/btrzjaJc3FH/hHaWKHSdxsJkGRM5gv2tb0/img.gif&quot; data-alt=&quot;매크로 + 마이크로 태스크 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOJmF3/btrzjaJc3FH/hHaWKHSdxsJkGRM5gv2tb0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dOJmF3/btrzjaJc3FH/hHaWKHSdxsJkGRM5gv2tb0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;305&quot; height=&quot;216&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;매크로 + 마이크로 태스크 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 모두 실행&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링 시 동기 + 마이크로가 먼저 반영되고, 매크로 렌더링이 두번째로 반영된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HnCvE/btrzkplzyWS/pXvckKqDuySnqpi06KPNP1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HnCvE/btrzkplzyWS/pXvckKqDuySnqpi06KPNP1/img.gif&quot; data-alt=&quot;모두 실행한 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HnCvE/btrzkplzyWS/pXvckKqDuySnqpi06KPNP1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/HnCvE/btrzkplzyWS/pXvckKqDuySnqpi06KPNP1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;301&quot; height=&quot;216&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모두 실행한 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원래 개발자의 의도였던 1~10만까지 화면이 변하게 설계하고 싶다. 어떻게 해야할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3가지정도 예시를 통해 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1번 예시 : 동기적으로 1~10만까지 직접 타이핑해서 수행한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649921143492&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      function renderCounter(elem, num) {
        elem.textContent = num;
      }
      
      function run() {
        renderCounter(synchronous, 1);
        renderCounter(synchronous, 2);
        renderCounter(synchronous, 3);
        renderCounter(synchronous, 4);
		...
        renderCounter(synchronous, 100000);
      }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2번 예시 : 마이크로 태스크를 1~10만까지 발생시킨다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649921349246&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      function run() {
        for (let i = 1; i &amp;lt;= 100000; i++) {
          queueMicrotask(() =&amp;gt; heavyTask(micro, i));
        }
      }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3번 예시 : 매크로 태스크를 1~10만까지 발생시킨다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649921423886&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      function run() {
        for (let i = 1; i &amp;lt;= 100000; i++) {
          setTimeout(() =&amp;gt; heavyTask(macro, i), 0);
        }
      }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤것이 의도대로 동작할까?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번은 run 함수의 실행컨텍스트에서 i=1~10만까지 갱신하므로, 렌더링은 run함수가 끝나야 수행된다. 결국 한번에 10만으로 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번은 run 함수의 실행컨텍스트를 통해 마이크로 태스크 큐에 10만개의 작업이 쌓이게 될 것인데, 마이크로 태스크를 모두 실행해야 렌더링으로 넘어간다. 결국 2번도 한번에 10만으로 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번은 run 함수에 의해 매크로 태스크 큐에 10만개의 작업이 쌓인다. 매크로 태스크를 하나씩 처리할 때마다 마이크로 태스크를 살펴보고 / 렌더링 작업을 수행한 뒤 다시 매크로 태스크를 하나 처리하러 온다. 결국 3번이 의도한 대로 동작하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZKILU/btrznzuqHUZ/QTEtzIgypysRuMzaF1Yhd1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZKILU/btrznzuqHUZ/QTEtzIgypysRuMzaF1Yhd1/img.gif&quot; data-alt=&quot;물론 당연하게도 성능이 끔찍하다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZKILU/btrznzuqHUZ/QTEtzIgypysRuMzaF1Yhd1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ZKILU/btrznzuqHUZ/QTEtzIgypysRuMzaF1Yhd1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;256&quot; height=&quot;216&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;물론 당연하게도 성능이 끔찍하다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 작업 수행과 렌더링을 병행해야 한다면,(무거운 작업을 수행하면서, 사용자에게 progress bar와 같이 진척속도를 알려줘야 한다거나) 큰 작업을 작은 작업으로 나누는 것이 도움이 된다. 그렇지만, 예시처럼 모든 숫자 변화를 보여주는 렌더링을 설계하면, 과부하로 인해 브라우저가 응답을 멈출 수 있으니 주의할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;tmi를 던져보자면....&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setTimeout 타이머로 매크로 태스크를 생성할 때, 두번째 인자를 0으로 설정해도 크롬 환경에서는 최소 4ms을 보장한다. 무거운 작업을 수행할 때, 원형 loading indicator를 &lt;span&gt;애니메이션과&lt;span&gt;&amp;nbsp;함께 사용하면 작업 도중에도 애니메이션은 렌더링과정과는 달리 브라우저에서 그려주므로 도움이 될 수 있겠다. 그러나, 작업이 꽤나 무거워서 진척 상황을 progress bar로 렌더링해야 한다면(3초 이상 로딩 스피너만 돌아가게 된다면, 꽤나 곤란할 수 있다.) 작업을 나누고 현재 진척 상황을 기준으로 progress bar를 다시 렌더링해주는 방식이 더 괜찮을 수 있다. 이 때는 앞서 다룬 매크로 태스크를 이용할 수 있겠다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/event-loop&quot;&gt;https://ko.javascript.info/event-loop&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@dami/JS-Microtask-Queue&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@dami/JS-Microtask-Queue&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 비동기 마이크로 태스크 큐와 렌더링 과정</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/134</guid>
      <comments>https://gobae.tistory.com/134#entry134comment</comments>
      <pubDate>Thu, 14 Apr 2022 17:02:51 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 13460번 구슬 탈출 2 - 자바스크립트</title>
      <link>https://gobae.tistory.com/133</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0ZDb1/btrxjH2galx/QNMdMCieBhltOa0QW7YzO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0ZDb1/btrxjH2galx/QNMdMCieBhltOa0QW7YzO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0ZDb1/btrxjH2galx/QNMdMCieBhltOa0QW7YzO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0ZDb1%2FbtrxjH2galx%2FQNMdMCieBhltOa0QW7YzO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;613&quot; height=&quot;286&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1648208740570&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;13460번: 구슬 탈출 2&quot; data-og-description=&quot;첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 &amp;le; N, M &amp;le; 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 '.', '#', 'O', 'R', 'B'&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/13460&quot; data-og-url=&quot;https://www.acmicpc.net/problem/13460&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bPvpB0/hyNOxWAXw2/uWmP8MZ253NvdnfHmRu6V1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/13460&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/13460&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bPvpB0/hyNOxWAXw2/uWmP8MZ253NvdnfHmRu6V1/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;13460번: 구슬 탈출 2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 &amp;le; N, M &amp;le; 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 '.', '#', 'O', 'R', 'B'&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 문제와 조건을 기반으로 순서를 파악해 구현하는 구현유형이자 그래프 기반 BFS 유형이기도 하며 시뮬레이션 유형이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종합선물세트와도 같은 문제를 풀다가, 자그마한 실수를 개선하면서 정답이 나와서 정리하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;자바스크립트로 코테 연습하는분들 화이팅&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 여러분이 궁금한 것은 코드일테니, 가장 먼저 소개할 것은 성공을 출력해주는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이도 궁금하다면 이어서 계속 보면 되겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre class=&quot;zephir&quot;&gt;&lt;code&gt;function sol(input) {
  let redBallPos = null;
  let blueBallPos = null;
  let holePos = null;
  const maxCnt = 10;
  const boardObj = {
    RED: 'R',
    BLUE: 'B',
    HOLE: 'O',
    EMPTY: '.',
  };
  const dirObj = {
    TOP: 0,
    RIGHT: 1,
    BOTTOM: 2,
    LEFT: 3,
  };

  const dx = [-1, 0, 1, 0];
  const dy = [0, 1, 0, -1];

  const boards = input.slice(1).map((str, rowIdx) =&amp;gt; {
    const row = str.split('');

    if (!redBallPos || !blueBallPos || !holePos) {
      row.forEach((elem, colIdx) =&amp;gt; {
        if (elem === boardObj.RED) redBallPos = [rowIdx, colIdx];
        else if (elem === boardObj.BLUE) blueBallPos = [rowIdx, colIdx];
        else if (elem === boardObj.HOLE) holePos = [rowIdx, colIdx];
      });
    }

    return row;
  });

  boards[redBallPos[0]][redBallPos[1]] = boardObj.EMPTY;
  boards[blueBallPos[0]][blueBallPos[1]] = boardObj.EMPTY;

  function moveBall(ball, otherBall, dir) {
    while (1) {
      const nx = ball[0] + dx[dir];
      const ny = ball[1] + dy[dir];

      if (nx === otherBall[0] &amp;amp;&amp;amp; ny === otherBall[1]) {
        break;
      } else if (boards[nx][ny] === boardObj.EMPTY) {
        ball[0] = nx;
        ball[1] = ny;
      } else if (boards[nx][ny] === boardObj.HOLE) {
        ball[0] = -1;
        ball[1] = -1;
        break;
      } else break;
    }
  }

  function checkEscape(ball) {
    if (ball[0] === -1 &amp;amp;&amp;amp; ball[1] === -1) return true;
    return false;
  }

  function checkMoveRedballFirst(red, blue, dir) {
    if (
      (dir === dirObj.TOP &amp;amp;&amp;amp; red[0] &amp;lt; blue[0]) ||
      (dir === dirObj.RIGHT &amp;amp;&amp;amp; red[1] &amp;gt; blue[1]) ||
      (dir === dirObj.BOTTOM &amp;amp;&amp;amp; red[0] &amp;gt; blue[0]) ||
      (dir === dirObj.LEFT &amp;amp;&amp;amp; red[1] &amp;lt; blue[1])
    ) {
      return true;
    }
    return false;
  }

  function checkStop(bRed, aRed, bBlue, aBlue) {
    if (
      bRed[0] === aRed[0] &amp;amp;&amp;amp;
      bRed[1] === aRed[1] &amp;amp;&amp;amp;
      bBlue[0] === aBlue[0] &amp;amp;&amp;amp;
      bBlue[1] === aBlue[1]
    )
      return true;
    return false;
  }

  let answer = -1;
  let findAnswer = 0;
  const queue = [[...redBallPos, ...blueBallPos, 1]];

  while (queue.length) {
    if (findAnswer) break;
    const [rx, ry, bx, by, cnt] = queue.shift();

    for (let dir = 0; dir &amp;lt; 4; dir++) {
      const reds = [rx, ry];
      const blues = [bx, by];

      if (checkMoveRedballFirst(reds, blues, dir)) {
        moveBall(reds, blues, dir);
        moveBall(blues, reds, dir);
      } else {
        moveBall(blues, reds, dir);
        moveBall(reds, blues, dir);
      }

      if (checkEscape(blues)) continue;
      if (checkEscape(reds)) {
        findAnswer = 1;
        answer = cnt;
        break;
      }
      if (checkStop([rx, ry], reds, [bx, by], blues)) continue;
      if (cnt === maxCnt) continue;

      queue.push([...reds, ...blues, cnt + 1]);
    }
  }

  return answer;
}

const input = [];
require('readline')
  .createInterface(process.stdin, process.stdout)
  .on('line', (line) =&amp;gt; {
    input.push(line);
  })
  .on('close', () =&amp;gt; {
    console.log(sol(input));
    process.exit();
  });
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 꽤나 복잡해보이지만, 천천히 보면서 어떤 과정이 필요한지 분석해보자.&lt;br /&gt;(문제가 구슬 탈출이지만, 공으로 착각해서 변수명이 ball이니 참고하길 바란다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제의 조건&lt;/b&gt;&lt;br /&gt;보드의 너비 M, 높이 N이 주어지며 3 &amp;lt;= N,M &amp;lt;= 10 이다.&lt;br /&gt;보드 각 좌표에 문자열은 R(빨간공), B(파란공), O(구멍), #(벽), .(빈칸) 을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨간공, 파란공을 상하좌우로 기울여서 빨간공만 구멍으로 빼낼 수 있는지 조사한다.&lt;br /&gt;파란공이 탈출하거나 10회 이상으로 기울여야 한다면 -1을 반환한다.&lt;br /&gt;10회 미만으로 기울여서 빨간공만 탈출시켰다면, 기울인 횟수를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 해결 과정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 해결을 위해 다음과 같은 과정을 거치도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. bfs로 탐색하며, [빨간공 위치, 파란공 위치, 반복수 cnt] 배열을 큐에 넣는다.&lt;br /&gt;2. 큐가 비어있을 때 까지 탐색하면서 아래의 절차를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a. 상하좌우를 모두 탐색한다.&lt;br /&gt;b. 탐색할 때 빨간공이 먼저 움직일지, 파란공이 먼저 움직일지를 이동할 방향과 각 공의 좌표에 따라 정한다.&lt;br /&gt;c. 빨간공과 파란공을 정해진 순서대로 이동시킨다.&lt;br /&gt;d. 파란공이 구멍에 빠졌다면 정답이 아니므로 넘어간다.&lt;br /&gt;e. 빨간공만 구멍에 빠졌다면 정답이므로 탐색을 멈춘다.&lt;br /&gt;f. 빨간공과 파란공이 빠지지 않았을 때 다음을 수행한다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;i. 만약 cnt가 10회라면, 더이상 기울일 수 없으므로 다음 방향의 이동을 수행한다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;ii. 빨간공과 파란공의 기울여서 움직인 좌표가 움직이기 전과 같다면, 큐에 넣지 않는다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;iii. 움직인 좌표가 움직이기 전과 다르다면, 이후 좌표를 큐에 넣어주면서 기울인 횟수 cnt를 증가시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 과정을 구현하기 위해 필요한 함수를 선언했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;moveBall : 기울인 방향에 따라 공의 위치를 움직이는 함수이다.&lt;br /&gt;checkEscape : 공이 구멍에 빠졌는지 검사한다.&lt;br /&gt;checkMoveRedballFirst : 정해진 방향으로 이동하기 전에, 빨간공이 파란공보다 먼저 움직여야 하는지 검사한다.&lt;br /&gt;checkStop : 공의 이동하기 전 위치와 이동한 후 위치가 동일한지 검사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은, 2차원 배열 boards을 이용해서 공을 직접 움직이면서 bfs 탐색을 수행하면 안된다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열 내에서 직접 움직이며 탐색한다면, 상하좌우를 검색하면서 계속 boards에 있는 공을 움직이는 셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 필자는 배열에서 처음 공의 위치를 먼저 파악해서 기록한 다음, 공이 위치했던 boards 인덱스를 비웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 탐색하면서 각 케이스마다의 빨간공과 파란공의 위치를 기반으로 움직이도록 했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;간단한 주석 첨부&lt;/h3&gt;
&lt;pre class=&quot;zephir&quot;&gt;&lt;code&gt;function sol(input) {
  let redBallPos = null;
  let blueBallPos = null;
  let holePos = null;
  const maxCnt = 10;
  const boardObj = {
    RED: 'R',
    BLUE: 'B',
    HOLE: 'O',
    EMPTY: '.',
  }; // 상수 객체
  const dirObj = {
    TOP: 0,
    RIGHT: 1,
    BOTTOM: 2,
    LEFT: 3,
  }; // 상하좌우 방향 객체

  const dx = [-1, 0, 1, 0];
  const dy = [0, 1, 0, -1];

  const boards = input.slice(1).map((str, rowIdx) =&amp;gt; {
    const row = str.split('');

    if (!redBallPos || !blueBallPos || !holePos) {
      row.forEach((elem, colIdx) =&amp;gt; {
        if (elem === boardObj.RED) redBallPos = [rowIdx, colIdx];
        else if (elem === boardObj.BLUE) blueBallPos = [rowIdx, colIdx];
        else if (elem === boardObj.HOLE) holePos = [rowIdx, colIdx];
      });
    } // 빨간공, 파란공, 구멍의 위치 찾아서 기록하기

    return row;
  });

  boards[redBallPos[0]][redBallPos[1]] = boardObj.EMPTY;
  boards[blueBallPos[0]][blueBallPos[1]] = boardObj.EMPTY;
  // bfs 탐색을 수행해야 하므로, 보드에서 직접 이동시킨다면 매 수행마다 공의 위치가 바뀐다.
  // 그래서 보드에서 공 위치를 빈칸으로 남기고, 매 이동마다 상대적으로 위치를 파악한다.

  function moveBall(ball, otherBall, dir) {
    // 현재 이동시킬 공과 다른 공의 위치를 상대적으로 비교하며, 다른 공이 길을 막았을 때 넘어가지 않도록 한다.
    while (1) {
      const nx = ball[0] + dx[dir];
      const ny = ball[1] + dy[dir];

      if (nx === otherBall[0] &amp;amp;&amp;amp; ny === otherBall[1]) {
        break;
      } else if (boards[nx][ny] === boardObj.EMPTY) {
        ball[0] = nx;
        ball[1] = ny;
      } else if (boards[nx][ny] === boardObj.HOLE) {
        // 먼저 이동한 공이 구멍에 빠졌다면, 뒤에 이동할 공도 구멍에 빠질 수 있으므로, (-1,-1)좌표를 부여한다.
        ball[0] = -1;
        ball[1] = -1;
        break;
      } else break;
    }
  }

  function checkEscape(ball) {
    // 공이 구멍에 빠졌다면 (-1, -1) 좌표를 가지므로 이를 확인한다.
    if (ball[0] === -1 &amp;amp;&amp;amp; ball[1] === -1) return true;
    return false;
  }

  function checkMoveRedballFirst(red, blue, dir) {
    // 이동방향에 따라 이동방향과 더 가까운 공을 먼저 이동시킨다.(Ex.방향이 좌측이면 더 왼쪽에 위치한 공)
    if (
      (dir === dirObj.TOP &amp;amp;&amp;amp; red[0] &amp;lt; blue[0]) ||
      (dir === dirObj.RIGHT &amp;amp;&amp;amp; red[1] &amp;gt; blue[1]) ||
      (dir === dirObj.BOTTOM &amp;amp;&amp;amp; red[0] &amp;gt; blue[0]) ||
      (dir === dirObj.LEFT &amp;amp;&amp;amp; red[1] &amp;lt; blue[1])
    ) {
      return true;
    }
    return false;
  }

  function checkStop(beforeBall, afterBall) {
      // 공의 이동 전과 이동 후의 위치가 동일한지 검사한다. 빨간공, 파란공 둘다 동일하다면 더이상 큐로 탐색할 필요가 없다.
    if (beforeBall[0] === afterBall[0] &amp;amp;&amp;amp; beforeBall[1] === afterBall[1])
      return true;
    return false;
  }

  let answer = -1;
  let findAnswer = 0;
  const queue = [[...redBallPos, ...blueBallPos, 1]];
  // 두 공의 처음 위치를 큐에 넣어주고 시작한다.

  while (queue.length) {
    if (findAnswer) break;
    const [rx, ry, bx, by, cnt] = queue.shift();

    for (let dir = 0; dir &amp;lt; 4; dir++) {
      const reds = [rx, ry];
      const blues = [bx, by];

      if (checkMoveRedballFirst(reds, blues, dir)) {
        // 빨간공을 먼저 이동시키고, 파란공을 나중에 이동시킨다.
        // reds, blues 배열의 0,1번 인덱스 값은 moveBall 함수 수행에 따라 변화하게 된다.
        moveBall(reds, blues, dir);
        moveBall(blues, reds, dir);
      } else {
        moveBall(blues, reds, dir);
        moveBall(reds, blues, dir);
      }

      // 파란공이 빠졌다면, 무조건 정답이 아니다.
      if (checkEscape(blues)) continue;
      if (checkEscape(reds)) {
        // 빨간공만 빠졌다면 정답이다.
        findAnswer = 1;
        answer = cnt;
        break;
      }
      // 빨간공과 파란공 둘 다 제자리라면, 이동한 좌표를 큐에 넣을 필요가 없다.
      if (checkStop([rx, ry], reds) &amp;amp;&amp;amp; checkStop([bx, by], blues)) continue;
      // 10회의 이동까지 정답을 못찾았다면, 더이상 탐색하지 않는다.
      if (cnt === maxCnt) continue;

      // 더 탐색할 수 있다면 큐에 넣어준다.
      queue.push([...reds, ...blues, cnt + 1]);
    }
  }

  return answer;
}

// 입력 처리
const input = [];
require('readline')
  .createInterface(process.stdin, process.stdout)
  .on('line', (line) =&amp;gt; {
    input.push(line);
  })
  .on('close', () =&amp;gt; {
    console.log(sol(input));
    process.exit();
  });
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DS &amp;amp; Algorithm/baekjoon</category>
      <category>13460 javascript nodejs</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/133</guid>
      <comments>https://gobae.tistory.com/133#entry133comment</comments>
      <pubDate>Fri, 25 Mar 2022 20:45:18 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 가사 검색 - 자바스크립트</title>
      <link>https://gobae.tistory.com/132</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUr3zs/btrxcyLeNWC/aMTAB9GSKqpmkHtK6Tsjyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUr3zs/btrxcyLeNWC/aMTAB9GSKqpmkHtK6Tsjyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUr3zs/btrxcyLeNWC/aMTAB9GSKqpmkHtK6Tsjyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUr3zs%2FbtrxcyLeNWC%2FaMTAB9GSKqpmkHtK6Tsjyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;571&quot; height=&quot;290&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1647437954100&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코딩테스트 연습 - 가사 검색&quot; data-og-description=&quot;&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/60060#&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/60060&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/baMr7G/hyNJc48rEG/mcMLwJ88glmpMi781r4Iq0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/tXy0Y/hyNI552qjy/AHPAm8cOOduJ1cEsdT2ekk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/60060#&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/60060#&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/baMr7G/hyNJc48rEG/mcMLwJ88glmpMi781r4Iq0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/tXy0Y/hyNI552qjy/AHPAm8cOOduJ1cEsdT2ekk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - 가사 검색&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진탐색으로 풀기 위해서 많은 시간을 소요했고, 결국 풀어서 포스팅하게 되었다.&lt;br /&gt;다소 로직이 더러울 수 있고 더 효과적인 방법이 있을 수 있으니, &quot;이렇게도 정답은 나오는구나&quot; 정도로 보시길 바랍니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre class=&quot;vbscript&quot;&gt;&lt;code&gt;    function solution(words, queries) {
  const answer = [];
  const wordsObj = {};
  const reverseWordsObj = {};

  function findQuestion(query, start, end) {
    let result = -1;

    while (start &amp;lt;= end) {
      const mid = Math.floor((start + end) / 2);

      if (mid === 0) {
        if (query[mid] === '?') result = 0;
        else result = 1;
        break;
      }

      if (query[mid] === '?') {
        if (query[mid - 1] !== '?') {
          result = mid;
          break;
        } else {
          end = mid - 1;
        }
      } else {
        start = mid + 1;
      }
    }

    return result;
  }

  function findFirstWordIdx(wordArr, start, end, compareStr, questionIdx) {
    let result = -1;

    if (start === end) {
      if (wordArr[0].slice(0, questionIdx) === compareStr) return 0;
      return result;
    }

    while (start &amp;lt;= end) {
      const mid = Math.floor((start + end) / 2);
      const midStr = wordArr[mid].slice(0, questionIdx);

      if (mid === 0) {
        if (midStr === compareStr) {
          result = 0;
        } else if (wordArr[mid + 1].slice(0, questionIdx) === compareStr) {
          result = 1;
        }
        break;
      }

      const prevStr = wordArr[mid - 1].slice(0, questionIdx);

      if (midStr === compareStr) {
        if (prevStr !== compareStr) {
          result = mid;
          break;
        } else {
          end = mid - 1;
        }
      } else if (midStr &amp;gt; compareStr) {
        end = mid - 1;
      } else {
        start = mid + 1;
      }
    }
    return result;
  }

  function findLastWordIdx(wordArr, start, end, compareStr, questionIdx) {
    let result = end;

    while (start &amp;lt;= end) {
      const mid = Math.floor((start + end) / 2);
      const midStr = wordArr[mid].slice(0, questionIdx);

      if (mid === wordArr.length - 1) {
        result = mid;
        break;
      }

      const nextStr = wordArr[mid + 1].slice(0, questionIdx);

      if (midStr === compareStr) {
        if (nextStr !== compareStr) {
          result = mid;
          break;
        } else start = mid + 1;
      } else if (midStr &amp;gt; compareStr) {
        end = mid - 1;
      } else {
        start = mid + 1;
      }
    }

    return result;
  }

  for (let word of words) {
    const len = word.length;
    const reverseWord = word.split('').reverse().join('');

    if (!wordsObj[len]) {
      wordsObj[len] = [word];
      reverseWordsObj[len] = [reverseWord];
    } else {
      wordsObj[len].push(word);
      reverseWordsObj[len].push(reverseWord);
    }
  }

  function ascendSort(obj) {
    Object.values(obj).forEach((arr) =&amp;gt; arr.sort());
  }

  ascendSort(wordsObj);
  ascendSort(reverseWordsObj);

  for (let query of queries) {
    const len = query.length;
    const isFrontQuestion = query[0] === '?';
    const isAllQuestion = query[0] === '?' &amp;amp;&amp;amp; query[len - 1] === '?';

    if (isAllQuestion) {
      answer.push(wordsObj[len] ? wordsObj[len].length : 0);
      continue;
    }

    if (!wordsObj[len]) {
      answer.push(0);
      continue;
    }

    const targetQuery = isFrontQuestion
      ? query.split('').reverse().join('')
      : query;

    const questionIdx = findQuestion(targetQuery, 0, targetQuery.length - 1);

    const compareStr = targetQuery.slice(0, questionIdx);
    const targetWords = isFrontQuestion ? reverseWordsObj[len] : wordsObj[len];
    const endIdx = targetWords.length - 1;
    const args = [targetWords, 0, endIdx, compareStr, questionIdx];

    const firstIdx = findFirstWordIdx(...args);
    const lastIdx = findLastWordIdx(...args);

    answer.push(firstIdx === -1 ? 0 : lastIdx - firstIdx + 1);
  }

  return answer;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 조건을 먼저 살펴본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가사 단어 제한사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단어의 배열 words의 크기는 2 &amp;lt;= words.length &amp;lt;= 100,000 이다.&lt;br /&gt;각 단어의 길이는 1 &amp;lt;= word.length &amp;lt;= 10,000 이다.&lt;br /&gt;빈 문자열은 주어지지 않으며, 각 단어는 모두 알파벳 소문자로 구성되고 단어는 중복되지 않는다.&lt;br /&gt;전체 단어의 길이의 합은 2 &amp;lt;= all &amp;lt;= 1,000,000 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;검색 키워드 제한사항&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키워드의 배열, 각 키워드의 크기는 위의 단어와 동일하다.&lt;br /&gt;키워드는 중복 가능하며 알파벳 소문자와 ? 문자로 구성된다.&lt;br /&gt;문자 ?를 반드시 1개 이상 포함하며, 접두사 혹은 접미사 형태이다.( = ???abc abc??? 형태)&lt;br /&gt;그리하여 a?bc abc ?abc?? 등은 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 키워드에서 ? 문자는 모든 문자에 매칭될 수 있으며, 각 키워드마다 매칭되는 가사 단어의 수를 반환해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1310&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmPguX/btrwbrFQI0K/mE7U6PstWw4AnDbJCnM1Z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmPguX/btrwbrFQI0K/mE7U6PstWw4AnDbJCnM1Z0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmPguX/btrwbrFQI0K/mE7U6PstWw4AnDbJCnM1Z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmPguX%2FbtrwbrFQI0K%2FmE7U6PstWw4AnDbJCnM1Z0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;836&quot; data-origin-width=&quot;1310&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 풀이 방향은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;(열심히 한시간 넘게 작성했던 부분들이 포스팅을 하고 나니 열심히 설명한 부분만 다 날라갔다. 왜 그러는지 모르겠다.)&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 조금 더 자세한 내용을 적었었지만.. 시행착오를 건너뛰고 내가 생각한 풀이 방향만을 설명하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 단어 배열words에서 word의 크기별로 분리한다. 이 때 기존 단어의 배열과 뒤집은 단어의 배열을 만들어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 1에서 구한 모든 배열을 오름차순 정렬한다.(=키워드와 매칭되는 첫 인덱스와 마지막 인덱스를 구할 것이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 키워드 배열 queries를 순회하면서 각 키워드들에 대한 매칭 수를 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3-1. 키워드가 abc?? 형태라면 그대로 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3-2. 키워드가 ??abc 형태라면 뒤집어준다. (이것 때문에 1에서 뒤집은 배열을 만들어줬고, 뒤집지 않으면 함수가 더 많아지고 복잡해질 것.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3-3. 키워드가 ????? 형태라면 키워드 길이와 길이가 같은 단어의 수가 매칭수이며, 단어가 없다면 0을 매칭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3-4. 키워드 길이와 동일한 길이의 단어들이 없다면, 0을 매칭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3-5. 키워드에서 ?이 등장하는 인덱스를 구한다. 이 인덱스를 구하면 첫번째 인덱스부터 해당 인덱스 전까지가 알파벳키워드에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3-6. 탐색할 단어배열을 구한다. 키워드가 abc?? 형태였으면 기존단어의 배열, ??abc 형태였으면 뒤집은 단어의 배열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3-7. 단어 배열에서 이진탐색을 통해 키워드와 매칭된 단어가 출현하는 첫번째 인덱스와 마지막 인덱스를 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3-8. 첫번째 인덱스(findFirstWordIdx함수) 결과가 -1이면, 키워드가 존재하지 않으므로 0을 더해주고, 존재한다면 매칭 개수를 더한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;findQuestion, findFirstWordIdx, findLastWordIdx 3가지 함수 모두 이진탐색을 수행하는 함수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 여러 탈출조건을 고려하느라 함수가 조금 복잡해진 감도 있는 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른분들의 코드를 찾아보니, Trie 자료구조로 구하는 방법도 있었고 abc??를 abcaa abczz 문자열로 나눠 구하는 방법도 있었으니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 방법도 찾아보면 좋을 것 같다.&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/programmers</category>
      <category>가사 검색 자바스크립트</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/132</guid>
      <comments>https://gobae.tistory.com/132#entry132comment</comments>
      <pubDate>Wed, 16 Mar 2022 22:38:19 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 외벽 점검 - 자바스크립트</title>
      <link>https://gobae.tistory.com/131</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD97ei/btrxezvQYVy/rKcjEX7khzBQAhmPvbIvO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD97ei/btrxezvQYVy/rKcjEX7khzBQAhmPvbIvO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD97ei/btrxezvQYVy/rKcjEX7khzBQAhmPvbIvO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD97ei%2FbtrxezvQYVy%2FrKcjEX7khzBQAhmPvbIvO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;579&quot; height=&quot;290&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1646915236467&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코딩테스트 연습 - 외벽 점검&quot; data-og-description=&quot;레스토랑을 운영하고 있는 &amp;quot;스카피&amp;quot;는 레스토랑 내부가 너무 낡아 친구들과 함께 직접 리모델링 하기로 했습니다. 레스토랑이 있는 곳은 스노우타운으로 매우 추운 지역이어서 내부 공사를 하&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/60062&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/60062&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QQ0yR/hyNFxO97rD/7rLfnkSatrfLCPDzkrIcR1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/eyfa20/hyNFHqGyl0/jxUl97s7hAireAsasJhBd1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/60062&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/60062&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QQ0yR/hyNFxO97rD/7rLfnkSatrfLCPDzkrIcR1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/eyfa20/hyNFHqGyl0/jxUl97s7hAireAsasJhBd1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - 외벽 점검&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;레스토랑을 운영하고 있는 &quot;스카피&quot;는 레스토랑 내부가 너무 낡아 친구들과 함께 직접 리모델링 하기로 했습니다. 레스토랑이 있는 곳은 스노우타운으로 매우 추운 지역이어서 내부 공사를 하&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 알고리즘 문제 포스팅이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리의 목적은 코드이므로, 빠르게 코드부터 살펴보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;function solution(n, weak, dist) {
  const flattenWeak = [...weak, ...weak.map((elem) =&amp;gt; elem + n)];
  const weakLen = weak.length;
  const distLen = dist.length;
  const visits = new Array(distLen).fill(0);
  let answer = distLen + 1;

  if (weakLen === 1) return 1;

  function permutation(L, arr) {
    if (L === distLen) {
      for (let i = 0; i &amp;lt; weakLen; i++) {
        const end = i + weakLen;
        let left = i;
        let cnt = 0;

        for (let elem of arr) {
          if (left &amp;gt;= end) break;
          cnt += 1;
          const maxDist = elem + flattenWeak[left];

          while (left &amp;lt; end &amp;amp;&amp;amp; maxDist &amp;gt;= flattenWeak[left]) {
            left++;
          }
        }

        if (left &amp;lt; end) continue;

        answer = Math.min(answer, cnt);
      }
      return;
    }

    for (let i = 0; i &amp;lt; distLen; i++) {
      if (visits[i]) continue;
      visits[i] = 1;
      permutation(L + 1, [...arr, dist[i]]);
      visits[i] = 0;
    }
  }

  permutation(0, []);

  return answer === distLen + 1 ? -1 : answer;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 꽤 길지만, 차근차근 조건을 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 숫자 n, 공사가 필요한 외벽의 배열 weak, 친구들의 이동가능거리 배열 dist 가 주어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레스토랑은 원형이며, 둘레가 n이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친구들은 공사가 필요한 어떤 위치에서든, 점검을 시작할 수 있다.&lt;br /&gt;자신의 점검 시작위치를 기준으로 친구는 외벽의 시계 혹은 반시계 방향으로 이동할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;우리는, 모든 공사를 점검할 수 있는 가장 적은 친구의 수를 구하면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한 조건)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;lt;= n &amp;lt;= 20&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= weak.length &amp;lt;= 15
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외벽의 위치는 모두 다르다.&lt;/li&gt;
&lt;li&gt;외벽 weak는 오름차순으로 주어진다.&lt;/li&gt;
&lt;li&gt;외벽 weak의 원소는 0 ~ n-1의 정수다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= dist.length &amp;lt;= 8
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;친구의 이동거리 dist의 원소는 1 ~ 100의 자연수다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;826&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqCqqH/btrvFA4rPI6/cHMpoNArrcygb6rtWTxefK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqCqqH/btrvFA4rPI6/cHMpoNArrcygb6rtWTxefK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqCqqH/btrvFA4rPI6/cHMpoNArrcygb6rtWTxefK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqCqqH%2FbtrvFA4rPI6%2FcHMpoNArrcygb6rtWTxefK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;261&quot; height=&quot;254&quot; data-origin-width=&quot;826&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 다음과 같은 원형 레스토랑의 둘레는 n이다.&lt;br /&gt;북쪽이 0이고 둘레는 n이므로 북쪽의 시계방향에는 1, 반시계방향에는 n-1이 위치할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이동방향 통일시키기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친구가 이동하는 경로는 반시계, 시계 모두 가능한게 우선 문제다.&lt;br /&gt;이 방향을 다 고려하면, 한도끝도없이 어려워지므로 시계방향으로 통일해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러기 위해서는 위의 그림에서 10 -&amp;gt; 1로 이동의 흐름을 쉽게 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 weak 배열 원소에 둘레에 해당하는 n씩을 더해서 weak 배열에 추가해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 배열 [1, 5, 6, 10]이 [1, 5, 6, 10, 13, 17, 18, 22] 가 되고, 이제 친구는 시계방향으로만 이동해도 괜찮다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 현재 따져봐야 할 이동 시작점은, 1 5 6 10에서 시작하는 경우이다.&lt;br /&gt;왜냐하면 13에서부터 시작하면 [1, 5, 6, 10]과 다를 바 없으므로 10까지만 평가하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 이제 친구들을 배치시켜봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;친구 배치시키기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친구의 순서를 배치할 때는 순열을 이용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 순열을 이용할까? 그냥 배치하면 안되나?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;예를 들어서, [1, 5, 6, 10] 점검을 친구 배열 [1, 3]에게 맡긴다면 어떻게 될까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1의 이동거리의 친구가 5,6을 점검하고 3의 이동거리인 친구가 10,1을 점검한다면 점검을 완료할 수 있다.&lt;/li&gt;
&lt;li&gt;그러나 1의 이동거리인 친구가 5,6을 점검하지 않고 10이나 1을 점검한다면 점검을 완료할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 [1,2,3] 의 배열의 경우 [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] 모두 순회해봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순열이라니 시간복잡도가 좀 걱정되지만, dist의 크기는 최대 8이므로 8P8 = 40,320회로 그래도 할만하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배치된 dist 배열들을 기반으로 이동을 수행하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소 친구의 수를 구해야 하므로, 완전 탐색이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순열에서 친구 순서의 배치가 끝났을 때 마다, 친구를 외벽에 배치시킨다.&lt;br /&gt;이 때 첫번째 외벽, 두번째 외벽, 세번째 외벽 ... 마지막 외벽에서 시작할 때를 모두 검증해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 점검 성공과 점검 실패를 나눠야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점검 성공&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 친구가 투입되기 이전에 모든 외벽을 점검했다.&lt;/li&gt;
&lt;li&gt;맨 마지막 친구가 투입될 때 모든 외벽을 점검했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점검 실패&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 친구가 투입되었으나 모든 외벽을 점검할 수 없었다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 점검 실패의 경우를 제외하고 친구가 투입된 숫자 중 최솟값을 구하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 검증을 모든 순열의 경우에 반복해주면 최솟값을 찾을 수 있다.&lt;br /&gt;만약 정답이 초기값을 유지하고 있다면, 모든 경우가 점검 실패이므로 -1을 반환하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장황하게 설명해서 이해가 잘 되는지 잘 모르겠지만, 코드에 주석을 달아 간단하게 설명하고 끝내겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주석을 포함한 코드&lt;/h3&gt;
&lt;pre class=&quot;verilog&quot;&gt;&lt;code&gt;function solution(n, weak, dist) {
  // 외벽의 시작과 끝부분을 이어붙이기 위해 flattenWeak를 선언했다.
  const flattenWeak = [...weak, ...weak.map((elem) =&amp;gt; elem + n)];
  const weakLen = weak.length;
  const distLen = dist.length;
  // 방문 배열을 통해 순열을 만든다.
  const visits = new Array(distLen).fill(0);
  let answer = distLen + 1;

  // 외벽 원소가 1개면, 아무나 1명만 투입되면 된다.
  if (weakLen === 1) return 1;

  // 친구 배열의 순열을 구하는 함수
  function permutation(L, arr) {
    if (L === distLen) {
      for (let i = 0; i &amp;lt; weakLen; i++) {
        // 여기서 i는 처음 점검을 시작할 외벽의 인덱스다. [1, 5, 6, 10, 13, 17, 18,22] 에서 1,5,6,10까지만 조회한다.
        const end = i + weakLen; // 케이스마다, 외벽의 갯수만큼만 점검하면 완료되므로 end 변수에 넣었다.
        let left = i; // 점검을 시작할 외벽의 인덱스이다.
        let cnt = 0;

        for (let elem of arr) {
          if (left &amp;gt;= end) break; // left &amp;gt;= end의 경우는 외벽 점검이 끝났음을 의미한다. 더이상 반복문을 돌 필요가 없다.
          cnt += 1; // 친구가 특정 외벽에서 이동을 시작하므로, cnt를 올려준다.
          const maxDist = elem + flattenWeak[left]; // 친구가 이동할 수 있는 최대 위치를 나타낸다. 10에서 시작해서 4만큼 이동 가능하면 maxDist는 14일 것.

          while (left &amp;lt; end &amp;amp;&amp;amp; maxDist &amp;gt;= flattenWeak[left]) {
            left++;
            // while문이 종료되는 조건을 생각해보자.
            // end에 도달했다면 이미 점검이 끝난 것이다.
            // maxDist가 left 인덱스보다 작다면, 현재의 elem(친구)는 left-1 까지만 방문한 셈이다.
            // 만약 두 조건을 만족한다면, 친구는 더 이동할 수 있으므로 left를 증가시켜준다.
          }
        }

        if (left &amp;lt; end) continue; // left가 end보다 작다는 것은, 모든 친구를 동원했음에도 외벽을 점검하지 못한 것이다.

        answer = Math.min(answer, cnt); // 매 케이스마다, 친구의 수 최솟값을 갱신시켜준다.
      }
      return;
    }

    for (let i = 0; i &amp;lt; distLen; i++) {
      // 순차적으로 조회해야하므로, 방문처리와 방문 후에는 미방문처리로 바꾼다.
      if (visits[i]) continue;
      visits[i] = 1;
      permutation(L + 1, [...arr, dist[i]]);
      visits[i] = 0;
    }
  }

  // 순열 함수를 실행한다.
  permutation(0, []);

  return answer === distLen + 1 ? -1 : answer;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사실 좀 고생했다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TMI지만, 계속 테스트케이스 25개 중 10개에서 런타임 에러가 발생했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 로직이 잘못 된 것인지, 특이 케이스를 고려 못한것인지 계속 고민했었는데..&lt;br /&gt;기존에 배열 answer에 모든 cnt 값을 추가해줬는데, answer를 문자로 바꾸고 최솟값을 비교하니 문제가 해결되었다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;(사실 이것 때문에 나처럼 고생하는 분이 있을 수도 있어서 오랜만에 정리하게 된 것이기도 하다.)&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다지 궁금하진 않겠지만, 혹시 궁금하실 분을 위해 코드는 밑에 첨부해두겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;런타임 에러를 유발한 코드&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1646915673770&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(n, weak, dist) {
  const flattenWeak = [...weak, ...weak.map((elem) =&amp;gt; elem + n)];
  const weakLen = weak.length;
  const distLen = dist.length;
  const visits = new Array(distLen).fill(0);
  const answer = [];

  if (weakLen === 1) return 1;

  function permutation(L, arr) {
    if (L === distLen) {
      for (let i = 0; i &amp;lt; weakLen; i++) {
        const end = i + weakLen;
        let left = i;
        let cnt = 0;

        for (let elem of arr) {
          if (left &amp;gt;= end) break;
          cnt += 1;
          const maxDist = elem + flattenWeak[left];

          while (left &amp;lt; end &amp;amp;&amp;amp; maxDist &amp;gt;= flattenWeak[left]) {
            left++;
          }
        }

        if (left &amp;lt; end) continue;

        answer.push(cnt);
      }
      return;
    }

    for (let i = 0; i &amp;lt; distLen; i++) {
      if (visits[i]) continue;
      visits[i] = 1;
      permutation(L + 1, [...arr, dist[i]]);
      visits[i] = 0;
    }
  }

  permutation(0, []);

  return answer.length === 0 ? -1 : Math.min(...answer);
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DS &amp;amp; Algorithm/programmers</category>
      <category>외벽 점검 javascript</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/131</guid>
      <comments>https://gobae.tistory.com/131#entry131comment</comments>
      <pubDate>Thu, 10 Mar 2022 21:30:28 +0900</pubDate>
    </item>
    <item>
      <title>브라우저/노드 환경에서 모듈, AMD, CommonJS, UMD 알아보기</title>
      <link>https://gobae.tistory.com/130</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qZ2R9/btrtlsVfwVn/8yU5v3kGS4vTdqNZLfhWi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qZ2R9/btrtlsVfwVn/8yU5v3kGS4vTdqNZLfhWi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qZ2R9/btrtlsVfwVn/8yU5v3kGS4vTdqNZLfhWi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqZ2R9%2FbtrtlsVfwVn%2F8yU5v3kGS4vTdqNZLfhWi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;716&quot; height=&quot;330&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트와 백엔드 공부를 하면서 상당히 혼동을 줬던 개념들이 포함된 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 다른 자바스크립트 파일에 선언한 변수들이 같은 공간에서 존재하던 문제도 겪었고&amp;nbsp;현재 파일에서 다른 파일을 불러올 때 exports/require, export/import 등을 사용해서 불러오는데&amp;nbsp;무엇을 어느 상황에서 사용해야 하는지, 그리고 왜 babel을 써야 export/import를 사용할 수 있는가..? 등등 여러 문제가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번 포스팅에서는 모듈이 무엇이고 어떻게 발전해왔으며 프론트와 백엔드에서 어떤 차이가 있는지를 모두 다뤄볼 예정이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모듈&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈을 검색해보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. (명사) 모듈, 교과목 단위(특히 영국 대학에서 한 교육 과정의 일부가 되는 단위)&lt;br /&gt;2. (명사) 컴퓨터 모듈(특정 기능을 하는 컴퓨터 시스템이나 프로그램의 단위)&lt;br /&gt;3. (명사) 모듈, 조립 부품(기계&amp;middot;가구&amp;middot;건물 등을 구성하는 규격화된 부품)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 뜻은 다르지만, &lt;b&gt;공통적으로 어떤 기능적인 단위를 의미&lt;/b&gt;하는 것은 분명하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 알아볼 모듈은, &lt;b&gt;어떤 기능들(그것에 대한 코드)이 모여있는 하나의 파일&lt;/b&gt;로 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대개 하나의 모듈은, 여러 기능을 포함한 하나의 클래스이거나 특정 목적을 가진 라이브러리 하나로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람을 구현한 Person 클래스가 있다면 모듈이 될 수 있고, 흔히 npm install로 받는 의존성들도 모듈에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 결국 모듈은 &lt;b&gt;하나의 파일&lt;/b&gt;를 의미한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 모듈이 중요한가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈에 대해 이해하지 못한 상태에서 브라우저에서 겪을 수 있는 문제를 통해 먼저 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1644903946904&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// index.html

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;hello world!&amp;lt;/h1&amp;gt;
  &amp;lt;/body&amp;gt;
  &amp;lt;script src=&quot;./first.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script src=&quot;./second.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/html&amp;gt;


// first.js

const name = 'Kim';

// second.js

const name = 'Lee';
console.log(name);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cs8SGC/btrtoAsnt8K/aqi6IyGw9UeH0FKH3iym8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cs8SGC/btrtoAsnt8K/aqi6IyGw9UeH0FKH3iym8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cs8SGC/btrtoAsnt8K/aqi6IyGw9UeH0FKH3iym8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcs8SGC%2FbtrtoAsnt8K%2Faqi6IyGw9UeH0FKH3iym8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;396&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;second.js 스크립트에서 이미 선언된 name 변수를 이용했다는 에러를 발생시키는데,&amp;nbsp;스크립트 태그로 분리된 것 처럼 보이는 first.js와 second.js가 사실 연관되어 있음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 first.js와 second.js가 같은 공간을 공유하기 때문에, 같은 변수를 선언하면 변수 충돌이 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;first, second로 다른 파일을 만들었다는 것은, 개발자가&amp;nbsp;두 파일이 다른 공간을 가졌기를 의도했을 것이다.&lt;br /&gt;이렇게 다른 파일이 같은 공간을 이용한다면, 사용한 모든 변수를 기억해야 하며 구현한 기능이 변수를 재사용함으로서 다르게 동작할 수도 있겠다.&lt;br /&gt;심지어 라이브러리를 가져다 쓰는 상황에서는 라이브러리에 무슨 변수가 선언되어있는지를 열심히 걱정해야 할 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 예시를 통해&amp;nbsp;&lt;b&gt;하나의 파일이 독립된 하나의 공간을 가져야 한다.&lt;/b&gt; 라는 필요성을 느끼게 되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그래서 각 파일이 모듈이 되면 어떤 장점들이 있는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.유지보수성 용이&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앞서 하나의 모듈은 어떤 기능들을 모아뒀다고 했다. 그러므로 모듈화가 잘 되었다면 테스트, 통합, 수정 등의 유지보수에 용이하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.네임스페이스화&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네임스페이스란, 하나의 공간에 하나의 개체가 존재한다는 것이다.&lt;/li&gt;
&lt;li&gt;즉, 자바스크립트 파일마다 각각 고유한 공간을 가질 수 있게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.재사용 용이&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 모듈로 인해 반복을 줄이고 재사용성을 늘릴 수 있다.&lt;/li&gt;
&lt;li&gt;예를 들면 타이어 모듈을 금호 타이어, 한국 타이어 등등의 모듈을 구현할 때 재사용 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.교체 용이&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3번과 비슷한 개념이지만, 각 모듈은 독립적이므로 필요하다면 모듈을 다른 모듈로 교체할 수도 있다.&lt;/li&gt;
&lt;li&gt;예를 들면 컴퓨터에 다양한 SSD, 그래픽카드들을 추가할 수 있다. 단, 이를 위해서는 미리 표준을 만들고 표준을 따르도록 제작되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점을 요약하자면 모듈 내에서의 응집도는 증가하는 방향이며, 모듈 간 결합도는 낮아지는 방향이기 때문에 그에 따른 효과들을 기대할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바스크립트에서 모듈&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트(브라우저, Node 환경을 모두 포함)에서 모듈이 가지는 특징을 정리하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.엄격모드(strict mode)로 실행된다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄격모드에 대해서는 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Strict_mode&quot;&gt;MDN&lt;/a&gt;을 참고하면 되겠다. 엄격모드에서의 주요 특징으로는 자바스크립트의 느슨한 문법을 기반으로 실수하기 쉬운 사항들을 오류로 발생시켜주고, 기존의 최상위 this가 브라우저에서 window, Node 환경에서 global이 아닌 undefined 값을 가지도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.모듈 레벨 스코프를 가진다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 배운대로, 모듈은 자신의 스코프를 가진다. 그래서 모듈을 외부에 공개하지 않는다면 외부에서 모듈 내부로 접근할 수 없다. 모듈을 외부에 공개하기 위해서는 define / exports / export 키워드를 이용하며 해당 모듈을 가져오기 위해서는 require / import 키워드를 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.최초 호출시 단 한번만 평가된다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a.js 모듈을 재사용하면서 b.js, c.js 등등 여러 모듈에서 불러올 수 있다. 모듈은 최초 호출시에만 실행되며 그 결과를 모듈을 이용하려는 모든 다른 모듈에 내보내준다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    // a.js
    console.log('hello world!');
    exports.person = {};

    // b.js
    const a = require('./a.js');
    a.person.name = 'Kim';

    // c.js
    const { person } = require('./a.js');
    console.log(person.name);

    // index.js
    const b = require('./b.js');
    const c = require('./c.js');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index.js에서 b.js, c.js를 호출하고 b.js, c.js에서 a.js를 각각 호출하는 구조이다. index.js를 실행하면 b.js에서 person 객체에 이름 속성을 추가하며, c.js에서는 person 객체에 이름이 존재하는지 확인한다. 그리고 실행 결과로 'hello world!' 그리고 'Kim'이 출력되는데, a.js가 2번 호출되었지만 최초 호출시에만 평가되므로 'hello world!'는 한 번 출력되며, a.js의 평가 이후 내보내지는 person 객체는 어떤 모듈에서 호출되던지 간에 동일하므로 b.js에서 person 객체에 name 속성을 부여하고 c.js에서 person 객체의 name 속성을 이용할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;브라우저 환경에서의 스크립트 모듈화와 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서는 script 태그에 &lt;b&gt;type='module' 속성을 추가&lt;/b&gt;해야 각 스크립트를 모듈로 만들 수 있다.&lt;br /&gt;브라우저 환경에서는 index.html 등의 html 파일에 script 태그를 이용해 자바스크립트 코드를 실행한다. 이 때 type='module' 속성을 추가하지 않고 &amp;lt;script&amp;gt;&amp;nbsp;태그와 코드만을 이용하면 각 스크립트가 모듈화되지 않고 같은 스코프를 공유하게 된다. 그렇게 되면 위에서 다룬 변수 충돌 문제가 발생할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 환경에서 스크립트를 type='module' 속성을 통해 모듈화를 이루고 그로 인해 추가되는 기능들을 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(&lt;s&gt;사실 먼저 AMD, CommonJS 와 같은 방법을 다뤄야 하는데, 어쩌다보니 이부분을 적게 되었다..&lt;/s&gt;)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 지연실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 스크립트는 지연 실행되는데, 마치 defer 속성처럼 동작한다. defer 속성에 대해서는 &lt;a href=&quot;https://gobae.tistory.com/110&quot;&gt;defer, async 포스팅&lt;/a&gt;에 정리해두었다. 그래서 defer의 특징에 따라 스크립트 다운 시 HTML 파싱을 멈추지 않으며 DOM이 만들어진 이후 DOMContentLoaded 이벤트가 발생하기 직전에 모듈 스크립트가 실행된다. 또한 모듈 스크립트 간의 순서를 유지하면서 실행을 보장할 수 있게 된다. DOMContentLoaded 이벤트 발생 전에 모듈 스크립트가 실행되므로, 항상 모듈 스크립트는 완전한 DOM(완전한 페이지)를 볼 수 있다. 만약 특정 태그에 이벤트리스너를 달아준다면, 항상 모든 태그가 생성되고 난 후 코드가 실행되므로 오류를 걱정하지 않아도 된다. 그러나 만약 특정 태그가 모듈 스크립트에 정의된 기능들을 기반으로 동작해야 한다면, (예를 들어 결제 버튼 태그라던가.) 모듈 스크립트가 완전히 불러와지기 전까지는 로더 등을 통해 사용자의 혼란을 예방해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;추가로 모듈 스크립트와는 달리 일반 스크립트는 바로 실행되므로 주의해야 한다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;hello world!&amp;lt;/h1&amp;gt;
  &amp;lt;/body&amp;gt;
  &amp;lt;script type=&quot;module&quot;&amp;gt;
    console.log('hello, this is module script!');
  &amp;lt;/script&amp;gt;
  &amp;lt;script&amp;gt;
    console.log('hello world! this is normal script');
  &amp;lt;/script&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IAMGa/btrtoAsoabQ/9hO2RJ3saBk8KrubXvldx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IAMGa/btrtoAsoabQ/9hO2RJ3saBk8KrubXvldx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IAMGa/btrtoAsoabQ/9hO2RJ3saBk8KrubXvldx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIAMGa%2FbtrtoAsoabQ%2F9hO2RJ3saBk8KrubXvldx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1544&quot; height=&quot;424&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 스크립트와 일반 스크립트 동작의 차이를 이해하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 인라인 스크립트의 비동기 처리가 가능하다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트의 비동기 처리를 위해서는 async 속성을 이용하는데, async 속성은 외부 스크립트에만 사용이 가능하다. 여기서 외부 스크립트는 src='a.js'와 같은 속성을 이용한 스크립트이며 인라인 스크립트는 HTML의 &amp;lt;script&amp;gt;&amp;nbsp;태그 안에 직접 코드를 작성한 스크립트를 의미한다. 모듈 스크립트에서는 인라인 스크립트에 async 속성을 이용하여 비동기 처리를 할 수 있다. asnyc 속성이 그러하듯이, 실행순서를 보장하지 않으므로 광고나 서드파티 스크립트 등으로 이용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 외부 스크립트와의 관계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src 속성의 값이 동일한 외부 스크립트는 한 번만 실행됨을 보장하며, 완전히 다른 오리진(출처)로부터 모듈 스크립트를 불러올 때 CORS 헤더를 필요로 한다. CORS에 따르면 현재 브라우저에서 다른 오리진으로부터 데이터를 받아오려면 다른 오리진(다른 서버)이 Access-Control-Allow-Origin 헤더로 *값 혹은 허용할 도메인으로 현재 도메인을 명시해둬야 한다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;    &amp;lt;!-- a.js는 한번만 실행된다. --&amp;gt;
    &amp;lt;script type=&quot;module&quot; src=&quot;a.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&quot;module&quot; src=&quot;a.js&quot;&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;!-- another-site.com이 Access-Control-Allow-Origin을 통해 허용해야 한다.--&amp;gt;
    &amp;lt;!-- 그렇지 않으면 스크립트는 실행되지 않는다.--&amp;gt;
    &amp;lt;script type=&quot;module&quot; src=&quot;http://another-site.com/their.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 외부 모듈을 불러올 때 경로를 명시해야 한다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로로는 절대경로 혹은 상대경로를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;    import hello from './hello.js';
    // 가능

    import hello from 'hello.js';
    // 불가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. nomodule 속성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구식 브라우저에서는 type='module'을 해석하지 못할 수 있는데, nomodule 속성으로 이 상황을 대비할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;  &amp;lt;script type=&quot;module&quot;&amp;gt;
    alert(&quot;모던한 브라우저입니다.&quot;);
  &amp;lt;/script&amp;gt;

  &amp;lt;script nomodule&amp;gt;
    alert(&quot;type=module을 해석할 수 있는 브라우저는 nomodule 타입의 스크립트를 실행하지 않습니다.&quot;)
    alert(&quot;구식 브라우저에서는 type=module이 붙은 스크립트가 무시되며 nomodule 속성을 가진 스크립트가 실행됩니다.&quot;);
  &amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 브라우저 환경에서 모듈 스크립트가 가지는 특징들을 정리해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import/export를 사용하는 방식은 ES2015에서 도입된 모듈 시스템으로, 이를 다루기 전에&amp;nbsp;&lt;b&gt;ES6 이전 모듈화를 위해 등장했던 AMD, CommonJS, UMD 방식&lt;/b&gt;에 대해 알아보겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CommonJS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트가 브라우저에 종속된 언어에서 node를 통해 서버에서도 사용 가능한 언어가 되면서, 노드에서 채택한 방식이다. 일반적으로 &lt;b&gt;서버(node 환경)에서는 CommonJS&lt;/b&gt;를 사용하며 &lt;b&gt;프론트(브라우저 환경)에서는 AMD&lt;/b&gt;와 더 관련있다고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CommonJS에서는 모듈을 불러올 때 require, 모듈을 내보낼 때 module.exports, exports를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    // hello.js
    const helloWorld = 'hello World';

    module.exports = { helloWorld };

    // world.js
    const hello = require('./hello.js');
    console.log(hello.helloWorld);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내보낼 때 module 예약어를 이용하는데, 이는 현재 모듈(현재 파일)에 대한 정보를 가진 객체다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 객체를 console.log(module)을 찍어보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czoSgf/btrtjOSruTN/01OwhClWuIYCMMRwjAnlX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czoSgf/btrtjOSruTN/01OwhClWuIYCMMRwjAnlX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czoSgf/btrtjOSruTN/01OwhClWuIYCMMRwjAnlX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczoSgf%2FbtrtjOSruTN%2F01OwhClWuIYCMMRwjAnlX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;379&quot; height=&quot;476&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 객체가 출력되는데, id, path, exports, filename 등등의 프로퍼티가 있으며 exports 프로퍼티에 주목해보자.&lt;br /&gt;exports 프로퍼티의 값에 해당하는 객체 내에 helloWorld 변수가 프로퍼티로 선언되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;exports 와 module.exports&lt;/h4&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;    // hello.js
    const helloWorld = 'hello World';

    module.exports = { helloWorld }; // module.exports
    exports.helloWorld = helloWorld; // exports&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈을 내보내는 방법으로 exports, module.export를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;module 객체에서 봤듯이, module 객체의 exports 프로퍼티는 값으로 빈 객체를 참조한다.&lt;br /&gt;require 키워드를 통해 외부 모듈을 불러올 때는 항상 module.exports 객체를 리턴받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exports 키워드는 module.exports를 참조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exports 키워드가 module.exports를 참조하므로, exports와 . 을 사용하면 module.exports 객체의 프로퍼티를 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;require 키워드는 module.exports를 참조하므로 결국 exports.property로 내보낸 각각의 기능은 module.exports의 프로퍼티가 되어 require로 불러올 수 있게 되고, module.exports에 직접 객체를 할당하면(ex. module.exports={helloWorld}) 역시나 require는 module.exports를 참조하므로 동일하게 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    // hello.js
    const helloWorld = 'hello World!';
    exports.helloWorld = helloWorld;


    // world.js
    const { helloWorld } = require('./hello.js'); // 꼭 exports 
    const hello = require('./hello.js');

    console.log(helloWorld); // hello World!
    console.log(hello.helloWorld); // hello World!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종종 require에서 exports 키워드로 내보낸 대상에 대해서는 {}로 가져오고 module.exports로 내보낸 대상에 대해서는 변수 자체로 가져오곤 하는데, require가 module.exports를 참조하며 module.exports가 객체이며 exports는 객체 프로퍼티를 생성함을 이해하면 원하는대로 가져오기를 수행할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AMD (Asynchronous Module Definition)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직독직해하면 비동기적인 모듈 선언 이라는 의미이다. RequireJS 라는 스크립트가 AMD 스펙을 구현해두었으며, CommonJS가 노드에서 사랑받는 반면, AMD는 브라우저 환경에서 사랑받는다. 브라우저에서 스크립트를 비동기적으로 실행하기 위함이다. define, require 함수를 이용하며 예시를 통해 확인해보자.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;  &amp;lt;!DOCTYPE html&amp;gt;
  &amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;script src=&quot;require.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
  &amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;require.js를 다운받아서 스크립트에 넣어두고, 모듈 코드를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    // exModule.js
    define(['hello', 'world'], function(h,w) {
      return {
        a: h,
        b: w,
        printA : () =&amp;gt; console.log('a'),
      }
    });

    // example.js
    require(['exModule'], function (exModule) {
      console.log(exModule.a); // hello
      console.log(exModule.b); // world
      exModule.printA(); // a
    });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 모듈 exModule을 만들고 define 함수를 통해 정의한다. require를 통해 첫번째 인자 'exModule' 모듈이 로드되었을 때 변수 exModule로 그것을 받고 define에 정의해 둔 모듈의 프로퍼티들을 이용할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;UMD (Universal Module Definition)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 시스템이 AMD, CommonJS로 나뉘면서 서로 호환이 되지 않았는데, 어떤 모듈을 쓰던지 동작하게 하기 위해 UMD가 등장했다.&lt;br /&gt;AMD가 define을 사용하며 CommonJS가 module.exports를 사용하는 차이를 기반으로 UMD 모듈을 선언할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 &lt;a href=&quot;https://github.com/umdjs/umd/blob/master/templates/returnExports.js&quot;&gt;공식 UMD 소스코드&lt;/a&gt;이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;(function (root, factory) {
    if (typeof define === 'function' &amp;amp;&amp;amp; define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);
    } else if (typeof module === 'object' &amp;amp;&amp;amp; module.exports) {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory();
    } else {
        // Browser globals (root is window)
        root.returnExports = factory();
  }
}(typeof self !== 'undefined' ? self : this, function () {

    // Just return a value to define the module export.
    // This example returns an object, but the module
    // can return a function as the exported value.
    return {};
}));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수는 즉시실행함수로 구현되어 있다. 즉 선언과 동시에 실행되며 모듈이 AMD인지 CommonJS인지 혹은 브라우저 방식인지에 따라 달리 실행된다. 차근차근 살펴보면 if문의 처음에 해당하는 AMD인 경우, 두번째에 해당하는 CommonJS인 경우에는 factory 부분이 각자의 콜백 함수 또는 모듈 객체가 된다. factory 콜백으로는 객체를 생성하는 함수를 인수로 넣었으므로, 각각 define([], {}) / module.exports={} 를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;반면 else에 해당하는 브라우저 방식의 경우엔 root에 해당하는 인수로 들어온 this가 window이기 때문에 root도 window가 되며 window.myModule에 factory의 실행결과 객체가 담기게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 UMD 소스코드에서는 각각의 환경에서 모두 모듈개념을 사용할 수 있도록 돕는다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;현재의 브라우저와 노드 환경, 자잘한 팁&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 브라우저에서는 ES2015(ES6)에서 등장한 ESModule을 기반으로 하며, 노드 환경에서는 CommonJS를 기반으로 한다.&lt;br /&gt;ESModule에 대해서는 다음 포스팅에서 알아볼 것이고, 노드 환경에 대해서만 가볍게 다뤄보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node 환경에서 자바스크립트 코드를 실행하면 CommonJS가 기반이므로 module.exports를 사용할 수 있다. 그러나 export, import 또한 사용할 수 있다. &lt;u&gt;&lt;b&gt;package.json에서 &quot;type&quot; 속성을 &quot;module&quot;로 지정&lt;/b&gt;&lt;/u&gt;하면 된다. type 속성은 commonjs, module 2가지 옵션을 설정 할 수 있는데 기본값이 commonjs이며 module 옵션을 설정하면 node 환경에서 export, import 키워드를 이용할 수 있다. 단, module 옵션을 설정하면 더이상 module.exports, require 방식을 사용할 수 없다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;    // package.json
   {
    &quot;name&quot;: &quot;node&quot;,
    &quot;version&quot;: &quot;1.0.0&quot;,
    &quot;description&quot;: &quot;&quot;,
    &quot;main&quot;: &quot;b.js&quot;,
    &quot;scripts&quot;: {
      &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;
    },
    &quot;author&quot;: &quot;&quot;,
    &quot;license&quot;: &quot;ISC&quot;,
    &quot;type&quot;: &quot;module&quot;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1950&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uX6aT/btrtlUriTXj/bCEaDZLuA7izRPGUSQBwf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uX6aT/btrtlUriTXj/bCEaDZLuA7izRPGUSQBwf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uX6aT/btrtlUriTXj/bCEaDZLuA7izRPGUSQBwf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuX6aT%2FbtrtlUriTXj%2FbCEaDZLuA7izRPGUSQBwf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;713&quot; height=&quot;398&quot; data-origin-width=&quot;1950&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 babel과 같은 트랜스파일러를 통해 import 문을 commonJS 방식으로 트랜스파일링 해줄 수 있다. 이 때 바벨을 사용하기 위해선 바벨 환경을 설정해야 하며, 흔히 사용하는 babel/preset-env를 부여하거나 @babel/plugin-transform-modules-commonjs 옵션을 사용하고 node 명령어 대신 babel-node 명령어를 통해 자바스크립트 파일을 실행하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 자바스크립트가 브라우저에 종속된 언어에서 서버에서 사용할 수 있는 언어가 되면서 주요한 이슈였던 모듈 시스템에 대해서 알아보았다. 다음 ES Module 포스팅에서는 ES Module이 무엇이며 노드환경에서 채택된 CommonJS와의 차이, import, export 사용법 등등에 대해 정리해보겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고한 사이트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/baeharam/Must-Know-About-Frontend/blob/main/Notes/javascript/module.md&quot;&gt;https://github.com/baeharam/Must-Know-About-Frontend/blob/main/Notes/javascript/module.md&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://www.zerocho.com/category/JavaScript/post/5b67e7847bbbd3001b43fd73&quot;&gt;https://www.zerocho.com/category/JavaScript/post/5b67e7847bbbd3001b43fd73&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://ko.javascript.info/modules-intro&quot;&gt;https://ko.javascript.info/modules-intro&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://stackoverflow.com/questions/7137397/module-exports-vs-exports-in-node-js&quot;&gt;https://stackoverflow.com/questions/7137397/module-exports-vs-exports-in-node-js&lt;/a&gt;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 모듈 시스템</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/130</guid>
      <comments>https://gobae.tistory.com/130#entry130comment</comments>
      <pubDate>Tue, 15 Feb 2022 15:06:48 +0900</pubDate>
    </item>
    <item>
      <title>병합 정렬(Merge Sort) 이란</title>
      <link>https://gobae.tistory.com/129</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vYVBs/btrsTcrvkgh/rYLtbyZ4NHFPTolHQrktcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vYVBs/btrsTcrvkgh/rYLtbyZ4NHFPTolHQrktcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vYVBs/btrsTcrvkgh/rYLtbyZ4NHFPTolHQrktcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvYVBs%2FbtrsTcrvkgh%2FrYLtbyZ4NHFPTolHQrktcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1504&quot; height=&quot;370&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;분할 정복&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병합 정렬을 학습하기 전에 분할정복에 대해 먼저 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분할 정복(Devide and Conquer)이란, 문제를 나눠서 각각을 풀고 다시 합병해서 답을 도출하는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 Devide, Conquer, Combine의 스텝으로 이루어지며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Devide는 현재 문제가 분할이 가능할 때, 2개 이상의 문제로 나누고&lt;br /&gt;Conquer는 Devide를 통해 나눈 문제가 여전히 분할이 가능하면, 분할된 문제에 재차 Devide를 수행함을 의미한다.&lt;br /&gt;Combine은 devide and conquer를 수행하고 더이상 나눠지지 않는 문제를 합병해 답을 도출하는 과정을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분할 정복을 채택한 알고리즘에는 대표적으로 병합 정렬, 퀵 정렬 등이 있으며 이미 퀵 정렬은 다루었으니 병합 정렬을 살펴보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;병합 정렬&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병합 정렬이라고도 하고, 합병 정렬이라고도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;퀵정렬과 동일하게 O(NlogN)의 시간복잡도를 가지는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퀵정렬이 데이터가 편향 될 가능성이 있음과는 달리,&lt;br /&gt;병합 정렬은 데이터를 항상 &lt;b&gt;정확히 반씩 나누기 때문에&lt;/b&gt; 최악에도 O(NlogN)을 보장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분할 정복이 나눠서 풀고 다시 합병해서 답을 도출한다고 했는데, 이를 토대로 병합정렬을 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병합 정렬의 과정은 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DBgrk/btrsRZe3OnU/aA5x6SKPAZsKKrYSnSgzG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DBgrk/btrsRZe3OnU/aA5x6SKPAZsKKrYSnSgzG0/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://www.101computing.net/merge-sort-algorithm/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DBgrk/btrsRZe3OnU/aA5x6SKPAZsKKrYSnSgzG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDBgrk%2FbtrsRZe3OnU%2FaA5x6SKPAZsKKrYSnSgzG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;992&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://www.101computing.net/merge-sort-algorithm/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 배열을 반으로 나눈다.&lt;br /&gt;2. 배열이 더이상 나눠지지 않을 때 까지 나눠진 배열에 1번 과정을 반복한다.&lt;br /&gt;3. 크기가 1인 각 배열을 두개씩 묶어서 합쳐주며, 크기가 2인 배열로 만들어준다. 이 때 하나의 배열로 만들면서 정렬을 수행한다.&lt;br /&gt;4. 병합한 배열이 N의 크기를 가질 때 까지 3번과 동일하게 병합 정렬을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gif를 통해 알아보기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciCWmJ/btrsQqK3ZPl/TZzTsC4wWSfSuz7k38Jink/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciCWmJ/btrsQqK3ZPl/TZzTsC4wWSfSuz7k38Jink/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciCWmJ/btrsQqK3ZPl/TZzTsC4wWSfSuz7k38Jink/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ciCWmJ/btrsQqK3ZPl/TZzTsC4wWSfSuz7k38Jink/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;220&quot; height=&quot;132&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;병합 정렬을 JS를 이용해 구현하기&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function mergeSort(arr, start, end){
  if(start &amp;lt; end){
    const mid = Math.floor((start + end) / 2);
    mergeSort(arr, start, mid);
    mergeSort(arr, mid+1, end);
    merge(arr, start, mid, end);
  }
}

function merge(arr, start, mid, end){
  let i = start;
  let j = mid + 1;
  let idx = start;

  while(i &amp;lt;= mid &amp;amp;&amp;amp; j &amp;lt;= end){
    sortedArray[idx++] = arr[i] &amp;lt;= arr[j] ? arr[i++] : arr[j++];
  }
  if(i &amp;gt; mid){
    for(let t=j; t&amp;lt;=end; t++){
      sortedArray[idx++] = arr[t];
    }
  } else{
    for(let t=i; t&amp;lt;=mid; t++){
      sortedArray[idx++] = arr[t];
    }
  }

  for(let t=start; t&amp;lt;=end; t++){
    arr[t] = sortedArray[t];
  }
}

const array = [5, 7, 9, 0, 3, 1, 6, 2, 4, 8];
const length = array.length;
const sortedArray = Array.from({length}, ()=&amp;gt;0);

mergeSort(array, 0, length - 1);
console.log(array); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;병합 정렬의 시간 복잡도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 N개에 대해서 2*N개의 공간이 필요하므로 공간복잡도는 O(N)이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간복잡도는 최선, 최악 모두 O(NlogN)을 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단계마다 데이터가 항상 반으로 나뉘고, 2배씩 증가하므로 단계의 크기가 logN을 유지하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 정렬에 필요한 수행시간은 데이터 갯수인 N을 요구하므로, O(NlogN)을 나타나게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 수행 시간이 N인 이유는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ekjwfL/btrsRPcJ92j/WHzmqjm54qXhF06aBwZKc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ekjwfL/btrsRPcJ92j/WHzmqjm54qXhF06aBwZKc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ekjwfL/btrsRPcJ92j/WHzmqjm54qXhF06aBwZKc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FekjwfL%2FbtrsRPcJ92j%2FWHzmqjm54qXhF06aBwZKc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;481&quot; height=&quot;516&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;step1에서 크기가 1인 배열을 합칠 때 각 인덱스를 i, j로 조회하여 [7], [3] =&amp;gt; [3, 7]의 배열을 만든다.&lt;br /&gt;step2에서 크기가 2인 배열을 합칠 때 각 인덱스를 i, j로 조회하여 [3, 7], [2, 16] =&amp;gt; [2, 3, 7, 16] 의 배열을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;step 1,2에서 합치기 전의 두 배열의 특징은 이전 과정에 의해 &lt;b&gt;이미 정렬 된 배열&lt;/b&gt;인 것이고,&lt;br /&gt;&lt;b&gt;배열 각각을 i,j 인덱스를 통해 처음부터 끝까지만 순회하면 새로운 정렬된 병합 배열을 만들 수 있기&lt;/b&gt; 때문에 비교작업을 총 N번만 수행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 스텝마다 총 N번의 비교 수행이 이루어지며, 병합의 스텝은 데이터가 절반씩 줄어드므로 logN번을 수행하게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안정 정렬에 속한다.(=중복 값이 입력 순서와 동일한 정렬 순서를 보장한다.)&lt;br /&gt;최악의 경우에도 O(NlogN)의 성능을 보장한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 배열 공간을 필요로한다. (제자리 정렬이 아니다.)&lt;br /&gt;무조건 절반으로 분할하므로, O(NlogN)에서 성능이 달라지지 않는다.&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/theory</category>
      <category>병합정렬 자바스크립트</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/129</guid>
      <comments>https://gobae.tistory.com/129#entry129comment</comments>
      <pubDate>Tue, 8 Feb 2022 23:12:49 +0900</pubDate>
    </item>
    <item>
      <title>계수 정렬(Counting Sort) 이란</title>
      <link>https://gobae.tistory.com/128</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgvsyF/btrsMWoFFzT/Ss9JeihO0xXeEQkppBpSK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgvsyF/btrsMWoFFzT/Ss9JeihO0xXeEQkppBpSK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgvsyF/btrsMWoFFzT/Ss9JeihO0xXeEQkppBpSK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgvsyF%2FbtrsMWoFFzT%2FSs9JeihO0xXeEQkppBpSK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1452&quot; height=&quot;386&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;계수 정렬&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정한 조건이 부합할 때, 배열 원소 간 비교하지 않고 정렬하는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정한 조건이란, 원소들이 정수이며 0~K(K=정수)의 범위 안에 포함될 때를 가정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 데이터 개수가 N개이며, 모두 0이상의 정수이며 데이터 중 최댓값이 K일 때 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계수 정렬은, 각각 데이터가 몇 번 씩 등장했는지를 세는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 가장 큰 데이터의 크기로 배열을 선언해서 0부터 K까지 모든 범위를 포함할 수 있어야 하며,&lt;br /&gt;배열 인덱스를 이용하기 때문에 0이상의 정수를 데이터로 갖는 배열을 가정해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;계수 정렬을 수행하는 순서를 살펴보자.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 인덱스 K를 참조할 수 있는 K+1 크기의 배열 countArr을 선언하고 모든 인덱스의 값을 0으로 초기화한다.&lt;br /&gt;2. 원본 배열의 첫번째 인덱스부터 순회하면서, 원본 배열의 데이터와 동일한 countArr 배열의 인덱스의 값을 1씩 증가시킨다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; ex) 예를 들어 원본 배열의 2번째 인덱스 값이 3이라면, countArr[3]++; 을 수행한다.&lt;br /&gt;3. 원본 배열의 마지막 인덱스까지 순회하고 나면, countArr 배열에는 원본 배열의 등장 횟수가 담긴다.&lt;br /&gt;4. countArr 배열의 인덱스를 순회하면서, 인덱스의 값이 0이 될 때 까지 출력해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;gif를 통해 이해해보자.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mBjCG/btrsLwqGz5c/doUF4IkiUx2aTNpEBYzk6k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mBjCG/btrsLwqGz5c/doUF4IkiUx2aTNpEBYzk6k/img.gif&quot; data-alt=&quot;출처 :&amp;amp;nbsp; https://www.geeksforgeeks.org/counting-sort-visualization-using-javascript/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mBjCG/btrsLwqGz5c/doUF4IkiUx2aTNpEBYzk6k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/mBjCG/btrsLwqGz5c/doUF4IkiUx2aTNpEBYzk6k/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;432&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp; https://www.geeksforgeeks.org/counting-sort-visualization-using-javascript/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JS 코드로 작성해보았다.&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const arr = [4, 4, 3, 5, 1, 2, 0, 8, 3, 6];

function countingSort(arr){
	const max = Math.max(...arr);
	const len = arr.length;
	const indexArr = Array.from({length:max + 1}, ()=&amp;gt;0);

	for(let i=0; i&amp;lt;len; i++){
		indexArr[arr[i]]++;
	}
	
	let idx = 0;
	for(let i=0; i&amp;lt;=max; i++){
		while(indexArr[i] &amp;gt; 0){
			arr[idx++] = i;
			indexArr[i]--;
		}
	}
}

countingSort(arr);
console.log(arr);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열에서 최댓값을 찾고, 최댓값+1의 크기로 배열을 선언한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;arr 배열을 조회하며, 값에 해당하는 indexArr 인덱스의 값을 1씩 증가시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;indexArr 배열을 조회하며, 각 인덱스의 값이 0이 될 때 까지 arr 배열에 순차적으로 현재 인덱스 값을 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 [4, 4, 3, 5, 1, 2, 0, 8, 3, 6] 배열이 [0, 1, 2, 3, 3, 4, 4, 5, 6, 8] 배열로 정렬된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;계수 정렬의 복잡도&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계수 정렬은 시간복잡도, 공간복잡도가 모두 O(N+K)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계수를 기록하고 풀어주는 반복문을 각각 수행하므로, 전체 소스코드의 시간복잡도가 O(N+K)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공간복잡도 역시 정렬을 수행할 데이터 개수 N개, 최댓값 K까지 공간이 필요하므로, N+K이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘에서 데이터 간 비교를 수행하지 않기 때문에 O(N)의 시간 복잡도의 성능을 보여준다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이는, 기존에 우수한 정렬 알고리즘으로 평가받는 퀵 정렬, 병합 정렬 O(NlogN) 보다 더 빠르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 정수 표현이 가능하고, 데이터 간 차이가 크지 않을 때 유리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 메모리 공간을 필요로 한다.(K 크기의 배열을 만들어야 한다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 데이터가 5개 존재하는데 각각 0, 3, 2, 1, 1000000 이라면?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/theory</category>
      <category>계수정렬 자바스크립트</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/128</guid>
      <comments>https://gobae.tistory.com/128#entry128comment</comments>
      <pubDate>Mon, 7 Feb 2022 21:22:59 +0900</pubDate>
    </item>
    <item>
      <title>퀵 정렬(Quick Sort) 이란</title>
      <link>https://gobae.tistory.com/127</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKvxDn/btrsLTMF3Ue/Na6srNQUdW9PU5zAcEU2m0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKvxDn/btrsLTMF3Ue/Na6srNQUdW9PU5zAcEU2m0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKvxDn/btrsLTMF3Ue/Na6srNQUdW9PU5zAcEU2m0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKvxDn%2FbtrsLTMF3Ue%2FNa6srNQUdW9PU5zAcEU2m0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1518&quot; height=&quot;392&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;퀵 정렬&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기준 데이터를 설정&lt;/b&gt;하고, 기준보다 큰 데이터와 작은 데이터의 위치를 바꾸는 정렬 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퀵 정렬에서 선택한&amp;nbsp;&lt;b&gt;기준 데이터&lt;/b&gt;를&amp;nbsp;&lt;b&gt;피봇 데이터&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 상황에서 이용되는 &lt;b&gt;표준 정렬 알고리즘&lt;/b&gt;이며,&lt;br /&gt;Merge Sort(병합 정렬)과 마찬가지로 &lt;u&gt;&lt;b&gt;정렬 라이브러리의 근간&lt;/b&gt;&lt;/u&gt;이 되곤 하는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퀵 정렬의 과정은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 기준(피봇) 데이터를 설정한다.(기본적인 형태에서는 첫번째 데이터를 피봇으로 삼는다.)&lt;br /&gt;2.1. 배열 처음부터 오른쪽으로 순회하며 피봇보다 큰 데이터의 인덱스를 찾는다.&lt;br /&gt;2.2. 배열 마지막부터 왼쪽으로 역순회하며 피봇보다 작은 데이터의 인덱스를 찾는다.&lt;br /&gt;2.3. 작은 데이터와 큰 데이터를 기준으로, 두 인덱스에 위치한 데이터를 swap 할 때 두 데이터가 오름차순으로 정렬된다면 swap을 수행한다.&lt;br /&gt;2.4. 그렇지 않다면 피봇과 작은 데이터의 위치를 swap한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;b&gt;2.4&lt;/b&gt;의 과정이 일어날 때 까지 스왑을 반복하며, 피봇이 이동하면 피봇 왼쪽과 오른쪽 배열에 대해서 퀵 정렬을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피봇 왼쪽 배열은 작은 데이터들의 집합이며 오른쪽은 큰 데이터들의 집합이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 배열 모두 정렬되어있지 않으므로 각각 퀵 정렬을 수행해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 배열에 재귀적으로 퀵정렬을 수행하며, 정렬을 위한 범위를 점점 좁혀가는 방식으로 동작하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gif를 통해 이해해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJzffk/btrsNwQR402/t5dtJYKNjdoQG10FdAH150/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJzffk/btrsNwQR402/t5dtJYKNjdoQG10FdAH150/img.gif&quot; data-alt=&quot;출처 : https://velog.io/@yaincoding/퀵-정렬Quick-Sort&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJzffk/btrsNwQR402/t5dtJYKNjdoQG10FdAH150/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bJzffk/btrsNwQR402/t5dtJYKNjdoQG10FdAH150/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;514&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://velog.io/@yaincoding/퀵-정렬Quick-Sort&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;퀵정렬을 JS 코드로 구현해보았다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;vbscript&quot;&gt;&lt;code&gt;const arr = [5, 7, 9, 0, 3, 1, 6, 2, 4, 8];

function quickSort(arr, start, end){
    if( start &amp;gt;= end) return;
    const pivot = start;
    let left = start + 1;
    let right = end;

    while(left &amp;lt;= right){
        while(left &amp;lt;= end &amp;amp;&amp;amp; arr[left] &amp;lt;= arr[pivot]){
            left +=1;
        }
        while(right &amp;gt; start &amp;amp;&amp;amp; arr[right] &amp;gt;= arr[pivot]){
            right -= 1;
        }
        if(left &amp;gt; right){
            [arr[right], arr[pivot]] = [arr[pivot], arr[right]];
        } else {
            [arr[left], arr[right]] = [arr[right], arr[left]];
        }
    }
    quickSort(arr, start, right-1);
    quickSort(arr, right+1, end);
}

quickSort(arr, 0, arr.length - 1);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드에서는 피봇을 첫번째 인덱스 원소로 설정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;left 변수에는 피봇보다 큰 인덱스, right 변수에는 피봇보다 작은 인덱스의 위치를 기억한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;left &amp;gt; right 이면(left와 right 인덱스 변수를 스왑했을 때 오름차순이 아니면), 피봇과 right 인덱스(작은 데이터)를 교환한다.&lt;br /&gt;left &amp;lt;= right 이면 스왑 시 오름차순으로 정렬되므로, 두 데이터를 스왑한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바깥 while문이 종료되면 피봇을 기준으로 왼쪽에는 작은 데이터들, 오른쪽에는 큰 데이터들이 위치하므로 각각 퀵정렬을 수행한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;퀵 정렬의 시간복잡도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상적으로는 O(NlogN), 최악의 경우에는 O(N^2)의 시간복잡도를 기대할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상적인 경우에서는, 모든 분할이 절반 씩 일어난다는 가정을 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분할시마다 데이터가 절반씩 줄어드므로, 순환 호출의 깊이가 log2 N을 보인다.&lt;br /&gt;각 분할시 수행하는 비교 연산은 동일하게 N번이 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 log2 N * N = Nlog2 N 의 시간복잡도를 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최악의 경우에서는, 이미 정렬된 배열에 대해 퀵정렬을 수행하는 경우이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퀵정렬의 수행 결과로는 항상 피봇 오른쪽에만 배열이 존재하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 N회의 분할(순환 호출)이 일어나게 되고, 비교 연산 또한 평균 N번을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 N * N = N^2의 시간복잡도를 보인다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균적으로 매우 빠른 수행속도를 자랑한다. o(NlogN)&lt;br /&gt;주어진 배열 안에서 인덱스끼리 Swap을 통해 정렬이 수행되므로 공간 효율적이다. (= 제자리 정렬)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불안정 정렬이다.(=중복 값이 입력순서와 동일하게 정렬됨을 보장하지 않는다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬된 리스트에 대해서는 O(N^2)의 수행시간을 요구한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;퀵 정렬의 O(N^2) 시간복잡도 문제를 해결하기 위해서는..?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 알고리즘에서는 첫번째 원소를 피봇으로 선택하고 있기 때문에, 이미 정렬된 배열에서 O(N^2) 문제를 보인다.&lt;/li&gt;
&lt;li&gt;그러므로 첫번째 원소가 아닌 다른 원소를 선택하도록 알고리즘을 설계하면 되겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DS &amp;amp; Algorithm/theory</category>
      <category>퀵정렬 자바스크립트</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/127</guid>
      <comments>https://gobae.tistory.com/127#entry127comment</comments>
      <pubDate>Mon, 7 Feb 2022 18:42:54 +0900</pubDate>
    </item>
    <item>
      <title>삽입 정렬(Insertion Sort) 이란</title>
      <link>https://gobae.tistory.com/126</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4kHfL/btrsnJydn7j/F2EwzI6EC0PbHkc4oXJBCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4kHfL/btrsnJydn7j/F2EwzI6EC0PbHkc4oXJBCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4kHfL/btrsnJydn7j/F2EwzI6EC0PbHkc4oXJBCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4kHfL%2FbtrsnJydn7j%2FF2EwzI6EC0PbHkc4oXJBCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1496&quot; height=&quot;396&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;삽입 정렬&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리되지 않은 &lt;b&gt;데이터를 적절한 위치에 삽입&lt;/b&gt;하는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삽입 정렬에서는 배열 첫번째 원소는 정렬되었다고 판단하며, 두번째 원소부터 어떤 위치로 들어가야 할 지 판단한다.&lt;br /&gt;즉, 두번째 원소부터 시작해서 앞에 있는 원소들과 비교하며 삽입할 위치에 데이터를 삽입하며 정렬하는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삽입 정렬의 과정은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. N번째 인덱스의 값을 저장한다.&lt;br /&gt;2. 1 ~ N-1번째 인덱스에 있는 원소들과 비교하며 적절한 위치를 찾아 삽입한다.&lt;br /&gt;3. 1번 과정과 2번 과정을 반복한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gif를 통해 이해해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bURT0i/btrsyueLFRM/PyF6uDGeVwo93E9v0SoWAk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bURT0i/btrsyueLFRM/PyF6uDGeVwo93E9v0SoWAk/img.gif&quot; data-alt=&quot;출처 : https://commons.wikimedia.org/wiki/File:Insertion-sort-example.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bURT0i/btrsyueLFRM/PyF6uDGeVwo93E9v0SoWAk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bURT0i/btrsyueLFRM/PyF6uDGeVwo93E9v0SoWAk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;300&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://commons.wikimedia.org/wiki/File:Insertion-sort-example.gif&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삽입 정렬을 JS 코드를 통해 구현하였다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;  const arr = [5, 6, 3, 1, 8, 7, 2, 4];

  function insertionSort(arr){
      const len = arr.length;

      for(let i=1; i&amp;lt;len; i++){
          let idx = i-1;
          const tmp = arr[i];
          while(tmp &amp;lt; arr[idx] &amp;amp;&amp;amp; idx &amp;gt;= 0){
              arr[idx + 1] = arr[idx];
              idx--;
          }
          arr[idx + 1] = tmp;
      };

      return arr;
  }

  console.log(insertionSort(arr)); // [1, 2, 3, 4, 5, 6, 7, 8]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 인덱스(i = 1)부터 탐색을 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tmp 변수에 삽입할 인덱스의 값을 저장하고, idx 변수를 이용해 i의 바로 왼쪽부터 tmp 변수가 들어갈 위치를 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;while문을 통해 tmp 변수보다 큰 값을 모두 우측으로 한칸씩 옮긴다.(= 인덱스를 1씩 증가시킨 위치에 값을 이동시킨다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tmp &amp;lt; arr[idx] &amp;amp;&amp;amp; arr[idx-1] &amp;gt;= tmp 인 위치의 idx를 구한다.&lt;br /&gt;(만약 tmp 변수가 가장 작은 변수라면 idx = 0에 있는 값을 idx = 1로 옮겨준다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 idx + 1 위치에 tmp 변수를 삽입한다.&lt;br /&gt;(현재 idx 위치는 tmp보다 작은 값이 있거나, 배열의 맨 앞의 앞인 -1을 의미한다.)&lt;br /&gt;(while문에 의해 idx + 1의 위치에 있는 값과 idx + 2에 있는 값이 같으므로, idx + 1에 tmp 변수를 넣어주면 된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 선택한 인덱스로부터 왼쪽에 있는 원소들과 대소를 비교하는데, 자신보다 작은 값을 가진 인덱스를 찾는다고 생각하면 되겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;삽입 정렬의 시간복잡도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균 시간복잡도는 비교, 교환을 통해 O(N^2)를 가진다.&lt;br /&gt;선택 정렬같이 이중 반복문을 수행하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최악의 경우는 역으로 정렬된 경우이며 선택 정렬과 같이 n(n-1)/2 =&amp;gt; O(n^2)를 보인다.&lt;br /&gt;최선의 경우는 모두 정렬된 경우이며, 비교 O(N), 교환 O(1)로 O(N)의 복잡도를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 정렬된 상태에서 삽입 정렬을 수행하면..?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기준에서 왼쪽 원소들과 대소를 비교하므로, 모든 원소들은 1번의 비교만으로 정렬이 수행된다.&lt;/li&gt;
&lt;li&gt;그러므로 &lt;b&gt;어느 위치에 삽입할지 검색&lt;/b&gt;하는 시간이 O(상수)시간으로 대체된다.&lt;/li&gt;
&lt;li&gt;정렬을 위해 각 원소를 순회해야 하므로, O(N)의 복잡도를 가진다. 즉, O(N*k) = O(N)의 복잡도를 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;삽입 정렬의 공간복잡도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택 정렬과 같이 배열 내에서의 swap을 통해 구현하므로, O(N)의 복잡도이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택 정렬에 비해 구현 난이도는 높으나 더 효율적이다.&lt;br /&gt;대부분이 정렬된 배열에서 특히 좋다.&lt;br /&gt;주어진 배열 안에서 인덱스끼리 Swap을 통해 정렬이 수행되므로 공간 효율적이다. (= 제자리 정렬)&lt;br /&gt;안정 정렬이다(=중복 값이 입력 순서와 동일하게 정렬된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안정 정렬을 간단한 예시를 통해 이해해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2&amp;nbsp;3 1 &lt;u&gt;5&lt;/u&gt;&amp;nbsp;4&amp;nbsp;5 를 삽입 정렬을 통해 정렬하면, 1 2 3 4 &lt;u&gt;5&lt;/u&gt; 5 순서로 중복 값 순서를 유지한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평균, 최악 시간복잡도가 O(N^2)이다.&lt;br /&gt;배열 길이가 길어질수록 효율이 떨어진다.&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/theory</category>
      <category>삽입정렬 자바스크립트</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/126</guid>
      <comments>https://gobae.tistory.com/126#entry126comment</comments>
      <pubDate>Fri, 4 Feb 2022 18:40:36 +0900</pubDate>
    </item>
    <item>
      <title>선택정렬(Selection Sort) 이란</title>
      <link>https://gobae.tistory.com/125</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM0K82/btrstJKNbHa/sbzGbX0UL2OcZrTNMFfSs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM0K82/btrstJKNbHa/sbzGbX0UL2OcZrTNMFfSs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM0K82/btrstJKNbHa/sbzGbX0UL2OcZrTNMFfSs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM0K82%2FbtrstJKNbHa%2FsbzGbX0UL2OcZrTNMFfSs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1472&quot; height=&quot;380&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정렬?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬은 데이터를 특정 기준에 따라 순서대로 나열하는 방법이다.&lt;br /&gt;문제 상황에 따라 적절한 정렬 알고리즘을 선정하여 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 정렬 중 알고리즘이 단순한 선택 정렬에 대해서 알아본다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;선택 정렬(Selection Sort)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정해진 위치에 어떤 원소를 넣을지 선택&lt;/b&gt;하는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리되지 않은 데이터 중, 가장 작은 데이터를 선택해 앞의 데이터와 순서를 바꾼다고 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택 정렬의 과정은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 처리되지 않은 데이터 중 최소값을 찾는다.&lt;br /&gt;2. 그 값을 맨 앞에 위치한 값과 교체한다.&lt;br /&gt;3. 처음 위치를 뺀 나머지 배열을 같은 방식으로 교체한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gif를 통해 바로 이해해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CrPrb/btrstKJG9Md/EjzqLUA9LK8a9k14dG7XiK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CrPrb/btrstKJG9Md/EjzqLUA9LK8a9k14dG7XiK/img.gif&quot; data-alt=&quot;출처 :&amp;amp;amp;amp;amp;amp;nbsp;https://dev-lagom.tistory.com/37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CrPrb/btrstKJG9Md/EjzqLUA9LK8a9k14dG7XiK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/CrPrb/btrstKJG9Md/EjzqLUA9LK8a9k14dG7XiK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;330&quot; height=&quot;236&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;amp;amp;amp;amp;nbsp;https://dev-lagom.tistory.com/37&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘을 JS코드로 구현해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;  const arr = [4, 6, 3, 2, 0, 1, 5];

  function selectionSort(arr){
      const len = arr.length;

      for (let cur = 0; cur &amp;lt; len; cur++){
          let idx = cur;
          for (let j = cur + 1; j &amp;lt; len; j++){
              if(arr[idx] &amp;gt; arr[j]){
                  idx = j;
              }
          }
          [arr[cur], arr[idx]] = [arr[idx], arr[cur]];
      }
      return arr;
  }

  console.log(selectionSort(arr)); // [0, 1, 2, 3, 4, 5, 6]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for문의 cur 변수는 '정해진 위치'를 의미하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;idx 변수는 '가장 작은 값의 위치'를 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&lt;b&gt; '정해진 위치'에 있는 값&lt;/b&gt;과 &lt;b&gt;'가장 작은 값의 위치'에 있는 '가장 작은 값'&lt;/b&gt;을 스왑해줌으로서, &amp;nbsp;오름차순 정렬을 수행한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;선택 정렬의 시간복잡도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택 정렬은 매번 이중 반복문을 거치며 선형탐색을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원소의 개수인 N회 만큼 가장 작은 수를 찾아 맨 앞으로 보내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 비교(=첫 회전)에서의 비교 횟수는 인덱스 1부터 N-1까지 총 N-1회이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 비교는 인덱스 2부터 N-1까지 총 N-2회이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N-1번째 비교는 N-2 인덱스와 N-1 인덱스와 비교하면 되고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 비교는 마지막 인덱스에 위치한 값은 최댓값이므로 수행할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로, 전체 연산 횟수는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(N-1) + (N-1) + (N-2) + ... + 2 + 1&lt;br /&gt;= (N * (N-1) / 2 ) = (N^2 - N) / 2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 O(N^2)의 시간 복잡도로 표현된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;선택 정렬의 공간복잡도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택 정렬은 동일한 배열 내에서 수행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 배열 안에서 인덱스끼리 Swap을 통해 정렬이 수행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 O(N) 만큼의 공간복잡도를 가진다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘이 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스마다 비교하는 횟수는 많지만, 교환 횟수는 1회밖에 되지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그러므로, 인접 인덱스간 많은 교환이 일어나는 거품 정렬(Bubble Sort)에 비해 효율적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 배열 안에서 인덱스끼리 Swap을 통해 정렬이 수행되므로 공간 효율적이다. (= 제자리 정렬)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간복잡도가 O(N^2)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불안정 정렬이다.(= 중복 값이 입력 순서와 동일하지 않게 정렬될 수 있는 알고리즘)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들면, `5` 7 5 3 2를 선택정렬로 정렬하면, 2 3 5 `5` 7 의 순서로 정렬된다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DS &amp;amp; Algorithm/theory</category>
      <category>선택정렬 자바스크립트</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/125</guid>
      <comments>https://gobae.tistory.com/125#entry125comment</comments>
      <pubDate>Fri, 4 Feb 2022 16:44:44 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트의 this는 어떻게 결정되는가?</title>
      <link>https://gobae.tistory.com/124</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqLJdN/btrrF6mxPHt/CFvI5XZ3kYfNNk4uybeho1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqLJdN/btrrF6mxPHt/CFvI5XZ3kYfNNk4uybeho1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqLJdN/btrrF6mxPHt/CFvI5XZ3kYfNNk4uybeho1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqLJdN%2FbtrrF6mxPHt%2FCFvI5XZ3kYfNNk4uybeho1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;198&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;this&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅에서는 this 키워드에 대해 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this는 자기참조변수로, 자신이 속한 객체나 자신이 생성할 인스턴스를 가리킨다.&lt;br /&gt;자바스크립트 엔진에 의해 암묵적으로 생성되며, 어디서든 참조할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;※ this의 바인딩은 함수호출방식에 따라 동적으로 결정된다. ★x100개다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 바인딩은 식별자와 값을 연결하는 과정을 의미하는데,&lt;br /&gt;this 바인딩은 식별자이자 키워드인 this에 this가 가리킬 객체를 연결한다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this가 함수 호출 시점에 결정되므로, 개발자가 작성한 코드와 다르게 동작할 우려가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 &lt;u&gt;this는 &lt;b&gt;동적으로 결정된다.&lt;/b&gt;&lt;/u&gt; 는 사실을 꼭 기억하고 this가 필요한 시점에 동작을 예상하며 코드를 작성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this는 기본적으로 전역 객체(브라우저의 경우 window, 노드의 경우 global)가 바인딩된다.&lt;br /&gt;메서드에서 this는 메서드를 호출한 객체를 가리키며, 생성자 함수에서는 생성자 함수를 통해 생성한 인스턴스를 가리킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이정도 지식을 우선 알아두고 시작해보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;this는 함수의 호출 방식에 따라 동적으로 결정된다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 호출 방식은 다양하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.일반 함수로 정의하여 호출한다.&lt;br /&gt;2.메서드로 정의하여 호출한다.&lt;br /&gt;3.생성자 함수로서 호출한다.&lt;br /&gt;4.함수를 호출해주는 빌트인 함수의 프로토타입 메서드를 이용해 간접적으로 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번부터 4번까지 살펴보겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.일반 함수로 정의하여 호출한다.&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    function func(){
      console.log(this);
      function func2(){
        console.log(this);
      }
      func2(); // window
    }
    func(); // window&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반함수로 호출하면, this에는 전역 객체가 바인딩된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당신이 어디서 내부함수로서의 함수를 선언했던간에, 항상 this는 전역객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 예시로 전역함수, 메서드 내부 함수, 콜백함수 등의 사용처가 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그렇다면 객체의 메서드와 메서드 내부 함수의 this는 어떻게 다를까?&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;    var name = 'global var';
    const obj = {
      name:'Tom',
      say(){
        console.log('my name is ', this.name);

        function say2(){
          console.log('my name is ', this.name);
        }
        say2();
      }
    };

    obj.say();
    // my name is Tom
    // my name is global var&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, var 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 된다.&lt;br /&gt;그러므로 window.name 으로 전역 객체의 프로퍼티인 var를 호출할 수 있다.&lt;br /&gt;그래서 obj.say 메서드의 2개 this 중에 하나라도 전역객체를 가리킨다면, var가 출력 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj 객체에는 메서드로 say가 있고, say는 this.name을 출력하고 내부에 중첩함수 say2를 가지고 있으며 say2에서도 this.name을 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 결과로는 첫번째 this.name은 'Tom'을 출력하고 두번째 this.name은 'global var'를 출력한다.&lt;br /&gt;say는 객체의 메서드로 자신이 속한 객체 obj를 참조하여 'Tom'을 출력하게 되고, say2 함수는 일반함수로 this로 전역객체를 참조하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 메서드 say의 this는 obj를 가리키지만, 메서드 내에 중첩함수인 say2의 this가 obj를 가리키지 않는다면 문제가 발생할 수 있다. 보통&amp;nbsp;메서드 내에 정의한 함수들은, 헬퍼함수로서 역할을 하기 위해 정의하기 때문이다. 그러므로 say 메서드가 해야하는 일을 나눠 가져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 메서드에 존재하는 일반함수들의 this를 특정해줘야 한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    var name = 'global var';
    const obj = {
      name:'Tom',
      say(){
        console.log('my name is ', this.name);
        const that = this;

        function say2(){
          console.log('my name is ', that.name);
        }
        say2();
      }
    };

    obj.say();
    // my name is Tom
    // my name is Tom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 메서드의 this를 &lt;b&gt;that 키워드&lt;/b&gt;에 할당하여 내부함수에서도 메서드의 this를 가리키도록 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 방법으로는, &lt;b&gt;화살표 함수&lt;/b&gt;를 이용해도 된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    var name = 'global var';
    const obj = {
      name:'Tom',
      say(){
        console.log('my name is ', this.name);

        const say2 = ()=&amp;gt; {
          console.log('my name is ', this.name);
        }
        say2();
      }
    };

    obj.say();
    // my name is Tom
    // my name is Tom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;화살표 함수의 this는 상위 스코프의 this를 가리킨다.&lt;/b&gt; 그러므로 상위 스코프 say 함수의 this인 obj를 가리킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 방법으로는, 빌트인 객체 프로토타입의 메서드들을 이용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드에는 apply, call, bind가 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    var name = 'global var';
    const obj = {
      name:'Tom',
      say(){
        console.log('my name is ', this.name);

        function say2(){
          console.log('my name is ', this.name);
        }
        say2.apply(this);
        say2.call(this);
      }
    };

    obj.say();
    // my name is Tom
    // my name is Tom
    // my name is Tom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 빌트인 객체 Function의 프로토타입 메서드 apply, call, bind로 this를 특정시켜줄 수 있다.&lt;br /&gt;이 메서드들은 뒤에서 자세히 다룰 예정이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 메서드로 정의하여 호출한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 객체 프로퍼티의 값일수도 있다. 이 때는 메서드로서 호출된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드의 this에는 메서드를 호출한 객체가 바인딩된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;즉, obj.method 라면, 메서드를 호출한 객체인 obj가 method 내부의 this 바인딩 대상객체가 된다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;    const obj = {
      name:'Tom',
      say(){
        console.log('my name is ', this.name);
      }
   };
    obj.say(); // my name is Tom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 say 메서드를 호출한 obj가 this가 되어 this.name =&amp;gt; Tom을 출력하는 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 여기서 유의할 점이 있는데,&amp;nbsp;&lt;u&gt;this는 동적으로 결정된다는 점&lt;/u&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 코드에서는 마치 say 메서드에서의 this가 이미 obj로 정해져 있는 것 처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 this는 메서드가 호출될 때 정해지며, obj 객체의 메서드로 호출되어 obj 객체를 가리키고 있었을 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;    const obj = {
      name:'Tom',
      say(){
        console.log('my name is ', this.name);
      }
   };
    obj.say(); // my name is Tom

    const obj2 = {
      name:'James',
    };

    obj2.__proto__.say = obj.say;
    obj2.say(); // my name is James&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj1이 가진 say 메서드를 obj2의 프로토타입 메서드에 추가했다.&lt;br /&gt;obj2.say()를 실행하니 this가 obj2로 지정되어 James가 출력된 모습을 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 다음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;say 메서드는 obj 객체에 종속된 함수가 아니다.&lt;br /&gt;obj 객체라는 공간이 있다면, say 프로퍼티가 있고, 그 프로퍼티가 어떤 공간을 가리킨다.&lt;br /&gt;say 프로퍼티가 가리키는 공간에 함수가 정의되어 있을 것이며, say 함수의 구현을 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 obj2.__&lt;b&gt;proto__&lt;/b&gt;.say = obj.say 를 통해서, &lt;span&gt;obj2의 프로토타입에 say 변수를 선언하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;obj 객체의 say 프로퍼티가 가리키는 공간에 대한 참조를 동일하게 가리키도록 한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토타입에 정의된 say 메서드는 obj2.say()로 호출 될 것이고, 호출시 동적으로 this가 바인딩되기 때문에 obj2 객체가 바인딩된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 메서드가 객체 내부에 종속된 것이 아니므로, 다음과 같은 동작도 가능하다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;    var name = 'hello world';

    const obj = {
      name:'Tom',
      say(){
        console.log('my name is ', this.name);
      }
    };
    const say = obj.say;
    say(); // my name is hello world&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 say는 this에 window가 바인딩되어 hello world를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;다양한 예시를 통해 this가 바인딩 될 대상이 호출 시점에 결정된다는 사실이 얼마나 중요한지를 알게 되었다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 생성자 함수로서 호출한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 함수에서 선언된 this에는 생성자 함수를 통해 생성된 인스턴스가 바인딩된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 생성자 함수는 함수를 new 키워드로 호출하면 된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    function Person(name){
      this.name = name;
      this.say = function(){
        console.log('my name is ', this.name);
      };
    }

    const tom = new Person('Tom');
    tom.say(); // my name is Tom

    const noTom = Person('Tom');
    console.log(noTom); // undefined (리턴이 없어 undefined가 반환되었다.)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 new 키워드와 함께 생성자 함수로서 사용하면, this가 생성한 인스턴스에 바인딩된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 키워드의 결과로서 암묵적인 return this;를 실행하고 tom에는 this 객체(인스턴스)가 담겨있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 인스턴스는, 생성자 함수가 빈 객체를 생성하고 this에 바인딩하여 name, say 프로퍼티들을 담아 반환한 객체다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 키워드가 없는 단순 함수 호출에서는 함수의 return이 존재하지 않아서 return undefined;로 해석이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 noTom에는 undefined 값이 담기게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 단순호출의 경우에 undefined만 반환되고 아무런 부수효과는 없는걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person 함수 내의 로직은 this에 name, say 프로퍼티를 생성하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 키워드 없이 Person 함수를 실행하면 this가 전역 객체를 가리킬 것인데, 결국 전역 객체에 name, say 프로퍼티를 생성한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 console.log(name); 을 찍어보면 window.name 을 참조하여 Tom을 반환할 것임을 예상할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.함수를 호출해주는 빌트인 함수의 프로토타입 메서드를 이용해 간접적으로 호출한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Function.prototype.apply, call, bind 메서드를 이용하면 this 바인딩을 실현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apply, call 메서드는 동작이 거의 비슷하나 매개변수를 받는 방식이 조금 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mdn에서 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/apply&quot;&gt;apply&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/call&quot;&gt;call&lt;/a&gt;을 통해 학습해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 함수는 모두 '함수 호출'을 위한 메서드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 호출하면서, this에 바인딩 될 객체를 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 인수로는 this로 지정할 객체를 넣어주고, 함수를 실행할 때 필요한 인수들을 두번째부터 넣어주면 된다.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;    var fire = 'fail..';
    const thisObj = { fire : 'fire!' };
    function func(a,b,c){
        console.log(a,b,c, this.fire);
    }

    console.log(func(3,2,1)); // 3 2 1 fail..
    console.log(func.apply(thisObj, [3,2,1])); // 3 2 1 fire!
    console.log(func.call(thisObj, 3, 2, 1)); // 3 2 1 fire!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;func(3,2,1)로 호출하면 this가 window를 가리키므로 3 2 1 fail.. 이 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;func.apply와 call로 this에 thisObj 객체를 바인딩한다. 그리고 두번째 인자부터 필요한 인수를 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apply 메서드에는 인수를 배열 []로 묶어서 전달하면 되고,&lt;br /&gt;call 메서드에는 인수를 쉼표로 구분해서 전달하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/bind&quot;&gt;bind 메서드&lt;/a&gt;는 조금 다르게 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bind 메서드는 this를 바인딩해주는 기능은 동일하나, this가 바인딩 된 함수 자체를 리턴한다.&lt;br /&gt;그러므로 리턴된 함수를 다른 변수에 할당해서 사용하거나, 콜백으로서 전달할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;go&quot;&gt;&lt;code&gt;    var fire = 'fail..';
    const thisObj = { fire : 'fire!' };
    function func(a,b,c){
        console.log(a,b,c, this.fire);
    }

    const bindFunc = func.bind(thisObj);
    func(3, 2, 1); // 3 2 1 fail..
    bindFunc(3, 2, 1); // 3 2 1 fire!

    const bindFunc2 = func.bind(thisObj, 100);
    bindFunc2(2, 1); // 100 2 1 fire!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apply, call 함수와 동일하게 첫번째 인자로 this에 바인딩할 객체를 받으며, 두번째 인자부터는 함수 자체에서 받는 인자의 값을 미리 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 func.bind의 결과를 bindFunc에 할당해서 사용할 수 있으며 a 인자 값을 100으로 설정하여 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 코딩하면서 코드가 의도대로 동작하지 않는 경우에 이 this가 원인인 경우가 종종 있었다.&amp;nbsp;특히나 이벤트핸들러에 넣어주는 콜백과 setTimeout등의 타이머함수에서 사용하는 콜백 등에서 this가 원하는 대로 동작하지 않았던 기억이 난다. 이벤트, 타이머등은 비동기로 동작하기 때문에, 개발자 입장에서 더더욱 원인과 문제를 파악하기 어렵기도 하다. 그렇기 때문에 우리는 this가 결정되는 방식을 이해하고 화살표 함수, apply, call, bind 등의 여러 방법들을 이용해서 우리의 코드가 예상대로 동작하도록 만들어야 한다.&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 this 바인딩</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/124</guid>
      <comments>https://gobae.tistory.com/124#entry124comment</comments>
      <pubDate>Tue, 25 Jan 2022 19:59:16 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트의 undefined, null에 대해 알아보자.</title>
      <link>https://gobae.tistory.com/123</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB6HIX/btrrcOeGstF/hZ3akPr0yRj1O2NmkEd4ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB6HIX/btrrcOeGstF/hZ3akPr0yRj1O2NmkEd4ZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB6HIX/btrrcOeGstF/hZ3akPr0yRj1O2NmkEd4ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB6HIX%2FbtrrcOeGstF%2FhZ3akPr0yRj1O2NmkEd4ZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;538&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 데이터의 원시값에는 7가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String, Number, Boolean, undefined, null, Symbol, BigInt가 그러하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;undefined, null이 각각 원시 자료형임을 이해하는 것이 우선이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅에서는 undefined, null은 무엇이며 언제 undefined와 null을 볼 수 있는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 '개발자가 변수에 직접 이 값을 할당해도 되는지?'에 대한 내용에 대해 다뤄볼 예정이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;undefined&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;undefined가 무엇이고, undefined가 어떤 상황에서 이용되는지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;undefined는 '정의되지 않은' 이라는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 정의란, 변수에 값을 할당해서 변수를 명확히 하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 값을 할당하기 위해선 먼저 변수가 선언되어 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 선언이란, 변수를 생성하는 것을 말한다.&lt;br /&gt;변수를 선언하면 값을 저장하기 위해 메모리 공간을 확보하고 개발자가 지정한 변수 이름과 확보된 메모리 주소를 연결해서 값을 저장할 수&amp;nbsp;있는 상태가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 변수 선언을 위한 키워드는 var, let, const이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    let hello;
    console.log(hello); // undefined&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진에서는 변수 선언을 위해 2단계 과정을 거친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언 단계 : 변수 이름을 등록하고, 자바스크립트 엔진에 변수 존재를 알리는 단계이다.&lt;br /&gt;초기화 단계 : 메모리 공간을 확보하고, 암묵적으로 공간에 undefined을 할당하여 초기화하는 단계이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화는 변수 선언 이후 '최초로' 값을 할당하는 것을 의미하며, 자바스크립트 엔진이 할당할 값이 없을 때에도 암묵적으로 undefined를 할당하기 때문에&amp;nbsp;위의 코드 결과에서 hello 변수가 undefined 값을 담고있다는 결과를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 공간은 재사용되기 때문에 초기 할당을 하지 않으면 이전의 값이 남아 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 쓰레기값 이라고 하는데, 자바스크립트 엔진이 변수 선언시 기본적으로 undefined를 할당해주므로&lt;br /&gt;값을 할당하지 않은 변수를 참조했을 때 쓰레기값이 나오는 위험을 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 선언한 변수에 값을 할당해볼 차례다.&lt;/p&gt;
&lt;pre class=&quot;openscad&quot;&gt;&lt;code&gt;    // case 1.
    let hello;
    hello = 'hello world;
    console.log(hello); // hello world

    // case 2.
    let hello2 = 'hello world2';
    console.log(hello2); // hello world2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;case 1, 2는 동일하게 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;case2에서는 case1을 단축하여 표현해서 선언이 되면서 자동으로 할당되는 것 처럼 보이지만,&lt;br /&gt;자바스크립트 엔진에서는 선언과 할당을 2개의 문으로 나누어 실행하기 때문에&lt;br /&gt;두 케이스 모두 먼저 변수를 선언하고, 그리고 할당을 진행하는 과정을 거치게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 선언의 과정(변수 이름을 등록, 메모리 공간을 확보한 후 undefined를 할당하여 초기화하는 것)을 거치고 난 후에&lt;br /&gt;할당의 과정(새로운 메모리 공간을 확보하여 할당값을 저장하고, 메모리 공간과 변수를 연결하는 과정)이 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돌아돌아 왔지만, 결국 undefined는 자바스크립트 엔진이 초기화를 위해 사용하는 값이라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 개발자가 변수를 참조했을 때, undefined를 반환한다면 해당 변수는 초기화되지 않은(=엄밀히 말하면 아직 값이 할당되지 않은) 변수라고 이해할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;null&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 null이 무엇이고, null은 어떤 상황에서 이용되는지를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null은 '값이 없는'의 의미이다.&lt;br /&gt;undefined와 동일하게 null 타입의 값은 null이 유일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의미에서 알 수 있듯 null은 '값이 없음'을 의도적으로 명시할 때 사용하는 변수이다.&lt;br /&gt;그리고 변수에 null을 할당하면, 이전에 참조하던 값을 더이상 참조하지 않겠다는 의미가 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 코딩을 하다가 null을 마주친다면 이것은 자바스크립트 엔진(컴퓨터)가 의도적으로 설정한 값이 아니라,&lt;br /&gt;'개발자(사람)가 의도적으로 값이 없다고 설정했구나!' 하고 생각하면 되겠다.&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;    let name = 'Tom';
    ... //여러 복잡한 로직들

    name = null;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 코드에서 얻을 수 있는 장점을 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null은 '이전의 값을 더이상 참조하지 않겠다'는 의미로 사용한다고 했는데,&lt;br /&gt;이전의 값 'Tom'은 더이상 사용되지 않을 예정이므로, 개발자가 name 변수에 null을 할당하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'Tom'을 참조하는 변수가 name이 유일하다면, 이제 'Tom'이 할당된 메모리 공간에 대한 참조가 없으므로 가비지컬렉터의 대상이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 null 값을 잘 이용하면 메모리를 효율적으로 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null 타입을 다룰 때는 타입 체크에 유의해야 한다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;    let value = null;
    console.log(typeof null); // object
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typeof를 이용해 null이 담긴 변수의 타입을 보면 object가 반환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(자바스크립트에서 인정한 공식적인 버그이며, 하위 호환성을 위해 수정될 수 없는 버그이므로 유의해야 한다.)&lt;br /&gt;(typeof를 구현할 때, null 타입 체크를 구현하지 않아서 그렇다. = null 타입 체크가 누락되었다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 다음과 같이 검증해야 한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;    let value = null;
    console.log(value === null); // true
    
    
    let value2;
    console.log(value == value2) // true
    console.log(value === value2) // false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일치 연산자(===)는 값과 타입이 모두 동일할 때 true를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;value변수의 값 null과 null의 값 null이 같고&lt;br /&gt;value변수의 타입인 null 타입과 null의 타입 null 타입이 같아서 true가 반환되는 원리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 우리는 value2를 선언만 하면, undefined 값이 할당됨을 예상할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 값을 비교해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동등 연산자(==)를 사용하면, undefined와 null이 같다는 결과를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일치 연산자(===)를 사용하면, undfeined와 null은 다르다고 알려준다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그래서 언제 undefined를 쓰고 언제 null을 쓰면 되는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 다루다보면, 변수를 할당하거나 재사용하는 등의 상황이 가끔씩 있는데&lt;br /&gt;이 때 할당해야 할 값이 undefined인지 null인지 대해 몰랐던 경험이 있었고, 기회를 맞아 정리하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 다룬 undefined, null에 대한 내용을 이해했다면 정답은 이미 나와있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;undefined는 자바스크립트 엔진이 사용하는 자료형이다.&lt;br /&gt;그렇기 때문에, '언제 undefined를 써야 하나요?' 라는 질문에는 '쓰지 마세요.'가 적절할 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지만 변수에 값이 없다는 것을 명시하고 싶다면, null을 쓰는 것이 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null은 개발자가 '값이 없음'을 명시하기 위해 사용하는 자료형이기 때문이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고한 출처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&amp;amp;ejkGb=KOR&amp;amp;barcode=9791158392239&quot;&gt;모던자바스크립트 딥다이브 도서&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://medium.com/crocusenergy/js-undefined-null-%EC%96%B4%EB%96%A8-%EB%95%8C-%EC%93%B8%EA%B9%8C-8782dc3c35b6&quot;&gt;https://medium.com/crocusenergy/js-undefined-null-어떨-때-쓸까-8782dc3c35b6&lt;/a&gt;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>변수에 undefined null 할당하기</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/123</guid>
      <comments>https://gobae.tistory.com/123#entry123comment</comments>
      <pubDate>Wed, 19 Jan 2022 18:15:10 +0900</pubDate>
    </item>
    <item>
      <title>발행-구독 패턴(Publisher-Subscriber Pattern)이란?</title>
      <link>https://gobae.tistory.com/122</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cd8HW1/btrq6u3uC9r/xCdHv6kqGJKODRH1Gua0xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cd8HW1/btrq6u3uC9r/xCdHv6kqGJKODRH1Gua0xK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cd8HW1/btrq6u3uC9r/xCdHv6kqGJKODRH1Gua0xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcd8HW1%2Fbtrq6u3uC9r%2FxCdHv6kqGJKODRH1Gua0xK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1896&quot; height=&quot;494&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이전 글&lt;/h3&gt;
&lt;figure id=&quot;og_1642571008679&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;옵저버 패턴(Observer Pattern)이란?&quot; data-og-description=&quot;옵저버 패턴 옵저버 패턴은, 옵저버들의 목록을 객체(관찰하려는 대상)에 등록하여 객체가 상태 변화가 있을 때 마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버들에게 통지하는 디자인 패&quot; data-og-host=&quot;gobae.tistory.com&quot; data-og-source-url=&quot;https://gobae.tistory.com/121&quot; data-og-url=&quot;https://gobae.tistory.com/121&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/btxFZq/hyM8aBhPsu/JTo8nMi0CeRsT4KJOHckH1/img.png?width=800&amp;amp;height=223&amp;amp;face=0_0_800_223,https://scrap.kakaocdn.net/dn/sLxjY/hyM8bmHSgL/a3FS4YiRNKhIyMPKQeCai0/img.png?width=800&amp;amp;height=223&amp;amp;face=0_0_800_223,https://scrap.kakaocdn.net/dn/0jr9t/hyM7391X3H/LzkgpaYGHtlSKe56yYrIMK/img.png?width=1394&amp;amp;height=1124&amp;amp;face=0_0_1394_1124&quot;&gt;&lt;a href=&quot;https://gobae.tistory.com/121&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gobae.tistory.com/121&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/btxFZq/hyM8aBhPsu/JTo8nMi0CeRsT4KJOHckH1/img.png?width=800&amp;amp;height=223&amp;amp;face=0_0_800_223,https://scrap.kakaocdn.net/dn/sLxjY/hyM8bmHSgL/a3FS4YiRNKhIyMPKQeCai0/img.png?width=800&amp;amp;height=223&amp;amp;face=0_0_800_223,https://scrap.kakaocdn.net/dn/0jr9t/hyM7391X3H/LzkgpaYGHtlSKe56yYrIMK/img.png?width=1394&amp;amp;height=1124&amp;amp;face=0_0_1394_1124');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;옵저버 패턴(Observer Pattern)이란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;옵저버 패턴 옵저버 패턴은, 옵저버들의 목록을 객체(관찰하려는 대상)에 등록하여 객체가 상태 변화가 있을 때 마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버들에게 통지하는 디자인 패&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gobae.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴에 대해서 정리한 포스팅.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 발행구독 패턴에 대해 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;발행구독 패턴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KtEtG/btrq9XQRCLS/zv7iRRcN6GV4MLBzTXYU4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KtEtG/btrq9XQRCLS/zv7iRRcN6GV4MLBzTXYU4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KtEtG/btrq9XQRCLS/zv7iRRcN6GV4MLBzTXYU4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKtEtG%2Fbtrq9XQRCLS%2Fzv7iRRcN6GV4MLBzTXYU4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;495&quot; height=&quot;654&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발행구독 패턴은, 비동기 메시징 패러다임이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발행자와 구독자가 있고, 그 사이에 브로커(=메시지 큐)가 존재하는 형태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것의 특징을 요약하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 발행자 메시지의 수신자가 정해져 있지 않다.&lt;br /&gt;2. 메시지는 정해진 범주에 따라서 구독을 신청한 수신자에게 전달이 된다.&lt;br /&gt;3. 수신자는 발행자에 대한 정보 없이, 원하는 메시지를 수신할 수 있다.&lt;br /&gt;4. 메시지 큐 패러다임과 마치 형제같은 관계로, 대형 메시지 지향 미들웨어 솔루션의 일부라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발행자와 구독자&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발행자(=pub)와 구독자(=sub)는 다음과 같은 특징을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. pub이 sub의 선언 위치나 존재를 알 필요가 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지 큐와 같은 브로커 역할을 하는 중간 지점에 메시지를 던져 두기만 하면, 브로커가 알아서 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. sub 역시 pub의 선언 위치나 존재를 알 필요가 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브로커에 할당된 작업만을 모니터링하고, 원하는 작업이 발생하면 할당받아 작업을 수행하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 pub, sub은 서로 알 필요가 없으며, 브로커와의 통신만을 수행할 수 있다면 역할을 잘 수행하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;브로커&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브로커는 발행자와 구독자 사이에 위치하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 메시지큐가 브로커로서의 역할을 수행하며, 두 객체 사이에서 구독과 발행 이후의 메시지를 처리해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 브로커가 모든 들어오는 메시지를 필터링하며, 타겟들(=구독자)에게 메시지를 배포하는 역할을 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메시지 큐&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대용량 데이터 처리를 위한 배치 작업이나 채팅 서비스, 비동기 데이터 처리등에 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 큐는, 프로세스나 프로그램 인스턴스가 데이터 상호 교환 시 사용하는 통신 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는, 메시지 지향 미들웨어(MOM = Message Oriented Middleware)를 구현한 시스템을 의미한다.&lt;br /&gt;&amp;nbsp; &amp;nbsp;여기서 MOM이란? 비동기 메시지를 사용하는 응용 프로그램 간 데이터 송수신을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공정 작업 연기가 가능한 유연성을 제공하며, SOA(Service Oriented Architecture) 개발에 도움을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 큐의 장점은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비동기(Asynchronous) : 큐에 넣어서 나중에 처리 가능&lt;/li&gt;
&lt;li&gt;비동조(Decoupling) : 앱과 분리 가능&lt;/li&gt;
&lt;li&gt;탄력성(Resilience) : 일부 실패가 전체에 영향x&lt;/li&gt;
&lt;li&gt;과잉(Redunadancy) : 실패할 경우 재실행 가능&lt;/li&gt;
&lt;li&gt;보증(Guarantees) : 작업 처리 확인 가능&lt;/li&gt;
&lt;li&gt;확장성(Scalable) : 다수 프로세스들이 큐에 메시지 보내기 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 큐의 단점은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'큐'이기 때문에 사용자가 많아지거나, 데이터가 많아지면 요청에 대한 응답이 늦어지게 된다.&lt;/li&gt;
&lt;li&gt;즉 과도한 트래픽이 몰리게 되면, 대기 시간 지연으로 인해 서비스가 망가질 위험이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 큐의 사용 예시는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 곳의 API로부터 데이터 송수신이 가능하다.&lt;/li&gt;
&lt;li&gt;다양한 앱과 비동기 통신이 가능하다.&lt;/li&gt;
&lt;li&gt;이메일 발송, 문서 업로드가 가능하다.&lt;/li&gt;
&lt;li&gt;많은 양의 프로세스를 처리 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;옵저버 패턴과 함께 발행구독 패턴 이해하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴과 발행구독 패턴은 매우 유사하지만, 분명한 차이점이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 큰 차이는 발행구독 패턴은 옵저버와 옵저버블 사이에 브로커(= 메시지 큐, 이벤트 버스)라고 불리는 중개자가 존재한다는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;942&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kOPnx/btrrcumMg0k/gOlZSIj6wY0XG7JCxMhRY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kOPnx/btrrcumMg0k/gOlZSIj6wY0XG7JCxMhRY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kOPnx/btrrcumMg0k/gOlZSIj6wY0XG7JCxMhRY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkOPnx%2FbtrrcumMg0k%2FgOlZSIj6wY0XG7JCxMhRY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;496&quot; height=&quot;942&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;942&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴과 발행구독 패턴의 차이를 정리해보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 옵저버 패턴은 옵저버와 옵저버블 간 서로를 알고 있으나, 발행구독 패턴에서는 서로를 몰라도 상관없다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴은 구독을 하고 notify하는 과정에서, 옵저버와 옵저버블의 직접적인 소통이 이루어져야 한다.&lt;br /&gt;(=옵저버 패턴 포스팅에서 살펴봤듯이, 옵저버블이 옵저버의 메서드를 호출하는 방식으로 동작한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 발행구독 패턴은 발행 객체와 구독 객체 사이에 직접적인 소통을 하지 않아도 된다.&lt;br /&gt;소통의 역할을 옵저버와 옵저버블 외에 브로커라고 하는 제 3의 구성요소가 수행하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브로커는 옵저버와 옵저버블 사이에서 메시지를 필터링하여 다시 배포하는 역할을 한다.&lt;br /&gt;그러므로 옵저버와 옵저버블은 서로를 알지 못해도 브로커에 의해 소통 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 발행구독 패턴이 옵저버 패턴보다 더 낮은 결합도를 가진다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번에서 다룬 내용과 같은 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴에서는 옵저버와 옵저버블이 서로를 모른 채 소통할 수 없다.&lt;br /&gt;그러나 발행구독 패턴에서는 pub(=observable) sub(=observer)가 서로를 몰라도 중간 메시지큐에 의해 소통할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 발행구독에서는 pub, sub이 서로 존재를 알 필요가 없으므로, 소스코드가 겹치거나 서로 의존할 일이 없겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 옵저버 패턴은 주로 동기적으로 동작하며, 발행구독 패턴은 비동기적으로 동작하게 된다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴 포스팅에서,&lt;br /&gt;옵저버 패턴은 대부분 옵저버블에서 옵저버 메서드를 호출하며 동기적으로 동작한다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발행구독 패턴에서는 pub, sub 사이에 브로커가 존재하여 각각 브로커에 메시지를 전달하며 비동기적으로 동작하게 된다.&lt;br /&gt;(= 브로커로 메시지큐가 자주 사용된다는 점으로 미루어 보아 비동기적으로 동작함을 짐작할 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 옵저버 패턴은 단일 도메인(=하나의 앱)에서 동작해야 하며 발행 구독은 크로스 도메인(=다른 앱) 상황에서도 동작이 가능하다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크로스 도메인이란, 네이버나 카카오 같은 서로 다른 도메인을 의미한다.&lt;br /&gt;또는 API 서버, 파일 서버, 소켓 서버 등 동일한 사이트에서 다른 목적으로 서버를 나눈 경우에도 크로스 도메인에 속한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발행구독 패턴에서 pub, sub은 서로를 몰라도 된다는 점을 이용하는 것이다.&lt;br /&gt;pub, sub이 각각 브로커와 소통할 수 있다면, 앱의 도메인이 다르더라도 처리가 가능하기 때문이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고한 출처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c&quot;&gt;https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/azure/architecture/patterns/publisher-subscriber&quot;&gt;https://docs.microsoft.com/ko-kr/azure/architecture/patterns/publisher-subscriber&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://zorba91.tistory.com/291&quot;&gt;https://zorba91.tistory.com/291&lt;/a&gt;&lt;/p&gt;</description>
      <category>Design Pattern</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/122</guid>
      <comments>https://gobae.tistory.com/122#entry122comment</comments>
      <pubDate>Wed, 19 Jan 2022 14:57:43 +0900</pubDate>
    </item>
    <item>
      <title>옵저버 패턴(Observer Pattern)이란?</title>
      <link>https://gobae.tistory.com/121</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/botz88/btrq5pmT1eh/pOy8NkKdUEltUGUJeYbyj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/botz88/btrq5pmT1eh/pOy8NkKdUEltUGUJeYbyj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/botz88/btrq5pmT1eh/pOy8NkKdUEltUGUJeYbyj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbotz88%2Fbtrq5pmT1eh%2FpOy8NkKdUEltUGUJeYbyj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1870&quot; height=&quot;522&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;옵저버 패턴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴은, 옵저버들의 목록을 객체(관찰하려는 대상)에 등록하여&lt;br /&gt;객체가 상태 변화가 있을 때 마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버들에게 통지하는 디자인 패턴이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Observer(관찰자)&lt;/b&gt;&lt;br /&gt;상태 변화를 감지하는 대상이다.&lt;br /&gt;옵저버에는 함수나 객체 모두 등록이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Obervable(객체)&lt;/b&gt;&lt;br /&gt;상태가 변경되는 대상이다.&lt;br /&gt;subscribe, unsubscribe, notify 등 행동을 처리하는 메서드를 보유하고 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴의 핵심은 의존성을 낮추는 것(결합도를 낮추는 것)이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;옵저버 패턴 구현을 위한 필수사항은 무엇인가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 구독방법을 포함해야 한다.&lt;br /&gt;2. 구독리스트를 담아야 한다.&lt;br /&gt;3. 이벤트 발행하는 방법을 포함해야 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그림을 통해 옵저버 패턴 알아보기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1394&quot; data-origin-height=&quot;1124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caeOJX/btrq4p8Nscq/mCWhHnJGFe5Vo5dbriEBe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caeOJX/btrq4p8Nscq/mCWhHnJGFe5Vo5dbriEBe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caeOJX/btrq4p8Nscq/mCWhHnJGFe5Vo5dbriEBe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaeOJX%2Fbtrq4p8Nscq%2FmCWhHnJGFe5Vo5dbriEBe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;1124&quot; data-origin-width=&quot;1394&quot; data-origin-height=&quot;1124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 그림을 보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선생님 : Tom&lt;br /&gt;학생 : Jane, Kane, Sane 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미리 말하자면, 현재 선생님은 Observable이며 학생이 Observer이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 학생은 선생님의 모든 변화를 알아야 한다.&lt;br /&gt;(= 선생님은 학생들에게 Notify 할 수 있어야 한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 학생은 자신의 상태를 업데이트 하는 기능을 가져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 선생님은 학생을 등록, 등록해제, 알림할 수 있어야 한다.&lt;br /&gt;그래서 선생님의 행동에 변화(객체에 변화)가 생길 때, notifyStudent 등과 같은 메서드를 통해 학생들에게 알려줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서 선생님은 학생의 명단을 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 선생님(객체)가 구독할 대상(학생)들을 등록할 수 있으며,&lt;br /&gt;선생님(객체)는 자신이 변화할 때 마다 구독한 대상(학생)들에게 알림을 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 학생은 선생님으로부터 받은 알림이 오면, 각자 정의된 행동을 수행하게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;옵저버 패턴에서의 호출 흐름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴에서는 이벤트 중심으로 호출관계 흐름이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;한 방향으로 진행&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상할 수 있듯이, Observable에서 Observer로의 방향이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버는 마치 구독 시스템의 push 알람처럼, 구독 대상의 변화에 의한 push 방식으로 데이터를 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버는 그저 구독 리스트에 등록만 되어 있다면, 옵저버블로부터 정보를 받아볼 수 있는 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;옵저버패턴의 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴에서는, 옵저버들이 구독하려는 대상(객체)를 알고 있으며, 객체들도 옵저버들의 목록을 알고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 객체 간 강한 결합(A의 상태 변화시 직접 B의 상태를 변경시키는 구체적인 코드)보다&lt;br /&gt;객체 간의 관계를 &lt;b&gt;느슨한 결합&lt;/b&gt;으로 만들 수 있으며&lt;br /&gt;객체의 상태 변화를 옵저버에서 자동으로 알 수 있고&lt;br /&gt;의존 관계가 1:N(객체 : 옵저버들) 이기 때문에 옵저버를 여러 개 만들 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴은 대부분 동기방식으로 동작하도록 설계한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기방식의 동작이란,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체에서 이벤트가 발생하면 객체가 옵저버들의 목록을 순회하면서 옵저버의 메서드들을 순차적으로 호출한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 옵저버 패턴을 이용하지 않고, 구독 시스템을 만들 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Youtube라는 객체와 Tom, Jane, James라는 옵저버 객체가 있다면&lt;/p&gt;
&lt;pre id=&quot;code_1642511273308&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Youtube{
	...
    
    notifySubscriber(data){
    	tom.update(data);
        jane.update(data);
        james.update(data);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 구조를 지닐 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 예상 가능한 문제점으로는,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Youtube 객체와 각각의 옵저버 객체들은 강한 결합을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Observer.update() 형태로 동일한 코드가 반복된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 새로운 옵저버 객체를 등록하기 위해서는 클래스를 직접 수정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 class 객체 내부에서 직접적으로 외부 객체를 사용하면서 발생하는 문제이기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 관계를 느슨한 결합으로 만들어 줄 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 옵저버 패턴을 이용할 수 있다는 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;느슨한 결합&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;느슨한 결합은 객체 내부에서 객체를 직접적으로 사용하는 의존성(=강한 결합)을 줄이려는 시도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강하게 결합되어 있다는 의미는 A의 변화에 B도 맞춰서 변해야 한다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;A함수에서 문제가 발생해서 A함수를 &amp;nbsp;고쳤는데, B 함수가 터진다는 말이다.&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강한 결합은 객체의 유연성과 코드 재사용성을 떨어뜨린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 다룬&amp;nbsp;Youtube 객체와 Observer 객체들은 현재 강한 결합을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Youtube 객체가 Observer의 메서드를 실행하기 위해서는 각 Observer들의 구현사항을 알고 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 Observer들의 구현사항에 변화가 생긴다면 Youtube 객체가 그에 맞춰 바뀌어야 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가 느슨한 결합으로 상호작용한다는 것은, 서로에 대해 잘 모른다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 잘 모른다는 것이지 아예 모른다는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 두 객체가 소통하기 위한 공통 규약등을 정해야 하며, 인터페이스가 있는 언어에서는 인터페이스에 이를 정의하는 편이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;느슨한 결합은 시스템의 유지보수를 더욱 용이하게 하고, 전체 프레임워크를 안정적으로 만들며 시스템 유연성을 증가시키곤 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 youtube 예제를 느슨한 결합으로 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1642512160821&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Youtube{
	constructor(){
    	this.observer=null;
    }
    
    register(observer){
    	this.observer=observer;
    }
    notifySubscriber(data){
    	this.observer.update(data);
    }
}

class Observer{
	update(data){
    	...
    }
}

class Jane extends Observer{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 예시가 가능하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서는 자바의 interface 개념이 없기 때문에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observer 클래스를 인터페이스처럼 활용했고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observer 클래스를 확장한 Jane 클래스와 Youtube 클래스의 관계가 느슨한 관계가 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 interface에서의 예를 잠시 들어보면&lt;/p&gt;
&lt;pre id=&quot;code_1642513614908&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface Observer {
    update(Data data);
}

public class Jane extends implements Observer{
	public void update(Data data){
    	....
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 인터페이스를 선언하고, 클래스에서는 해당 인터페이스를 구현하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스에 선언된 메서드는 클래스에서 필수적으로 구현해야 하기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observer 인터페이스를 Implement하는 모든 클래스(옵저버)들에 update가 공통규약으로서 구현 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 코드를 더 발전시켜 옵저버 패턴을 적용해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 패턴에서, 옵저버블과 옵저버는 1:N 관계를 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 객체에 공통규약으로서 필요한 메서드는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버블에는 구독, 구독취소, 알림 메서드를 정의하여 여러 옵저버를 다룰 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버는 옵저버블이 알림 메서드를 활용할 때, 알림을 받고 개별 동작을 수행할 수 있는 메서드를 정의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1642514067521&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Youtube{
	constructor(){
    	this.observer=[];
    }
    
    subscribe(observer){
    	this.observer.push(observer);
    }
    unsubscribe(observer){
    	this.observer = this.observer.filter(obs =&amp;gt; obs !== observer);
    }
    notifySubscriber(data){
    	this.observer.forEach(obs =&amp;gt; obs.update(data));
    }
}

class Observer{
	update(data){
    	...
    }
}

class Jane extends Observer{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 동작하려면 구독/구독 해제 시 기존 구독 여부를 알아야 하는 등 추가적인 로직이 필요하지만, 아무튼 이렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Youtube(옵저버블) 클래스는 구독, 구독해제, 알림 기능을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(notify에서 옵저버들의 메서들을 직접 실행했으나, 다른 방법으로도 구현할 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jane(옵저버) 클래스는 옵저버 클래스를 확장하였으며 update 메서드를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;update 메서드에는 Jane 클래스가 구독한 옵저버블의 상태변화가 일어났을 때 행동해야 할 로직이 담겨있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 메서드들은 옵저버와 옵저버블의 역할을 수행하기에 최소한의 메서드들을 가지고 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적인 메서드들을 보유할 수도 있다.(당연히 보유할 것이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, 각각의 객체들은 공통 규약(정해진 메서드) 외에 상대의 구현사항을 알 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 옵저버패턴을 이용하면 옵저버와 옵저버블 간 의존성을 낮출 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;옵저버 패턴에서는 주의할 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버와 옵저버블 간 모르는 상태가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통 규약을 통해 상대 객체에서 활용하는 메서드들을 알고 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버블에 옵저버를 등록하는 첫 과정을 통해 서로 알고 있다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버블과 옵저버 간 서로 아예 모르는 패턴도 존재하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구독-발행 패턴(pub-sub pattern)이 그러하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pub-sub 패턴은 기존 옵저버와 옵저버블의 사이에, 브로커 혹은 메시지 큐라는 중개자를 두고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버 &amp;lt;-&amp;gt; 브로커 &amp;lt;-&amp;gt; 옵저버블 의 소통을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pub-sub 패턴에 대해서는 다음 포스팅에서 정리해 볼 예정이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고한 출처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/옵서버_패턴&quot;&gt;https://ko.wikipedia.org/wiki/옵서버_패턴&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pjh3749.tistory.com/266&quot;&gt;https://pjh3749.tistory.com/266&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/azure/architecture/patterns/publisher-subscriber&quot;&gt;https://docs.microsoft.com/ko-kr/azure/architecture/patterns/publisher-subscriber&lt;/a&gt;&lt;/p&gt;</description>
      <category>Design Pattern</category>
      <category>옵저버패턴</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/121</guid>
      <comments>https://gobae.tistory.com/121#entry121comment</comments>
      <pubDate>Tue, 18 Jan 2022 23:04:20 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 클래스란?</title>
      <link>https://gobae.tistory.com/120</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGHHrA/btrq2keR9VN/8PKEB0OjsKVwnNf0fTl7bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGHHrA/btrq2keR9VN/8PKEB0OjsKVwnNf0fTl7bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGHHrA/btrq2keR9VN/8PKEB0OjsKVwnNf0fTl7bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGHHrA%2Fbtrq2keR9VN%2F8PKEB0OjsKVwnNf0fTl7bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1898&quot; height=&quot;550&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클래스, 객체, 인스턴스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스는 객체를 만들어내기 위한 설계도 혹은 틀이며, 연관되어 있는 변수와 메서드의 집합이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스는 객체 정의를 위한 상태(멤버변수)와 메서드(함수)로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스와 객체, 인스턴스 간 연관을 짧게 설명하자면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;객체&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 세계에서 구현할 대상으로, 클래스에 선언된 형태 그대로 생성된 실체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인스턴스&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스(설계도)로부터 구현된 구체적인 실체를 의미하며&lt;br /&gt;클래스로부터 객체를 생성했다면, 그것을 인스턴스라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, Person 클래스라면 Person 클래스로 구현한 Tom은 인스턴스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체와 인스턴스의 관계는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 인스턴스는 객체에 포함된다.&lt;br /&gt;2. &lt;code&gt;oop 관점에서, 객체가 메모리에 할당되어 실제로 사용&lt;/code&gt;되면 &quot;인스턴스&quot; 라고 한다.&lt;br /&gt;3. &lt;code&gt;추상적&lt;/code&gt; 개념과 &lt;code&gt;구체적&lt;/code&gt; 객체 사이 &lt;code&gt;&quot;관계&quot;&lt;/code&gt;에 초점을 맞춘다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1. &lt;code&gt;&quot;~의 인스턴스&quot;&lt;/code&gt; 라는 이름으로 불린다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;2. &lt;code&gt;객체는 클래스의 인스턴스&lt;/code&gt;다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;3. 객체간 링크는, 클래스 간 연관관계의 인스턴스다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;4. &lt;code&gt;실행 프로세스는, 프로그램의 인스턴스&lt;/code&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;rarr; 사실 이와 같이 인스턴스라는 용어는 반드시 클래스/객체 사이의 관계로 한정지어 사용할 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 결론은, &lt;code&gt;인스턴스는 원본(추상적 개념)으로부터 생성된 복제본&lt;/code&gt; 이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클래스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스를 생성할 때는,&lt;/p&gt;
&lt;pre class=&quot;nimrod&quot;&gt;&lt;code&gt;    class Person{
      constructor(name, age){
        this.name = name;
        this.age = age;
      }

      sayName(){
        return `hi my name is ${this.name}`;
      }
      method2(){...}
      method3(){...}
    }

    const tom = new Person('Tom', 23);
    tom.sayName();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 키워드와 함께 클래스 이름을 호출하고, 생성자(constructor)에서 받을 인자를 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자(constructor)는 객체의 기본 상태를 설정해주는데&lt;br /&gt;new 키워드에 의해 자동 호출되어 클래스에 의해 생성될 객체를 초기화해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서는,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Person 클래스(설계도)로부터 새로운 객체(구체적인 실체) tom을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 인수 'Tom', 23이 constructor에 의해 this.name, this.age에 할당된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클래스 분해하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 자바스크립트에서는 클래스가 없어서 함수와 new 연산자를 활용해 클래스를 구현했었다.&lt;br /&gt;그리고 ES2015(ES6)에서 추가되어서 클래스를 이용해 객체지향을 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄연히 말하면, 다른 언어의 클래스와는 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 클래스는 Class 키워드를 사용하지만 &lt;b&gt;내부적으로는 프로토타입&lt;/b&gt;을 따르기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d14iIH/btrq4rdJxRW/nADCbnvxCHqvF8OYdKnUZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d14iIH/btrq4rdJxRW/nADCbnvxCHqvF8OYdKnUZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d14iIH/btrq4rdJxRW/nADCbnvxCHqvF8OYdKnUZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd14iIH%2Fbtrq4rdJxRW%2FnADCbnvxCHqvF8OYdKnUZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;525&quot; height=&quot;339&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 코드에서, Person 클래스의 인스턴스 tom을 보면&lt;br /&gt;생성자와 sayName 메서드가 프로토타입에 구현이 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES6에서 새롭게 등장한 개체가 아니기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, 자바스크립트에서 클래스는 함수의 일종이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;80&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RLdzK/btrq1f6d11Y/S2kMnLZSlCAkyvbLSiIwZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RLdzK/btrq1f6d11Y/S2kMnLZSlCAkyvbLSiIwZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RLdzK/btrq1f6d11Y/S2kMnLZSlCAkyvbLSiIwZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRLdzK%2Fbtrq1f6d11Y%2FS2kMnLZSlCAkyvbLSiIwZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;38&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;80&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 위에서 구현한 클래스를 함수와 프로토타입, new 키워드로 구현할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    function  Person(name, age){
      this.name = name;
      this.age = age;
    }

    User.prototype.sayName = function(){
      return `hi my name is ${this.name}`;
    }

    const tom = new Person('Tom', 23);
    tom.sayName();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/61tLr/btrq1gKTyYD/BKxe4kaEu4zjXNJRHORkRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/61tLr/btrq1gKTyYD/BKxe4kaEu4zjXNJRHORkRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/61tLr/btrq1gKTyYD/BKxe4kaEu4zjXNJRHORkRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F61tLr%2Fbtrq1gKTyYD%2FBKxe4kaEu4zjXNJRHORkRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;343&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전히 똑같은 객체는 아니지만, 거의 같다고 할 수 있는 객체와 내부 구현을 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clsss 키워드가 하는 일은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 클래스 이름을 가진 함수를 만들고, 함수 본문(arg1,arg2,...)을 생성자에서 가져온다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 클래스에 생성자가 없을 수도 있어서 이 때는 본문()이 빈 함수가 만들어진다.&lt;br /&gt;2. 해당 함수의 프로토타입에 클래스에서 정의한 메서드들을 저장한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;클래스와 생성자 함수 방식의 차이&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방식이 결국 프로토타입을 이용하며 new 키워드를 사용하는 점에서 유사하지만&lt;br /&gt;클래스는 단순히 생성자 함수를 편하게 쓰기 위한 그 자체이지는 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function 키워드로 생성한 Person 함수는, Person()의 호출과 new Person()의 호출이 가능해서&lt;br /&gt;new 키워드로만(+정확히 말하면 파스칼케이스인 함수 이름까지) 생성자함수 사용을 고려하는 반면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;class 키워드에 의해 생성된 함수에서는 특수 프로퍼티 IsClassConstructor가 있으며 true 값을 가진다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로퍼티에 의해 해당 함수는 new 키워드 없이 호출할 수 없다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIvV3E/btrq600oFZ2/r3KvBBTM75E3N69g0EvPV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIvV3E/btrq600oFZ2/r3KvBBTM75E3N69g0EvPV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIvV3E/btrq600oFZ2/r3KvBBTM75E3N69g0EvPV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIvV3E%2Fbtrq600oFZ2%2Fr3KvBBTM75E3N69g0EvPV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1104&quot; height=&quot;452&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 console.log(Person); 와 같이 문자열화해서 클래스를 출력할 때&lt;br /&gt;class의 내부 구현 형태가 나타나는데, 이것 또한 IsClassConstructor의 덕분이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두번째 차이로는, 클래스 메서드는 열거가 불가능하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 프로퍼티 플래그에서 학습한 enumerable이 false인 것인데,&lt;/p&gt;
&lt;figure id=&quot;og_1642484980199&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자바스크립트] 객체 프로퍼티 플래그와 설명자(Flag &amp;amp; Descriptor)&quot; data-og-description=&quot;객체 프로퍼티란? 객체에서는 key:value 쌍으로 프로퍼티가 저장되며 각각을&amp;nbsp;key : 프로퍼티 이름, value : 프로퍼티 값 으로도 부른다. 그러므로, key:value의 한 쌍을 프로퍼티라고 생각하면 되겠다. &quot; data-og-host=&quot;gobae.tistory.com&quot; data-og-source-url=&quot;https://gobae.tistory.com/118&quot; data-og-url=&quot;https://gobae.tistory.com/118&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/2zHJc/hyM72h7Lc6/euwS0BjtUzoSnfd4emyJwk/img.png?width=800&amp;amp;height=241&amp;amp;face=0_0_800_241,https://scrap.kakaocdn.net/dn/cnIdEX/hyM723vaFa/GgJSmWkj3j2x5Ng461p3Zk/img.png?width=800&amp;amp;height=241&amp;amp;face=0_0_800_241,https://scrap.kakaocdn.net/dn/bD1BUx/hyM8af9e0s/6DazvCXpJVAQE3p4Gr8Kx1/img.png?width=1614&amp;amp;height=488&amp;amp;face=0_0_1614_488&quot;&gt;&lt;a href=&quot;https://gobae.tistory.com/118&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gobae.tistory.com/118&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/2zHJc/hyM72h7Lc6/euwS0BjtUzoSnfd4emyJwk/img.png?width=800&amp;amp;height=241&amp;amp;face=0_0_800_241,https://scrap.kakaocdn.net/dn/cnIdEX/hyM723vaFa/GgJSmWkj3j2x5Ng461p3Zk/img.png?width=800&amp;amp;height=241&amp;amp;face=0_0_800_241,https://scrap.kakaocdn.net/dn/bD1BUx/hyM8af9e0s/6DazvCXpJVAQE3p4Gr8Kx1/img.png?width=1614&amp;amp;height=488&amp;amp;face=0_0_1614_488');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[자바스크립트] 객체 프로퍼티 플래그와 설명자(Flag &amp;amp; Descriptor)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;객체 프로퍼티란? 객체에서는 key:value 쌍으로 프로퍼티가 저장되며 각각을&amp;nbsp;key : 프로퍼티 이름, value : 프로퍼티 값 으로도 부른다. 그러므로, key:value의 한 쌍을 프로퍼티라고 생각하면 되겠다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gobae.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;생성자 함수를 통해 만든 객체를 순회할 때는 메서드가 enumerable이 true라는 의미일테니 코드를 통해 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 생성자 함수를 통해 객체를 만들면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ2mzW/btrq35hruWY/kiQZzuwxHsXdzHkLWNQ2Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ2mzW/btrq35hruWY/kiQZzuwxHsXdzHkLWNQ2Ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ2mzW/btrq35hruWY/kiQZzuwxHsXdzHkLWNQ2Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ2mzW%2Fbtrq35hruWY%2FkiQZzuwxHsXdzHkLWNQ2Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1116&quot; height=&quot;580&quot; data-origin-width=&quot;1116&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토타입에 정의된 sayName 메서드가 조회된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 클래스를 통해 생성된 객체에서는 조회되지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wh2IW/btrq4v72RBi/hVGczDzlsALCMsWiJPijRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wh2IW/btrq4v72RBi/hVGczDzlsALCMsWiJPijRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wh2IW/btrq4v72RBi/hVGczDzlsALCMsWiJPijRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwh2IW%2Fbtrq4v72RBi%2FhVGczDzlsALCMsWiJPijRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1106&quot; height=&quot;556&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for... in, Object.keys, Object.entries 등의 객체 프로퍼티를 조회하는 메서드는&lt;br /&gt;Number, string, Array 등의 값을 가진 프로퍼티를 조회하는 목적이 크고&lt;br /&gt;프로퍼티 메서드를 순회하려는 목적으로 사용해본 적은 없는 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그 외에 클래스는 use strict(엄격 모드)로 실행이 되며&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;extends, super 키워드를 통한 쉬운 상속 구현이 가능한 점&lt;/b&gt; 등등의 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(물론 생성자 함수에서도 프로토타입을 이용하면서 상속을 구현할 수 있다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Getter &amp;amp; Setter&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 다룬 Getter, Setter 포스팅에서 클래스에서도 사용이 가능하다고 했다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    class Person{
      constructor(name, age){
        this.name=name;
        this.age=age;
      } // 생성자가 없으면 setter가 동작하지 않는다.

      get name(){
        return this._name;
      }

      set name(value){
        if(value.length &amp;lt; 3){
          console.log(&quot;이름은 3글자 이상이어야 합니다.&quot;);
          return;
        }
        this._name=value;
      }
    }

    const tom = new Person('Tom', 23);
    console.log(tom.name); // Tom

    tom.name = 'aa'; // 이름은 3글자 이상이어야 합니다.
    console.log(tom.name); // Tom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 getter와 setter를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getter와 setter 역시 프로토타입에 정의되며, 특정 프로퍼티의 호출, 할당, 변경에 대해 부가적인 옵션을 부여할 수 있다.&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트에서의 클래스</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/120</guid>
      <comments>https://gobae.tistory.com/120#entry120comment</comments>
      <pubDate>Tue, 18 Jan 2022 14:49:44 +0900</pubDate>
    </item>
    <item>
      <title>객체 프로퍼티의 getter와 setter</title>
      <link>https://gobae.tistory.com/119</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1878&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YOuCg/btrqRSDEqOS/PShL0q6HgLJ7nV5y7Ft6w0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YOuCg/btrqRSDEqOS/PShL0q6HgLJ7nV5y7Ft6w0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YOuCg/btrqRSDEqOS/PShL0q6HgLJ7nV5y7Ft6w0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYOuCg%2FbtrqRSDEqOS%2FPShL0q6HgLJ7nV5y7Ft6w0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1878&quot; height=&quot;540&quot; data-origin-width=&quot;1878&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 프로퍼티의 종류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 프로퍼티는 데이터 프로퍼티, 접근자 프로퍼티로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 프로퍼티(Data Property)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key:value 형태의 값에 문자열, 숫자, 함수, 객체, 배열 등등의 값이 들어간 모든 프로퍼티를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;접근자 프로퍼티(Accessor Property)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근자 프로퍼티는 함수이며, get과 set의 역할을 한다.&lt;br /&gt;그러나, 외부에서 해당 객체를 사용할 때 접근자 프로퍼티를 일반 프로퍼티처럼 사용할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Getter &amp;amp; Setter&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근자 프로퍼티는 getter, setter 메서드로 표현될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그리고 객체에서 getter, setter는 각각 get, set으로 나타낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getter 메서드는 객체 프로퍼티를 가져오는 함수이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setter 메서드는 객체 프로퍼티 값을 설정하는 함수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getter 메서드는 obj.property 로 프로퍼티를 읽을 때 실행되며&lt;br /&gt;setter 메서드는 obj.property = value 로 프로퍼티에 값을 할당할 때 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;    const obj = {
          macro:'hi',

        get hello(){
          return this.macro;
        },
          set hello(value){
          this.macro = value;
        }
    };

    console.log(obj.hello); // hi
    obj.hello = 'hello'; 
    console.log(obj.hello); // hello&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 프로퍼티를 호출할 때 get, 프로퍼티에 값을 할당할 때 set 메서드가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 내부에서는 get, set 키워드를 사용했지만&lt;br /&gt;객체 외부에서는 일반 프로퍼티처럼 &lt;code&gt;obj.hello&lt;/code&gt; 를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 getter, setter를 모두 구현할 필요는 없이 둘 중 하나만 사용할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const obj = {
          macro:'hi',

          get hello(){
          return this.macro;
        },
    };

    console.log(obj.hello); // hi
    obj.hello = 'this is not work';
    console.log(obj.hello); // hi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 코드를 작성하면, setter가 존재하지 않기 때문에 getter에서 출력된 값이 변하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hello 메서드는 가상 프로퍼티로, 읽고 쓸 수 있으나 실제로 존재하는 프로퍼티는 아니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;접근자 프로퍼티 설명자(Descriptor)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서도 다뤘듯이,&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1642421048747&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자바스크립트] 객체 프로퍼티 플래그와 설명자(Flag &amp;amp; Descriptor)&quot; data-og-description=&quot;객체 프로퍼티란? 객체에서는 key:value 쌍으로 프로퍼티가 저장되며 각각을&amp;nbsp;key : 프로퍼티 이름, value : 프로퍼티 값 으로도 부른다. 그러므로, key:value의 한 쌍을 프로퍼티라고 생각하면 되겠다. &quot; data-og-host=&quot;gobae.tistory.com&quot; data-og-source-url=&quot;https://gobae.tistory.com/118&quot; data-og-url=&quot;https://gobae.tistory.com/118&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cTeVG1/hyM6JjB7IR/ehEckYpugSIYakRxxlMsW1/img.png?width=800&amp;amp;height=241&amp;amp;face=0_0_800_241,https://scrap.kakaocdn.net/dn/roZ0e/hyM71315cK/ollUGpFIOcLxyTaDu8kn9k/img.png?width=800&amp;amp;height=241&amp;amp;face=0_0_800_241,https://scrap.kakaocdn.net/dn/b02URQ/hyM70KNPwI/7Wh0YG5xktFkjmYLtXnAxk/img.png?width=1614&amp;amp;height=488&amp;amp;face=0_0_1614_488&quot;&gt;&lt;a href=&quot;https://gobae.tistory.com/118&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gobae.tistory.com/118&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cTeVG1/hyM6JjB7IR/ehEckYpugSIYakRxxlMsW1/img.png?width=800&amp;amp;height=241&amp;amp;face=0_0_800_241,https://scrap.kakaocdn.net/dn/roZ0e/hyM71315cK/ollUGpFIOcLxyTaDu8kn9k/img.png?width=800&amp;amp;height=241&amp;amp;face=0_0_800_241,https://scrap.kakaocdn.net/dn/b02URQ/hyM70KNPwI/7Wh0YG5xktFkjmYLtXnAxk/img.png?width=1614&amp;amp;height=488&amp;amp;face=0_0_1614_488');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[자바스크립트] 객체 프로퍼티 플래그와 설명자(Flag &amp;amp; Descriptor)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;객체 프로퍼티란? 객체에서는 key:value 쌍으로 프로퍼티가 저장되며 각각을&amp;nbsp;key : 프로퍼티 이름, value : 프로퍼티 값 으로도 부른다. 그러므로, key:value의 한 쌍을 프로퍼티라고 생각하면 되겠다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gobae.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근자 프로퍼티에도 설명자가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이전 글에서 설명한 설명자는 데이터 프로퍼티의 설명자에 해당하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 접근자 프로퍼티의 설명자에 대해 배워보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 설명자에는 value, writable, enumerable, configurable 속성이 있으며&lt;br /&gt;접근자 프로퍼티에는 &lt;b&gt;get, set, enumerable, configurable&lt;/b&gt; 속성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 설명자들의 개념은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;get : 인수가 없으며, 프로퍼티를 읽을 때 동작한다.&lt;br /&gt;set : 인수가 하나이며, 프로퍼티에 값을 할당할 때 호출된다.&lt;br /&gt;enumarable : 나열 가능성으로, ture일 시 반복문 등을 통해 나열이 가능하며 false일 때는 나열이 불가능하다.&lt;br /&gt;configurable : 설정 가능성으로, true일 시 프로퍼티 삭제 / 플래그 수정이 가능하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;false일 때는 프로퍼티 삭제 플래그 수정이 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;set 설명자의 인수가 하나인 이유&lt;/b&gt;는 간단한데,&lt;br /&gt;&lt;b&gt;&lt;code&gt;obj.propertyName = value;&lt;/code&gt; 와 같이 setter를 활용하므로&lt;/b&gt;, 인수는 value 하나이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 프로퍼티와 동일하게 접근자 프로퍼티에도 defineProperty를 통해 getter, setter를 생성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const obj = {
          macro:'hi',
    };

    Object.defineProperty(obj, 'hello', {
      get(){
        return this.macro;
      },
      set(value){
        this.macro = value;
      }
    });

    console.log(obj.hello); // hi
    obj.hello = 'hello world!';
    console.log(obj.hello); // hello world!

    Object.getOwnPropertyDescriptor(obj, 'hello');
    // obj.hello 프로퍼티 또한 설명자 객체를 얻을 수 있으며
    // 설명자 객체는 다음의 형태를 가진다.
    // configurable: false
    // enumerable: false
    // get: &amp;fnof; get()
    // set: &amp;fnof; set(value)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;getter, setter 활용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getter, setter에 해당하는 접근자 프로퍼티에서 호출, 할당 시 추가 로직을 부여할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;  const obj = {
      get name(){
          return this._name;
      },

      set name(value){
          if(value.length &amp;lt; 3){
              console.log('이름은 3글자 이상이어야 합니다.');
              return;
          }
          this._name = value;
      }
  };

  obj.name = 'TT'; // 이름은 3글자 이상이어야 합니다.
  console.log(obj.name); // undefined

  obj.name = 'Tom';
  console.log(obj.name); // Tom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 작성하면, obj.name 프로퍼티를 호출하면 get의 로직에 따르게 되고,&lt;br /&gt;obj.name = value로 name 프로퍼티에 값을 할당하면 set의 로직에 따르게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set 메서드를 통해 name 프로퍼티에 3글자 미만의 이름을 할당할 수 없으므로 'TT'를 할당할 수 없고&lt;br /&gt;'Tom'은 3글자 이상이므로 성공적으로 할당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 메서드 마지막에 위치한 &lt;b&gt;_name 프로퍼티&lt;/b&gt;는 protected 속성이며, 객체 내부와 그 자손에서만 사용하겠다는 의미다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;물론, 관습상 _으로 시작하는 속성은 외부에서 호출하지 않는 것이지 &lt;b&gt;'호출이 불가능한 속성은 아니다'.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이, 접근자 프로퍼티는 데이터 프로퍼티와 연관이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 임의로 데이터 프로퍼티를 검증하는 로직을 setter에 넣어서 원하는 동작을 구현할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 객체의 속성 값에 대하여, 제약조건을 부여할 수도 있고 변경이 불가능하게 만들 수도 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이후 사용처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 getter, setter는 앞으로 다룰 &lt;b&gt;Class&lt;/b&gt;와 &lt;b&gt;Proxy 객체&lt;/b&gt;에서도 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 작동 원리는 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getter는 프로퍼티를 읽는 과정에 관여하며,&lt;br /&gt;setter는 프로퍼티에 값을 할당할 때의 과정에 관여한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고 출처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/property-accessors&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.javascript.info/property-accessors&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://beomy.tistory.com/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://beomy.tistory.com/14&lt;/a&gt;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/119</guid>
      <comments>https://gobae.tistory.com/119#entry119comment</comments>
      <pubDate>Mon, 17 Jan 2022 21:09:26 +0900</pubDate>
    </item>
    <item>
      <title>객체 프로퍼티 플래그와 설명자(Flag &amp;amp; Descriptor)</title>
      <link>https://gobae.tistory.com/118</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLllrd/btrqQd8PUuf/Gke8NXeXMOtlV99Y9BOil1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLllrd/btrqQd8PUuf/Gke8NXeXMOtlV99Y9BOil1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLllrd/btrqQd8PUuf/Gke8NXeXMOtlV99Y9BOil1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLllrd%2FbtrqQd8PUuf%2FGke8NXeXMOtlV99Y9BOil1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1614&quot; height=&quot;488&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 프로퍼티란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체에서는 key:value 쌍으로 프로퍼티가 저장되며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각을&amp;nbsp;key : 프로퍼티 이름, value : 프로퍼티 값 으로도 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로, key:value의 한 쌍을 프로퍼티라고 생각하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅에선 객체 프로퍼티의 특수한 옵션인 플래그(Flag)와 설명자(Descrpitor)라는 강력한 기능을 알아본다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로퍼티 플래그&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티 플래그는 writable, enumarable, configurable의 3가지 속성으로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 개념은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;writable&lt;/b&gt;&lt;br /&gt;값 수정 가능성.&lt;br /&gt;true일 시 값을 수정할 수 있으며, false일 때는 읽기만 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;enumerable&lt;/b&gt;&lt;br /&gt;나열 가능성&lt;br /&gt;ture일 시 반복문 등을 통해 나열이 가능하며, false일 때는 나열이 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;configurable&lt;/b&gt;&lt;br /&gt;설정 가능성.&lt;br /&gt;true일 시 프로퍼티 삭제 / 플래그 수정이 가능하며, false일 때는 프로퍼티 삭제 플래그 수정이 불가능하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로퍼티 플래그 확인하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 프로퍼티들은 각각 플래그를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 리터럴{}로 생성한 객체에서는, 각각 프로퍼티 플래그는 모두 true 값을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티 플래그를 확인하기 위해서는&lt;br /&gt;&lt;code&gt;Object.getOwnPropertyDescriptor&lt;/code&gt;를 사용하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드의 인자로 &lt;b&gt;(대상 객체, 프로퍼티 이름)&lt;/b&gt;을 넣어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;    const obj = {
      name : 'Tom',
      age : 23,
    };

    Object.getOwnPropertyDescriptor(obj, 'name');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/da6oFd/btrq0X32nyX/d0NZkaWYZKbegsMggb22Vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/da6oFd/btrq0X32nyX/d0NZkaWYZKbegsMggb22Vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/da6oFd/btrq0X32nyX/d0NZkaWYZKbegsMggb22Vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fda6oFd%2Fbtrq0X32nyX%2Fd0NZkaWYZKbegsMggb22Vk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;364&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;getOwnPropertyDescriptor&lt;/code&gt; 메서드의 결과로 새로운 객체가 리턴되는데, 이를 &lt;b&gt;프로퍼티 설명자&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림과 같이 프로퍼티 설명자는 &lt;b&gt;프로퍼티 값, 3가지 프로퍼티 플래그의 불린값&lt;/b&gt;을 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 프로퍼티마다 이 프로퍼티 설명자가 존재하며, 플래그나 프로퍼티 값을 변경하면 설명자의 값이 변경된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로퍼티 값, 플래그 변경하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Object.definedProperty&lt;/code&gt;로 프로퍼티 값과 플래그를 변경할 수 있으며,&lt;br /&gt;메서드의 인자로는 &lt;b&gt;(대상 객체, 프로퍼티 이름, 변경하려는 설명자의 프로퍼티 이름과 값)&lt;/b&gt; 을 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 변경하려는 &lt;b&gt;프로퍼티가 있다면 변경을 수행&lt;/b&gt;하며,&lt;br /&gt;&lt;b&gt;프로퍼티가 없다면 프로퍼티를 생성하며 플래그 값은 기본적으로 false&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;    const obj = {
      name:'Tom'
    };

    Object.defineProperty(obj, 'name', {value:'James'});
    Object.getOwnPropertyDescriptor(obj, 'name');

    const obj2 = {};
    Object.defineProperty(obj2, 'name', {value:'Tom'});
    Object.getOwnPropertyDescriptor(obj2, 'name');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AL6KL/btrq1fwHBFt/nRD2Gy5tw2LVftvI1fWc9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AL6KL/btrq1fwHBFt/nRD2Gy5tw2LVftvI1fWc9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AL6KL/btrq1fwHBFt/nRD2Gy5tw2LVftvI1fWc9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAL6KL%2Fbtrq1fwHBFt%2FnRD2Gy5tw2LVftvI1fWc9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;528&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj는 프로퍼티 name을 가지고 있으며, obj2는 프로퍼티 name를 가지고 있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj는 설명자에서 value 값만 바뀌었으며&lt;br /&gt;obj2는 설명자에서 value 값을 생성하면서 플래그값이 모두 false로 세팅되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;    obj2.name = 'Tom';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방식으로 새로운 프로퍼티를 만들면 플래그 기본값이 true이지만,&lt;br /&gt;definedProperty 메서드를 이용하면 플래그 기본값은 false임을 주의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;만약 definedProperty를 이용하면서 플래그를 true로 설정하고 싶다면, 3번째 인자값에 추가로 부여하면 된다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;    Object.defineProperty(obj2, 'name', {
    	value:'Tom', 
        writable:true, 
        enumerable:true, 
        configurable:true
      }
    );&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;플래그 살펴보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 3가지 플래그들의 동작을 코드를 통해 알아보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Writable&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 수정 가능성.&lt;br /&gt;true일 시 값을 수정할 수 있으며, false일 때는 읽기만 가능하다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;  const obj = {
      name:'Tom'
  };

  Object.defineProperty(obj, 'name', {writable:false} );
  obj.name = 'James';
  obj;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4jfjs/btrqQe0RmEF/c4A8dc3S2u7qv7jc33I0Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4jfjs/btrqQe0RmEF/c4A8dc3S2u7qv7jc33I0Lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4jfjs/btrqQe0RmEF/c4A8dc3S2u7qv7jc33I0Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4jfjs%2FbtrqQe0RmEF%2Fc4A8dc3S2u7qv7jc33I0Lk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;354&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj 객체의 name 프로퍼티의 '값 수정 가능성'을 false로 변경하여 읽기만 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;writable:false 속성은 &lt;b&gt;'strict mode'에서만 오류를 발생&lt;/b&gt;시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 원래대로라면 obj.name='James' 코드에서 오류를 발생시켜야 하지만,&lt;br /&gt;'sloppy mode'를 기본으로 하는 브라우저(크롬)의 콘솔에서 작업했기 때문에, 변경 작업은 무시되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 콘솔에서 strict mode를 사용하고 싶다면 &lt;a href=&quot;https://ko.javascript.info/strict-mode&quot;&gt;다음&lt;/a&gt;을 참조하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 obj 객체를 조회했을 때, name:'Tom' 값이 변경되지 않았음을 볼 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Enumerable&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나열 가능성&lt;br /&gt;ture일 시 반복문 등을 통해 나열이 가능하며, false일 때는 나열이 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 프로퍼티를 나열하기 위해서, for in 문을 사용해본다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;  const obj = {
      name:'Tom',
      age:23,
      hello(){
          return 'world'
      },
  }
  for(let i in obj){
    console.log(i);
  }

  Object.defineProperty(obj, 'hello', {enumerable:false});
  for(let i in obj){
    console.log(i);
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwqECX/btrq0XJMWkb/gKNYlanAR7yLdLlxpjBvmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwqECX/btrq0XJMWkb/gKNYlanAR7yLdLlxpjBvmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwqECX/btrq0XJMWkb/gKNYlanAR7yLdLlxpjBvmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwqECX%2Fbtrq0XJMWkb%2FgKNYlanAR7yLdLlxpjBvmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;555&quot; height=&quot;654&quot; data-origin-width=&quot;1114&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문에서는 enumerable:false인 요소를 불러오지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 두번째 for 문에서는, hello 프로퍼티가 조회되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점으로는&lt;br /&gt;Object.keys를 이용해 프로퍼티 키들을 배열로서 얻고자 할 때&lt;br /&gt;enumerable:false인 값은 역시 조회되지 않음에 유의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;entries, keys, values 메서드 모두 열거를 통해 배열 원소에 키를 넣기 때문에,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enumarable 플래그에 따라 동작하는 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;configurable&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 가능성.&lt;br /&gt;true일 시 프로퍼티 삭제 / 플래그 수정이 가능하며, false일 때는 프로퍼티 삭제 플래그 수정이 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;configurable 플래그는 강력한 제약을 주므로, 더 주의해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;한 번 configurable 플래그를 false로 설정하면 플래그 자신에 의해 다시 되돌릴 수 없다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;  const obj = {};

  Object.defineProperty(obj, 'age', {value:23});
  Object.getOwnPropertyDescriptor(obj, 'age');
  obj.age = 150;
  obj;

  delete obj.age;
  obj;

  Object.defineProperty(obj, 'age', {configurable:true});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6bfuw/btrqQezVegy/O2qm9vMiMid55NAaORU4kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6bfuw/btrqQezVegy/O2qm9vMiMid55NAaORU4kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6bfuw/btrqQezVegy/O2qm9vMiMid55NAaORU4kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6bfuw%2FbtrqQezVegy%2FO2qm9vMiMid55NAaORU4kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;515&quot; height=&quot;672&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;configurable 옵션에 의해 해당 프로퍼티에 대한 삭제가 불가능하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티의 플래그들도 모두 변경이 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 말하면, 플래그 변경의 제약사항은&lt;br /&gt;writable : true -&amp;gt; false 가능&lt;br /&gt;enumerable : 변경 불가&lt;br /&gt;configurable : 변경 불가 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 추가적으로 getter/setter의 변경이 불가능하며, 새롭게 생성하는 것은 가능하다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ &lt;u&gt;코드에서 obj.age = 150; 동작이 수행되지 않는 것은, configurable 때문이 아니라 writable이 false이기 때문이다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;configurable은 프로퍼티 삭제 / 플래그 변경과 관련되어 있음에 유의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 여러 프로퍼티를 동시에 생성 / 변경하거나, 설명자를 조회하고 싶다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 메서드 뒷부분이 복수형이면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object.definedproperties, Object.getOwnPropertyDescriptors&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 거의 유사하니 생략하겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개별 프로퍼티가 아닌 객체 자체 수정을 막고 싶을 수도 있다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object.pretentExtension, Object.seal, Object.freeze 메서드도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pretentExtension, seal 메서드는 사용한 적이 없으나&amp;nbsp;freeze는 사용해봐서 예시를 적어보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바닐라 자바스크립트 프로젝트에서 자바의 enum가 필요했을 때가 있었다.&lt;br /&gt;타입스크립트에서는 enum 형을 제공하지만 바닐라 자바스크립트에서는 enum은 존재하지 않았었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object.freeze를 이용해 enum형과 비슷한 구조를 만들어 사용했던 기억이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 enum과 유사한 자료형을 만들기 위해서 어떻게 Object.freeze를 사용하는지는,&lt;br /&gt;&lt;a href=&quot;https://ichi.pro/ko/jaba-seukeulibteueseo-yeolgeo-hyeong-saengseong-278098955874874&quot;&gt;다음&lt;/a&gt;의 포스팅을 참고하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 프로퍼티에 저장된 값이 객체, 배열, 함수등의 참조값일 때는 수정이 가능하므로 유의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1642407399134&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const obj = {
	name : 'Tom',
    friends : ['James', 'Jane', 'Kane'],
};

Object.freeze(obj);

obj.name = 'Jacop';
console.log(obj.name);

obj.friends = [];
console.log(obj.friends);

obj.friends.push('stranger');
console.log(obj.friends);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsAEvO/btrqN2TAXOb/hNKYZ6Jke6OHNM68MwQdk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsAEvO/btrqN2TAXOb/hNKYZ6Jke6OHNM68MwQdk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsAEvO/btrqN2TAXOb/hNKYZ6Jke6OHNM68MwQdk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsAEvO%2FbtrqN2TAXOb%2FhNKYZ6Jke6OHNM68MwQdk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;514&quot; height=&quot;684&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;불변객체를 만들고 싶다면?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object.freeze와 같은 메서드로는 '얕은 변경'을 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, 위의 예시와 같이 객체가 프로퍼티 값으로 객체를 가지고 있을 때, 중첩 객체까지의 변경을 방지할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럴 때는 객체의 중첩 객체까지 재귀적으로 Object.freeze 메서드를 호출하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 아래와 같이 코드를 작성하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1642918518040&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function deepFreeze(obj){
	if(obj &amp;amp;&amp;amp; typeof obj === 'object' &amp;amp;&amp;amp; !Object.isFrozen(obj)){
    	Object.freeze(obj);
        Object.keys(obj).forEach(key =&amp;gt; deepFreeze(obj[key]));
    }
    return obj;
}

const obj = {
	name : 'Tom',
    address : {
    	city : 'Seoul',
        street : 'Dasanro'
    }
};

deepFreeze(obj);

console.log(Object.isFrozen(obj)); // true
console.log(Object.isFrozen(obj.address)); // true&lt;/code&gt;&lt;/pre&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 객체 속성 플래그와 설명자</category>
      <category>프로퍼티 설명자</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/118</guid>
      <comments>https://gobae.tistory.com/118#entry118comment</comments>
      <pubDate>Mon, 17 Jan 2022 17:17:39 +0900</pubDate>
    </item>
    <item>
      <title>래퍼 객체(Wrapper Object)란?</title>
      <link>https://gobae.tistory.com/117</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bV4X7S/btrqMjM1v1b/aBnE0grezKMVD2f0LJkqI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bV4X7S/btrqMjM1v1b/aBnE0grezKMVD2f0LJkqI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bV4X7S/btrqMjM1v1b/aBnE0grezKMVD2f0LJkqI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbV4X7S%2FbtrqMjM1v1b%2FaBnE0grezKMVD2f0LJkqI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1908&quot; height=&quot;532&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;래퍼 객체란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;래퍼객체는 이름 그대로, 감싸는 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;래퍼객체는 원시값과 관련되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원시값에는 string, number, bigInt, boolean, Symbol, null, undefined로 7가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Glossary/Primitive&quot;&gt;MDN&lt;/a&gt;에 의하면,&amp;nbsp;원시값의 정의는 &lt;b&gt;객체가 아니면서 메서드도 가지지 않는 불변의 데이터&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드를 가지지 않는다면서..?&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;    const str = 'capital letter';
    console.log(str.toUpperCase());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 실제로는 원시값을 담은 변수에서 메서드를 사용할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 자바스크립트가 원시값에 래퍼객체를 이용해서 객체의 다재다능함을 부여하기 때문에 가능하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;래퍼객체의 종류, 동작 순서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;래퍼객체에는 &lt;b&gt;String, Number, Boolean, Symbol&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;래퍼객체는 원시값에서 메서드를 호출하면, 순간적으로 생성되어 메서드에 해당하는 로직을 수행하고 결과를 반환해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;    const str = 'capital letter';
    console.log(str.toUpperCase());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 동작 순서는 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;1. str 변수에서 toUpperCase를 호출하는 순간 String 래퍼 객체가 생성된다.&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;2. 래퍼 객체가 str 값과, 자신의 메서드를 이용해 결과를 반환한다.&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;3. 결과반환을 마친 래퍼객체는 삭제된다.&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 스텝은 자바스크립트 엔진이 수행해주며, 이 덕분에 원시값의 성질을 유지하면서도 메서드 이용이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 TMI를 덧붙여 string, number 타입의 메서드 호출을 예로 들면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;래퍼 객체가 생성되면 래퍼객체의&amp;nbsp;[[StringData]] 혹은 [[NumberData]] 슬롯에 원시값을 할당하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;래퍼 객체의 역할이 끝나면 해당 슬롯을 이용하여 다시 식별자가 원시값을 갖도록 되돌린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 식별자가 원시값을 갖도록 되돌려지므로, 래퍼 객체는 가비지컬렉션의 대상이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;래퍼객체를 통한 메서드 이용 시 주의할 점으로는 &lt;b&gt;원시값은 불변의 값&lt;/b&gt;이라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드를 호출했을 때 결과가 반환되지만, 변수의 값이 변경되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로&amp;nbsp;항상 &lt;b&gt;메서드의 수행 결과를 이용할 때는, &lt;b&gt;즉시&amp;nbsp;&lt;/b&gt;결과를 이용하지 않는다면 다른 변수에 저장&lt;/b&gt;해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 관련해서, 착각하기 쉬운 케이스를 보자.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;    const str = '배가 고파요 생선님.';
    str.replace('생선','선생'); // 다음의 결과로 '배가 고파요 선생님' 을 출력한다.
    console.log(str); // 그러나 str 변수가 가리키는 원시값 자체는 변하지 않았다.

    const modifiedStr = str.replace('생선', '선생'); // 결과를 변수에 넣어줘야 한다.
    console.log(modifiedStr); &lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;생성자를 통해 래퍼객체를 생성할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자를 통한 래퍼객체 생성을 허용한 것은 하위호환을 위한 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자(new 키워드)를 통해 직접 해당 타입의 래퍼객체를 선언해서, 원시값처럼 쓰는 것은 좋지 않다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edoT8y/btrqFnw5Zm8/yxuoi1RGyMFXK1VZyVbYbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edoT8y/btrqFnw5Zm8/yxuoi1RGyMFXK1VZyVbYbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edoT8y/btrqFnw5Zm8/yxuoi1RGyMFXK1VZyVbYbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedoT8y%2FbtrqFnw5Zm8%2Fyxuoi1RGyMFXK1VZyVbYbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;458&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열로 선언한 숫자값 f를 new 키워드로 숫자화한다면, 타입이 객체가 되어버린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 타입 체크등의 로직에서 문제를 일으킬 수도 있고,&lt;br /&gt;원시값에 비해 객체는 기능이 무거워서 자원을 많이 사용하므로 필요하지 않은 객체 선언을 지양해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;숫자-&amp;gt;불린 / 문자-&amp;gt;숫자로의 형변환을 원한다면?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 키워드 없이 String/Number/Boolean 으로만 감싸주면 해당하는 원시값으로 형변환이 일어나게 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 자바스크립트의 자동 형변환을 이해하고, while(1), +'1234', '12'-'70' 등의 코드를 작성할 수도 있겠다.&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 래퍼객체</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/117</guid>
      <comments>https://gobae.tistory.com/117#entry117comment</comments>
      <pubDate>Fri, 14 Jan 2022 15:05:27 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] V8 엔진의 메모리 관리 이해하기</title>
      <link>https://gobae.tistory.com/116</link>
      <description>&lt;figure id=&quot;og_1641919046790&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자바스크립트] 비동기 작업, 이벤트 루프와 태스크 큐&quot; data-og-description=&quot;이전 글 [자바스크립트] 동기와 비동기 동기와 비동기 동기 Synchronous : 동시에 발생하는 순차적, 직렬적으로 태스크를 수행한다. 요청을 보냈다면, 응답을 받아야 다음 동작이 이루어진다. 순차&quot; data-og-host=&quot;gobae.tistory.com&quot; data-og-source-url=&quot;https://gobae.tistory.com/101&quot; data-og-url=&quot;https://gobae.tistory.com/101&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/7iEzp/hyM3LgWu35/zddwAIznQC8YHc71seUydK/img.png?width=750&amp;amp;height=724&amp;amp;face=0_0_750_724,https://scrap.kakaocdn.net/dn/TCF5E/hyM3KPRxwz/i0DeOG0T9uyZ31Bl3RBYW1/img.png?width=750&amp;amp;height=724&amp;amp;face=0_0_750_724,https://scrap.kakaocdn.net/dn/cYqlI4/hyM3I5C1YA/BQdoqNO3m65ZPrTe1nMt2k/img.png?width=1406&amp;amp;height=954&amp;amp;face=0_0_1406_954&quot;&gt;&lt;a href=&quot;https://gobae.tistory.com/101&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gobae.tistory.com/101&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/7iEzp/hyM3LgWu35/zddwAIznQC8YHc71seUydK/img.png?width=750&amp;amp;height=724&amp;amp;face=0_0_750_724,https://scrap.kakaocdn.net/dn/TCF5E/hyM3KPRxwz/i0DeOG0T9uyZ31Bl3RBYW1/img.png?width=750&amp;amp;height=724&amp;amp;face=0_0_750_724,https://scrap.kakaocdn.net/dn/cYqlI4/hyM3I5C1YA/BQdoqNO3m65ZPrTe1nMt2k/img.png?width=1406&amp;amp;height=954&amp;amp;face=0_0_1406_954');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[자바스크립트] 비동기 작업, 이벤트 루프와 태스크 큐&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이전 글 [자바스크립트] 동기와 비동기 동기와 비동기 동기 Synchronous : 동시에 발생하는 순차적, 직렬적으로 태스크를 수행한다. 요청을 보냈다면, 응답을 받아야 다음 동작이 이루어진다. 순차&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gobae.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 비동기에 대한 포스팅에서,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLapBc/btrqtVF7Oro/kdFdt5KPUodgZD8leB9mhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLapBc/btrqtVF7Oro/kdFdt5KPUodgZD8leB9mhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLapBc/btrqtVF7Oro/kdFdt5KPUodgZD8leB9mhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLapBc%2FbtrqtVF7Oro%2FkdFdt5KPUodgZD8leB9mhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;656&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 루프와 태스크 큐를 통한 비동기의 이해를 위해 잠깐 자바스크립트 엔진이 등장했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 자바스크립트 엔진을 좀 더 자세하게 이해해보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;먼저 stack, heap 메모리의 차이를 알아야 한다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uJftX/btrqmKr2GpM/xokaIwcpkkHKwZEhjwbCX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uJftX/btrqmKr2GpM/xokaIwcpkkHKwZEhjwbCX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uJftX/btrqmKr2GpM/xokaIwcpkkHKwZEhjwbCX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuJftX%2FbtrqmKr2GpM%2FxokaIwcpkkHKwZEhjwbCX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;425&quot; height=&quot;278&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;796&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OVwEN/btrqnllBP42/FBkf1PLtbTLCed1c3HlnFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OVwEN/btrqnllBP42/FBkf1PLtbTLCed1c3HlnFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OVwEN/btrqnllBP42/FBkf1PLtbTLCed1c3HlnFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOVwEN%2FbtrqnllBP42%2FFBkf1PLtbTLCed1c3HlnFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;405&quot; height=&quot;642&quot; data-origin-width=&quot;796&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스택&lt;/b&gt;&lt;br /&gt;스택은 함수 호출 시마다 지역 변수, 매개변수, 리턴값 등이 쌓이는 공간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;힙&lt;/b&gt;&lt;br /&gt;힙은 동적으로 할당되는 메모리 공간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;malloc, new 키워드 등으로 할당이 되며,&lt;br /&gt;힙 영역에 할당한 메모리 공간에 대한 주소가 참조되는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 스택과 힙은 같은 공간을 공유하며, 둘 사이에 미사용 메모리 공간이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택과 힙은 두 공간을 공유하므로, 언젠가는 서로 충돌할 수도 있다.&lt;br /&gt;-&amp;gt; 이 때 스택에 의한 충돌은 &lt;b&gt;스택 오버플로우&lt;/b&gt;, 힙에 의한 충돌은 &lt;b&gt;힙 오버플로우&lt;/b&gt; 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 주된 원인으로는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스택 오버플로&lt;/b&gt; - 함수의 재귀적 호출&lt;br /&gt;&lt;b&gt;힙 오버플로&lt;/b&gt; - 과도한 동적 메모리 할당(malloc) 이 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 비슷한 용어로 &lt;b&gt;버퍼 오버플로&lt;/b&gt;가 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 제한된 메모리를, 컴퓨터가 너무 많이 사용해서 화면이 정지되는 현상이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V8 엔진?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8 엔진은 nodeJS의 기반이 되는 엔진이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 인터프리터 언어이므로, 코드를 해석 + 실행할 수 있는 엔진이 필요하다.&lt;br /&gt;여기서 google chrome V8 엔진이 js코드를 해석하고 컴파일해서 기계어로 변환해주는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 비동기 포스팅에서 알아봤듯이, 자바스크립트는 단일스레드로 동작하는데 이벤트 루프덕에 여러 작업 처리가 가능했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;v8은 자바스크립트 컨텍스트 당 한개의 프로세스를 사용하며&lt;br /&gt;실행중인 프로그램을 v8 프로세스에서 할당된 일정량의 메모리로 표현할 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메모리는 Resident Set이라고 부르며,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1JABM/btrqoaxljuu/wMRJZ1I8av5y7E3s8vnPSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1JABM/btrqoaxljuu/wMRJZ1I8av5y7E3s8vnPSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1JABM/btrqoaxljuu/wMRJZ1I8av5y7E3s8vnPSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1JABM%2Fbtrqoaxljuu%2FwMRJZ1I8av5y7E3s8vnPSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;605&quot; height=&quot;842&quot; data-origin-width=&quot;1492&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 그림과 같은 내부 구조를 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 눈에 띄는 것은 커다란 Heap 공간과 Stack 공간이 있으며, Heap에 대해 자세하게 다루고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림을 참고하면서 각각 공간들에 대해서 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V8에서의 스택 메모리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 포스팅은 거의 힙 공간에 대한 포스팅이므로, 스택에 대해선 가볍게 다룰 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스택은 자동 관리되며, 운영체제가 관리한다.&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1641919216531&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;V8 Memory usage(Stack &amp;amp; Heap)&quot; data-og-description=&quot; &quot; data-og-host=&quot;speakerdeck.com&quot; data-og-source-url=&quot;https://speakerdeck.com/deepu105/v8-memory-usage-stack-and-heap&quot; data-og-url=&quot;https://speakerdeck.com/deepu105/v8-memory-usage-stack-and-heap&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/chddjD/hyM4v42dDP/y1zuXrO74TaitWiczrvva0/img.jpg?width=1024&amp;amp;height=576&amp;amp;face=0_0_1024_576&quot;&gt;&lt;a href=&quot;https://speakerdeck.com/deepu105/v8-memory-usage-stack-and-heap&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://speakerdeck.com/deepu105/v8-memory-usage-stack-and-heap&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/chddjD/hyM4v42dDP/y1zuXrO74TaitWiczrvva0/img.jpg?width=1024&amp;amp;height=576&amp;amp;face=0_0_1024_576');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;V8 Memory usage(Stack &amp;amp; Heap)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;speakerdeck.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택에 대해서는 위의 링크에 들어가서 실행해보길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 알 수 있는 점을 적어보면,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전역 스코프는 스택에서 '전역 프레임'에 보관된다.&lt;/li&gt;
&lt;li&gt;모든 함수 호출은 '프레임 블록'으로 스택 메모리에 추가된다.&lt;/li&gt;
&lt;li&gt;반환값과 인자를 포함한 모든 '지역변수'들은 스택에서 '함수 프레임 블록' 안에 저장된다.&lt;/li&gt;
&lt;li&gt;int, string 등의 모든 원시타입 값은 스택에 바로 저장된다.&lt;/li&gt;
&lt;li&gt;객체 타입 값은 힙에 생성되며, 스택 포인터에 의해 힙에서 스택을 참조한다.&lt;/li&gt;
&lt;li&gt;함수 내에서 다른 함수가 호출된다면, 스택의 최상단에 추가된다.&lt;/li&gt;
&lt;li&gt;함수가 종료(함수 프레임이 반환)될 때는 스택에서 제거된다.&lt;/li&gt;
&lt;li&gt;주요 프로세스가 완료되면, 힙에 있는 객체들은 스택 포인터 없이 혼자 남게 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 객체들은 결국 가비지 컬렉터에 의해 삭제될 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V8에서의 힙 메모리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙에는 &lt;b&gt;객체와 동적 데이터&lt;/b&gt;들이 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resident set의 그림에서 볼 수 있듯이, 메모리 영역 중 가장 큰 블록이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙은 스택과는 달리 메모리가 자동으로 관리되지 않으므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램의 메모리가 기하급수적으로 증가할 위험이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙에서는 메모리 관리를 위해&amp;nbsp;&lt;b&gt;가비지 컬렉션&lt;/b&gt;이 발생하며 &lt;b&gt;New, Old 영역&lt;/b&gt;에서만 실행된다.&lt;br /&gt;이 가비지컬렉션에 대해서는 각 영역에서 흐름과 관련되어 먼저 영역을 학습하고 다룰 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙 메모리는 New, Old, Large Object, Code, [cell, property cell, Map] 영역으로 나뉘며, 차례대로 살펴보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;New 영역(Young Generation)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 만들어진 모든 &lt;code&gt;객체&lt;/code&gt;가 저장된다.&lt;br /&gt;New 영역은 짧은 주기를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크기가 작고, JVM S0, S1와 같은 &lt;code&gt;semi 영역&lt;/code&gt;을 가지며,&lt;br /&gt;&lt;code&gt;스캐벤져(minor GC)&lt;/code&gt;라는 가비지 컬렉션이 이 영역을 관리한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Old 영역(Old Generation)&lt;/b&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;스캐벤져(minor GC)&lt;/code&gt; 가 2번 발생할 때 까지 살아남은 &lt;code&gt;New 영역 객체&lt;/code&gt;들이 이동하는 영역이다.&lt;br /&gt;Old 영역은 &lt;code&gt;메이저GC&lt;/code&gt;가 관리하며,&amp;nbsp;두 영역으로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Old 포인터 영역&lt;br /&gt;스캐벤져로부터&amp;nbsp;&lt;code&gt;살아남은 객체&lt;/code&gt;를 가지고 있고, 이 객체들은 &lt;code&gt;다른 객체를 참조&lt;/code&gt;한다.&lt;br /&gt;&lt;br /&gt;2. Old 데이터 영역&lt;br /&gt;&lt;code&gt;데이터만 가진 객체&lt;/code&gt;(다른 객체 참조x)를 가진다.&lt;br /&gt;&lt;code&gt;문자열, boxing된 숫자, doubled unboxing된 배열&lt;/code&gt; 등이 이 곳에 위치한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Large Object 영역&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 영역의 &lt;code&gt;제한된 크기보다 큰 객체&lt;/code&gt;가 존재하는 영역이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 객체는 자체 mmap 메모리 영역을 가지며, 가비지컬렉션에 의해 삭제되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1641920688531&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mmap(memory map) 메모리 영역이란?

프로그래밍시 디스크에 존재하는 데이터를 가져올 때의 시간을 줄이기 위해,
이 데이터(파일)이 메모리에 위치할 수 있도록 page 개념을 활용한 방식.
프로세스 가상 메모리 주소 공간에 파일을 매핑, 가상 메모리 주소에 직접 접근해서 읽기/쓰기를 수행한다고 한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Code 영역(JIT)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;컴파일러가 컴파일된 코드를 저장&lt;/code&gt;하는 곳&lt;br /&gt;유일하게 &lt;code&gt;실행 가능한 메모리&lt;/code&gt;가 있는 영역이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;셀, 속성 셀, 맵 영역&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 &lt;code&gt;Cells, PropertyCells, Maps&lt;/code&gt;를 포함한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 영역은 모두 같은 크기 객체 포함, 어떤 종류의 객체를 참조하는 지 제약을 두므로 수집을 단순하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 &lt;code&gt;영역은 페이지들로 구성&lt;/code&gt;되어 있고, &lt;code&gt;페이지 크기&lt;/code&gt;는 라지 오브젝트 영역을 제외하곤 &lt;code&gt;1MB&lt;/code&gt;이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가비지 컬렉션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택은 알아서 관리되지만, &lt;b&gt;힙 공간은&lt;/b&gt; v8엔진이 관리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 관리의 역할을 &lt;b&gt;가비지 컬렉션&lt;/b&gt;이&amp;nbsp;수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가비지 컬렉션은 &lt;b&gt;참조가 없는 객체들이 사용하는 메모리를 비워&lt;/b&gt; 새 객체를 위한 공간을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1641920700892&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;참조가 없는 객체란?

더이상 필요가 없어서 스택으로부터 주소 참조가 발생하지 않는 객체이며,
도달 가능성(reachability)이 없는 객체라고도 한다.

태생부터 도달이 가능한 객체로는,
현재 실행중인 함수의 지역 변수, 
매개 변수중첩 함수의 체인에 있는 함수에서 사용되는 지역 변수
매개변수전역 변수 등등이 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;프로그램이 사용 가능한 것보다 더 많은 메모리가 힙에 할당될 때 &lt;/span&gt;&lt;code style=&quot;letter-spacing: 0px;&quot;&gt;메모리 부족 오류&lt;/code&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙이 잘못 관리되면&amp;nbsp;&lt;code&gt;메모리 누수 위험&lt;/code&gt;이 있기 때문에, 가비지컬렉션이 잘 관리해서 이를 방지해야 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드로 살펴보는 가비지 컬렉션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가비지 컬렉션은 자바스크립트 엔진 내에서 끊임없이 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;모든 객체를 모니터링하며, 도달할 수 없는 객체를 삭제하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 간단한 예시들을 통해 알아보자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;객체를 생성하기&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;    let user = {
      name : 'Tom'
    };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 통해 객체를 생성하면, 힙에 생성된 객체의 위치를 user 변수가 기억하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;    user = null;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;user변수의 값을 다른 값으로 덮어쓰면, 기존 { name : 'Tom' } 객체로의 참조가 사라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 이제 해당 객체는, &lt;b&gt;도달할 수 없는 상태&lt;/b&gt;이다.&lt;br /&gt;사용자는 이 객에 접근할 수도 없으며, 참조할 수도 없다.&lt;br /&gt;이 상태의 객체는 가비지컬렉션의 대상이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;두 개의 변수를 이용해 참조한다면?&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;    let user = {
      name : 'Tom'
    };

    let manager = user;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 객체의 위치를 user가 기억하고 있으며, manager 역시 그 위치를 기억하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 user, manager 모두 객체의 위치를 알고 있으므로&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;    user = null;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 user를 다른 값으로 덮어 씌워도 manager가 여전히 객체의 주소를 기억하므로&lt;br /&gt;가비지컬렉션의 대상이 되지 않는다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그림을 통해서 이해하기&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/95BRr/btrqsF41OPW/rqdyku6WIOvZP9I0LzEjT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/95BRr/btrqsF41OPW/rqdyku6WIOvZP9I0LzEjT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/95BRr/btrqsF41OPW/rqdyku6WIOvZP9I0LzEjT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F95BRr%2FbtrqsF41OPW%2Frqdyku6WIOvZP9I0LzEjT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;403&quot; height=&quot;296&quot; data-origin-width=&quot;1470&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 그림으로부터 가비지컬렉션의 대상을 알기 위해선, &lt;b&gt;루트로부터의&amp;nbsp;도달 가능성&lt;/b&gt;을 봐야 한다.&lt;br /&gt;루트로부터 도달 가능한 객체가 아닌 &lt;b&gt;Object3&lt;/b&gt;은 가비지 컬렉션의 대상이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;1144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UBtAi/btrqsFRtcWA/xpqcJxv8qUhEeAqH5Tk8e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UBtAi/btrqsFRtcWA/xpqcJxv8qUhEeAqH5Tk8e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UBtAi/btrqsFRtcWA/xpqcJxv8qUhEeAqH5Tk8e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUBtAi%2FbtrqsFRtcWA%2FxpqcJxv8qUhEeAqH5Tk8e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;393&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;1144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서도 마찬가지다.&lt;br /&gt;Object 1, 3, 4는 서로 도달할 수 있지만 문제는 루트에서 이 객체들에 도달할 수가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그러므로 Object 1, 3, 4 모두 가비지컬렉션의 대상이 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;v8의 가비지컬렉션과 발생 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 가비지컬렉션이 New, Old 영역에서 이루어진다고 했는데, 각각을 마이너GC(스캐벤져), 메이저GC라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스캐벤져(Minor Garbage Collection)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙의 new 영역에 존재하면서 메모리를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 new 영역의 객체들은 &lt;code&gt;1~8MB정도&lt;/code&gt;의 작은 크기이며,&lt;br /&gt;new 영역에 &lt;code&gt;할당 비용은 매우 저렴&lt;/code&gt;하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 영역에서는 객체를 위한 &lt;code&gt;공간을 확보하려 할 때마다 증가&lt;/code&gt;하는 &lt;code&gt;할당 포인터&lt;/code&gt;가 있으며&lt;br /&gt;이 &lt;code&gt;할당 포인터&lt;/code&gt;가 &lt;code&gt;new 영역 마지막에 도달&lt;/code&gt;하면 &lt;code&gt;스캐벤져&lt;/code&gt;가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스캐벤져는 &lt;a href=&quot;https://en.wikipedia.org/wiki/Cheney%27s_algorithm&quot;&gt;Cheney 알고리즘&lt;/a&gt;을 사용해 구현되었는데,&lt;br /&gt;해당 알고리즘의 특징으로는 '매우 자주 발생'하며, '수행속도가 빠르고' '병렬 헬퍼 스레드 사용한다' 정도가 있다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스캐벤져(Minor GC) 과정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 영역은 &lt;code&gt;To 영역&lt;/code&gt;과 &lt;code&gt;From 영역&lt;/code&gt;으로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://speakerdeck.com/deepu105/v8-minor-gc&quot;&gt;스캐벤져 발생 과정 슬라이드 참고&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음부터 스캐벤져(minor GC)를 MG로 표현하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스캐벤져 발생 과정 요약&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;From 영역에 객체 obj01~06이 존재하고&amp;nbsp;새 객체 obj07를 생성해야 할 때,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;v8이 &lt;code&gt;to 영역&lt;/code&gt;에서 필요한 메모리를 할당할 수 없는 상황이라면&amp;nbsp;&lt;b&gt;MG가 발생&lt;/b&gt;한다.&lt;br /&gt;- &lt;code&gt;MG&lt;/code&gt;에 의해 모든 객체들이 &lt;code&gt;from-&amp;gt;to 영역&lt;/code&gt;으로 이동한다.&lt;br /&gt;- &lt;code&gt;MG&lt;/code&gt;가 &lt;code&gt;스택포인터&lt;/code&gt;부터 &lt;code&gt;from 영역&lt;/code&gt;까지 순회하며&lt;br /&gt;- 메모리를 사용한 객체 찾고, &lt;code&gt;이 객체들만 to 영역으로 이동한다.&lt;/code&gt;&lt;br /&gt;- 마지막 객체까지 찾으면, &lt;code&gt;to 영역은 자동 압축되며&lt;/code&gt;&amp;nbsp;조각화로&amp;nbsp;&lt;code&gt;공간을 더 확보하게 된다.&lt;/code&gt;&lt;br /&gt;- &lt;code&gt;그리고 from 영역에 남은 객체&lt;/code&gt;는 &lt;code&gt;가비지컬렉션에 의해&lt;/code&gt;&amp;nbsp;제거된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 새 객체 &lt;code&gt;b07&lt;/code&gt;이 &lt;code&gt;영역&lt;/code&gt;에 할당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;일정 시간 후에, 다시 to 영역이 가득 차서 새 객체 &lt;code&gt;obj10 을 할당할 수 없다면&lt;/code&gt;&lt;code&gt;MG가 발생한다.&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;- 이번에는 &lt;code&gt;두번째 MG 발생이다.&lt;/code&gt;&lt;br /&gt;- 다음의 두번째 MG 까지 살아남은 객체들은&amp;nbsp;&lt;code&gt;Old 영역&lt;/code&gt; 으로 이동하게 된다.&lt;br /&gt;- 첫번째 MG 이후 생성되어&lt;code&gt;처음으로 생존한 객체&lt;/code&gt;들은 동일하게 &lt;code&gt;to 영역&lt;/code&gt;으로 이동하게 되고,&lt;br /&gt;- 남아있는 &lt;code&gt;From 영역 객체&lt;/code&gt;는 &lt;code&gt;가비지컬렉션에 의해 제거된다.&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;MG&lt;/code&gt;&amp;nbsp;는&amp;nbsp;&lt;code&gt;stop-the-world 프로세스&lt;/code&gt; 를 취하지만, 굉장히 빠르고 효율적이라 무시가 가능하다고 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Major GC&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Old 영역을 작고 깨끗하게 유지하는 가비지컬렉션이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;v8에서 &lt;code&gt;Old 영역 메모리가 충분하지 않다 판단&lt;/code&gt;할 때 발생하며&lt;br /&gt;Old 영역은 &lt;code&gt;동적으로 계산된 크기에 기반하며&lt;/code&gt;, Minor GC 주기에서 채워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;major GC는 메모리 오버헤드가 있기 때문에 &lt;b&gt;mark-sweep-compact 알고리즘&lt;/b&gt;을 사용하는데,&lt;br /&gt;TRI-color(흰색, 회색, 검은색) 마킹 시스템을 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;해당 시스템은 &lt;code&gt;세 단계 프로세스&lt;/code&gt;를 거치며, 세번째 단계는 조각화 휴리스틱에 따라 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1641920710746&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;메모리 오버헤드란?

어떤 처리를 하기 위해 들어가는 간접적인 처리 시간, 메모리를 의미한다.

예를 들면, A를 위해 단순 실행이 10초일 때, 안전성을 위한 옵션을 추가해 15초가 걸렸다면? 
오버헤드는 5초이다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Mark-Sweep-Compact 알고리즘&lt;/b&gt;의 3단계 프로세스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;마킹&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;가비지 컬렉터&lt;/code&gt;가 어떤 객체가 &lt;code&gt;사용중인지 식별&lt;/code&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용중인 객체는 &lt;code&gt;활성 상태&lt;/code&gt;로 표시하며,&lt;br /&gt;&lt;code&gt;힙 메모리&lt;/code&gt;를 &lt;code&gt;방향그래프&lt;/code&gt;로 간주해 &lt;code&gt;DFS를 수행&lt;/code&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 해당 객체로부터 접근 가능한 모든 객체들을 방문하게 되며, 이것들에 '마킹'을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스위핑&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;가비지 컬렉터&lt;/code&gt;가 &lt;code&gt;힙 메모리를 순회&lt;/code&gt;하면서, &lt;code&gt;활성상태가 아닌 객체의 메모리 주소 기록&lt;/code&gt;한다.&lt;br /&gt;이 공간은 &lt;code&gt;free-list&lt;/code&gt;에 &lt;code&gt;사용 가능한 목록&lt;/code&gt;으로 표시되며, 이 목록에 있는 공간에는 다른 객체의 저장이 가능하다.&lt;br /&gt;(즉 마킹되지 않은 모든 객체들이 메모리에서 삭제된다고 이해할 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;압축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위핑 후 필요하다면, 모든 활성 객체들이 함께 이동한다.&lt;br /&gt;&lt;code&gt;조각화를 줄이고&lt;/code&gt;, 새 객체들에 대한 &lt;code&gt;메모리 할당 성능 증가&lt;/code&gt; 효과가 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Major GC는 수행 중에 앱 실행을 멈추므로, &lt;code&gt;stop the world GC&lt;/code&gt; 라고도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Major GC 과정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 Minor GC 주기를 거치고 Old 영역이 거의 다 차게 되면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8이 Major GC를 발생시킬 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Major GC는 스택 포인터에서 시작한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재귀적으로 객체 그래프를 순회하며, Old 영역 내 메모리를 사용한 객체와 남아있는 객체를 가비지로 표시한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동시 마킹이 완료되거나 메모리 제한에 도달하면 Major GC가 메인 스레드를 사용하여 마킹의 마지막 단계를 수행한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때 일시 정지 시간이 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Major GC는 동시 스위프 스레드를 사용해 모든 참조 없는 객체들의 메모리를 사용 가능한 상태로 표시한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;또한 조각화를 피하기 위해 관련 메모리 블록을 동일한 페이지로 이동시키는 병렬 압축 작업도 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, 스택 포인터들은 이 세 단계를 통해 갱신된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8 엔진의 메모리와 관리 방법에 대해 학습하면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택 힙 등의 메모리와 가비지 컬렉션이 동작하는 원리에 대해 알 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 예전 C언어에서 malloc으로 할당한 변수들을 해제해줬던 작업들과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 클로저를 공부하면서&amp;nbsp;'클로저의 남용이 메모리의 문제와 연결된다.'는 이유를 더 잘 이해할 수 있었던 점도 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이, 이전의 파편같은 지식들이 퍼즐같이 맞춰지는 과정이 새로운 지식에 대한 학습의 즐거움인 것 같다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고 출처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ui.toast.com/weekly-pick/ko_20200228&quot;&gt;https://ui.toast.com/weekly-pick/ko_20200228&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://ko.javascript.info/garbage-collection#ref-417&quot;&gt;https://ko.javascript.info/garbage-collection#ref-417&lt;/a&gt;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/116</guid>
      <comments>https://gobae.tistory.com/116#entry116comment</comments>
      <pubDate>Wed, 12 Jan 2022 02:00:30 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] 일반 객체 다루기</title>
      <link>https://gobae.tistory.com/115</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이전 글&lt;/h3&gt;
&lt;figure id=&quot;og_1641886019925&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[객체] 자바스크립트에서 객체를 생성하는 다양한 방법&quot; data-og-description=&quot;객체 객체란, key:value 쌍을 저장하는 자료구조로, 하나의 변수에 여러 속성들을 저장할 수 있도록 돕는다. 사실, 자바스크립트의 거의 모든 것들은 객체라고 할 수 있다. 배열, 함수, 객체, 날짜, &quot; data-og-host=&quot;gobae.tistory.com&quot; data-og-source-url=&quot;https://gobae.tistory.com/112&quot; data-og-url=&quot;https://gobae.tistory.com/112&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eG2ht/hyM3G7lIHr/gWG9KtJlX2AkRbSHy7quQ1/img.png?width=800&amp;amp;height=191&amp;amp;face=0_0_800_191,https://scrap.kakaocdn.net/dn/bTnmTk/hyM3PDb9lW/bI1Cj1Kt8o7up3qkW5s0S1/img.png?width=800&amp;amp;height=191&amp;amp;face=0_0_800_191,https://scrap.kakaocdn.net/dn/buCzh1/hyM3N6sLbF/WlMcY9AI2C7keQG3weiyKk/img.png?width=1184&amp;amp;height=736&amp;amp;face=0_0_1184_736&quot;&gt;&lt;a href=&quot;https://gobae.tistory.com/112&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gobae.tistory.com/112&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eG2ht/hyM3G7lIHr/gWG9KtJlX2AkRbSHy7quQ1/img.png?width=800&amp;amp;height=191&amp;amp;face=0_0_800_191,https://scrap.kakaocdn.net/dn/bTnmTk/hyM3PDb9lW/bI1Cj1Kt8o7up3qkW5s0S1/img.png?width=800&amp;amp;height=191&amp;amp;face=0_0_800_191,https://scrap.kakaocdn.net/dn/buCzh1/hyM3N6sLbF/WlMcY9AI2C7keQG3weiyKk/img.png?width=1184&amp;amp;height=736&amp;amp;face=0_0_1184_736');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[객체] 자바스크립트에서 객체를 생성하는 다양한 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;객체 객체란, key:value 쌍을 저장하는 자료구조로, 하나의 변수에 여러 속성들을 저장할 수 있도록 돕는다. 사실, 자바스크립트의 거의 모든 것들은 객체라고 할 수 있다. 배열, 함수, 객체, 날짜,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gobae.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 자체를 먼저 다뤘어야 했는데, 어쩌다보니 순서가 좀 뒤바뀌게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 자바스크립트에서의 &lt;b&gt;일반 객체(순수 객체)&lt;/b&gt;에 대해 정리한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 객체는 중괄호 {} 를 이용해 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 원소들은 각각 key:value 쌍으로 구성된다.&lt;br /&gt;key는 문자형이어야만 하고 value로는 모든 자료형이 허용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 원소는 프로퍼티(property)라고 하며,&lt;br /&gt;key는 이름으로 value를 찾기 위한 라벨이라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 생성하는 대표적인 두가지 방법을 소개하면,&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;    const obj = new Object(); // 객체 생성자
    const obj = {}; // 객체 리터럴&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;new 키워드를 이용하는 생성자 방법&lt;/code&gt;과 &lt;code&gt;중괄호를 이용해 선언하는 리터럴 방법&lt;/code&gt;이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 프로퍼티의 생성, 불러오기, 추가, 삭제&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;생성하기&lt;/h4&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;    const player = {
      name : 'Tom',
      age : 12,
      level : 7,
      'soul food' : 'banana',
      'created_at' : '20220111',
    };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;player 객체가 존재한다고 할 때, 아래와 같이 해석한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 player는 객체 리터럴로 선언되었다.&lt;br /&gt;5개의 프로퍼티가 있고, 키 값으로 name, age, level, soul food, created_at이 있으며 그에 해당하는 값들이 존재한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;불러오기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 불러오는 다양한 방법이 있다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;    console.log(player.name); // O
    console.log(player['name']); // O

    console.log(player.created_at); // O
    console.log(player['created_at']); // O

    console.log(player['soul food']); // O

    console.log(player[name]); // X
    console.log(player.soul food) // X&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 방법과 가능하지 않은 방법을 표시해뒀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;점 표기법(dot notation)&lt;/b&gt;을 이용하거나, 대괄호 표기법 []을 이용해 값을 조회할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점 표기법을 이용할 때는 key가 &lt;b&gt;유효한 변수 식별자&lt;/b&gt;여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ &lt;b&gt;유효한 변수 식별자&lt;/b&gt;란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공백이 없어야 하며, 숫자로 시작하지 않아야 하고 $ _ 를 제외한 특수 문자를 포함할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;soul food&lt;/b&gt;와 같이 여러 단어가 조합된 키는 &lt;b&gt;대괄호 표기법 []&lt;/b&gt;을 이용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대괄호 표기법 [] 안에는 점 표기법과 동일하게 키 이름을 적어주는데, &lt;b&gt;반드시 문자열&lt;/b&gt;로 적어줘야 한다.&lt;br /&gt;문자열이 아닌&amp;nbsp;&lt;b&gt;변수를 넣어주면, 런타임 시에 변수가 평가되어 변수에 할당된 값을 키 이름으로 취급&lt;/b&gt;하여 조회하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 코드를 통하면 쉽게 이해가 될 듯 하다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;    const obj = {
      name : 'Tom',
      Tom : 'its not name',
    };

    const name = 'Tom';

    console.log(obj.name) // 'Tom'
    console.log(obj['name']) // 'Tom'
    console.log(obj[name]) // 'its not name' (런타임 시 name 변수값을 이용해서 obj['Tom'] 으로 평가된다.)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;추가하기&lt;/h4&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;    const obj = {
      name : 'Tom'
    };

    obj.age = 17;
    obj['food'] = 'banana'; 

    obj[hobby] = 'tennis'; // X&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체에 프로퍼티를 추가하는 것도 마찬가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;엄밀히 말하면, 기존에 해당 키 이름이 존재하면 값의 변경이 되며, 키 이름이 존재하지 않으면 생성(추가)가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막에 적힌 obj[hobby]는 오류가 발생하는데, hobby 변수가 존재하지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키 이름이 hobby인 프로퍼티를 생성하고 싶다면, obj['hobby'] 로 작성해야 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;삭제하기&lt;/h4&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const obj = {
      name : 'Tom',
      age : 17
    };

    delete obj.name;
    console.log(obj); // {age : 17}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;delete 키워드를 이용하면, 객체에서 프로퍼티를 삭제할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;대괄호 표기법 응용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대괄호 표기법 []은 생각보다 더 강력하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임 시 사용자로부터 입력받은 값 등을 변수를 이용하면 변수의 값에 해당하는 키 이름을 설정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;    const name = 'Tom';
    const userObj = {};
    userObj[name] = 1;

    console.log(userObj) // {Tom : 1}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 name 값이 런타임 시 평가되어 userObj[name]이 userObj['Tom'] 으로 평가되어 key:value로 'Tom' : 1 을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 변수가 평가된 값을 이용한 프로퍼티 생성이 이루어짐을 응용하면,&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const nameList = ['Tom','Juliet','Jack','Whale'];
    const nameObj = {};

    nameList.forEach(name =&amp;gt; nameObj[name] = 1);
    console.log(nameObj); // {Tom: 1, Juliet: 1, Jack: 1, Whale: 1}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 각 리스트의 원소들을 key 이름으로 가지도록 할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점 표기법을 이용해서 이 예시와 같은 객체를 만드는 작업은 아래와 같은데&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;    const nameObj = {};
    name.Tom = 1;
    name.Juliet = 1;
    name.Jack = 1;
    name.Whale = 1;
    console.log(nameObj); // {Tom: 1, Juliet: 1, Jack: 1, Whale: 1}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 원소가 4개라서 망정이지, 10개만 넘어가도 답이 없음은 분명하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;계산된 프로퍼티&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 리터럴 {} 로 객체를 생성할 때, 프로퍼티의 키 이름을 대괄호로 둘러 쌀 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이를 계산된 프로퍼티(Computed Property) 라고 한다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;    const hobby = prompt('좋아하는 운동은?');
    const favorite = {
      [hobby] : true
    };
    console.log(favorite); // { tennis : true }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대충 사용자가 브라우저에서 prompt에 좋아하는 운동으로 tennis을 입력했다고 하면, 결과는 위와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 위 코드는 아래 코드와 동일하게 동작한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const hobby = prompt('좋아하는 운동은?');
    const favorite = {};
    favorite[hobby] = true;

    console.log(favorite); // { tennis : true }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 변수 값을 이용해서 프로퍼티를 생성할 때,&lt;br /&gt;기존 변수값에 추가하고 싶은 문자열 등을 추가해서 키 이름으로 생성할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;    const name = 'Tom';
    const comic = {
        [name + 'AndJerry'] : 'NO!',
    };
    console.log(comic); // { TomAndJerry : 'No!' }


    // 혹은 이렇게도 가능

    const nameList = ['Tom', 'Jane', 'Kane'];
    const team = {};

    nameList.forEach(name =&amp;gt; {
      team[name + 'hobby'] = 'soccer';
      team[name + 'year'] = 22;
    });
    console.log(team); // {Tomhobby: 'soccer', Tomyear: 22, Janehobby: 'soccer', Janeyear: 22, Kanehobby: 'soccer', &amp;hellip;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 [변수 + '추가하고자 하는 문자열'] 의 조합이 가능하다.&lt;br /&gt;런타임 시 변수가 문자열로 평가되므로, ['문자열' + '문자열'] = ['이어 붙인 문자열'] 이 되는 원리이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;키, 값이 중복될 시 단축이 가능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 변수 값들을 프로퍼티로 삼아 객체를 새로 생성해야 하는 상황이 있다.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;    const name = 'Tom';
    const age = 17;

    const user = {
        name:name,
          age:age
    };

    // 이와 같이 key:value의 값이 겹친다면 단축이 가능하다.

    const shortUser = {
      name,
      age
    };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name:name 과 단축된 name은 같은 역할을 한다.&lt;br /&gt;name:name 에서 콜론(:)의 왼쪽 name은 키 이름에 해당하며 우측 name은 변수값에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;즉 현재 변수 name 값이 'Tom' 이므로 name:'Tom' 이 생성되며,&lt;br /&gt;단축된 name으로 프로퍼티를 선언해도 위와 동일하게 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 단축 프로퍼티를 사용하면?&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;    const obj = {
      name:name,
      age:age,
      hobby:hobby
    };

    // 위의 코드를

    const obj = {name, age, hobby};
    // 이렇게 획기적으로 줄일 수 있다.  &lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;키 이름으로 숫자 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키 이름으로 숫자를 사용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 위에서 설명했던 방법과 조금 다를 수 있겠다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const obj = {
      0: 'Tom',
      1: 'Jane',
      2: 'Kane',
    };

    console.log(obj['0']); // O
    console.log(obj[0]); // O
    console.log(obj.0); // X&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj['0'] 의 경우, '0'의 키 이름을 조회하므로 정상 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj[0]의 경우, 숫자 0은 변수가 될 수 없으므로 문자열로 자동 형변환이 된다.&lt;br /&gt;그러므로 obj[0] = obj['0'] 취급되어 위와 동일하게 정상 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj.0의 경우, 앞서 언급한 &lt;b&gt;유효한 변수 식별자에 어긋난다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dot notation(.) 바로 뒤에 숫자로 시작할 수 없다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체에 특정 프로퍼티가 존재하는지 확인하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체에 특정한 프로퍼티 키 이름이 존재하는지 확인해야 하는 상황이 종종 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자는 그럴 때 마다 프로퍼티가 존재하지 않는 것을 검출하기 위해서&lt;br /&gt;&lt;code&gt;if(!obj.name) blahblah&lt;/code&gt; 와 같은 방식을 사용했으나&lt;br /&gt;&lt;b&gt;in&lt;/b&gt; 연산자로 존재 여부 확인이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;    const obj = {
      name : 'Tom',
      age : 17
    };

    console.log(!obj.name) // false;
    console.log(!obj.hobby) // true;

    console.log('name' in obj); // true
    console.log('hobby' in obj); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 &lt;code&gt;!obj.hobby === !undefined === true&lt;/code&gt; 이기 때문에, 현재는 기능적으로 문제는 없는 것 처럼 보이지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const obj = {
      name : 'Tom',
      age : 17,
      hobby : undefined
    };

    console.log(!obj.hobby); // true
    console.log('hobby' in obj) // true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 hobby 키를 가진 프로퍼티가 존재할 수도 있기 때문에, 완벽한 검증이 아닐 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 여기서도 &lt;code&gt;문자열 in 객체&lt;/code&gt; 형식으로 &lt;code&gt;'hobby' in obj&lt;/code&gt; 형태를 이용해야 한다.&lt;br /&gt;&lt;code&gt;hobby in obj&lt;/code&gt;를 타이핑해서 엉뚱한 hobby 변수 값을 조사해서 에러를 일으키지 말자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 순회하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체도 배열처럼 순회할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for in 문법을 이용하면 된다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;    const obj = {
      name : 'Tom',
      age : 17,
      hobby : 'tennis'
    };

    for(let key in obj){
      console.log(key, obj[key]);
    };

    // name Tom
    // age 17
    // hobby tennis&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 in 키워드로 키를 조회하며, 객체[키]로 값을 함께 조회할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자는 Object의 메서드를 이용한 조회를 더 많이 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object.keys - 모든 키를 포함한 1차원 배열 반환&lt;br /&gt;Objecy.values - 모든 값을 포함한 1차원 배열 반환&lt;br /&gt;Object.entries - [키, 값] 쌍의 2차원 배열 반환&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;    const obj = {
      name : 'Tom',
      age : 17,
      hobby : 'tennis'
    };

    console.log(Object.keys(obj)); // ['name', 'age', 'hobby']
    console.log(Object.values(obj)); // ['Tom', 17, 'tennis']

    console.log(Object.entries(obj));
    /*
    [
        ['name', 'Tom'],
        ['age', 17],
        ['hobby', 'tennis']
    ]
    */&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 사용시 유의점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체와 같은 참조값을 사용할 시 유의할 점이 있다.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;    const name = 'Tom';
    const obj = {
      name:'Tom'
    };

    name = 'Jane' // X

    obj = {
      name : 'Jane
    }; // X

    obj.name = 'Jane' // O&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const 키워드로 선언한 변수는 재할당이 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 새로운 값으로 바꿀 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 name, obj 변수에 재할당이 불가능하지만, obj의 프로퍼티에 대한 변경은 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj 변수에는 '객체 자체'가 할당된 것이 아니라, '객체의 주소값' 이 할당되어 있어서&lt;br /&gt;const 키워드는 '주소값'을 기억하고 있기 때문에 '주소값'을 변하게 하는 재할당이 불가능하지만, 객체의 프로퍼티를 변경이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, obj 객체의 껍질 자체는 불변이지만 obj 객체의 내부 프로퍼티들은 불변이 아니라고 이해하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이와 같이 obj 변수가 '주소값' 임을 이해하게 되면, 한가지 의문점을 더 해결할 수 있는데&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;    const user = {
        name:'Tom',
          phone:01000000000
    };

    function takeANumber(customer){
      customer.number = 0;
      console.log(customer);
    }

    takeANumber(user); // {name: 'Tom', phone: 134217728, number: 0}

    console.log(user); // {name: 'Tom', phone: 134217728, number: 0}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 함수 매개변수로,&lt;br /&gt;user를 넣어줬을 때 '복사' 되었다고 착각할 수 있는데&lt;br /&gt;매개변수로 넣어준 user 객체에 name 프로퍼티가 생성되는 상황이 왜 발생하는지 이해하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;takeANumber에서 customer 매개변수가 user의 주소 값을 가지고 있다.&lt;br /&gt;그래서 주소값에 있는 객체에 number:0의 프로퍼티를 생성한 것일 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 기존 객체를 변경하지 않으면서 takeANumber 함수를 이용하고 싶다면,&lt;br /&gt;다양한 방법이 있겠지만 가장 간단한 방법으로는&lt;b&gt; spread operator&lt;/b&gt;를 사용하길 권장한다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;    const user = {
        name:'Tom',
          phone:01000000000
    };

    function takeANumber(customer){
      customer.number = 0;
      console.log(customer);
    }

    takeANumber({...user}); // {name: 'Tom', phone: 134217728, number: 0}

    console.log(user); // {name: 'Tom', phone: 134217728}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax&quot;&gt;spread operator&lt;/a&gt;는 '전개구문' 이라 부르며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름 그대로 해당 객체 / 배열 등을 풀어준다고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이 방법이 완전한 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;'얕은 복사'를 수행하기 때문에, 객체 혹은 배열 내에 값으로 또 다른 배열이나 객체를 포함한다면 다른 방법을 사용해야 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;출처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/object&quot;&gt;https://ko.javascript.info/object&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://doitnow-man.tistory.com/130&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://doitnow-man.tistory.com/130&lt;/a&gt;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 객체</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/115</guid>
      <comments>https://gobae.tistory.com/115#entry115comment</comments>
      <pubDate>Tue, 11 Jan 2022 16:36:26 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] 고차함수와 배열 내장 메서드</title>
      <link>https://gobae.tistory.com/114</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;고차함수란&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고차함수는, &lt;code&gt;함수를 인자로 전달&lt;/code&gt;받거나, &lt;code&gt;함수를 결과로 반환&lt;/code&gt; 하는 함수이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서 함수를 결과를 반환하게 되면, 그것은 클로저로 동작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고차함수는, 어떻게 할지의 '절차' 보다는 &lt;b&gt;'무엇을 할지'&lt;/b&gt;에 초점을 맞춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서는 map, filter, forEach, reduce 등 무엇을 할지에 대해 초점을 맞춘 메서드들이 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드들에서 break, continue를 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; break, continue는 '절차'를 스킵하거나 /강제 종료하기 위한 명령어이기 때문.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 고차함수 메서드들은 &lt;b&gt;원하는 결과를 얻기 위한 '과정'을 중요시&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 절차를 중요시하는 대표적인 예로는 반복문이 있는데&lt;br /&gt;&lt;b&gt;반복문&lt;/b&gt;은 '무엇을 할지'가 아니라 &lt;b&gt;'어떻게 할지'&lt;/b&gt;에 초점을 맞춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 초기값, 멈추는 기준, 증가시킬 iterator의 총 3가지 값을 요구하며&lt;br /&gt;때때로 절차를 무시할 수 있는 break, continue의 도움을 받는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼, 이제 고차함수를 직접 구현해보고 배열에 내장된 고차함수들을 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;고차함수 구현하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 일급객체인 함수는 단순한 &lt;code&gt;&quot;값&quot;&lt;/code&gt; 취급된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그러므로 매개변수로 전달하거나 결과값으로 반환하는 등 '값'으로 할 수 있는 모든 역할에 대입할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예시를 보면&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function validateUser(userName) {
    if (findUser(userName)) return true;
    else return false;
}
function validateManager(managerName) {
    if (findManager(managerName)) return true;
    else return false;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 두 함수는, 각각 특정 로직을 통해 유저인지 / 매니저인지를 각각 검증한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그러나 findUser, findManager 함수만 다를 뿐 validate 함수의 내부 로직은 매우 유사하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이럴 때 고차함수의 성질을 이용한다면 함수 자체를 매개변수로 넘겨서 더 간단하게 표현이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;    function validateWithFindFunc(name, findFunc) {
        if (findFunc(name)) return true;
        else return false;
    }

    validateWithFindFunc('userName', findUser);
    validateWithFindFunc('managerName', findManager);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 함수를 매개변수로 넘겨서, validateWithFindFunc 함수 내부에서 사용함으로서&lt;br /&gt;로직이 유사한 두 함수를 하나로 통합시킬 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도, 절차지향적으로 표현한 코드를 고차함수를 이용해서 더 간단하게 표현할 수도 있는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 절차지향적인 for를 이용하면&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;    const arr = ['Tom', 'Jane', 'Bob'];
    const arr2 = [];

    for(let i=0; i&amp;lt;arr.length; i++){
      arr2.push(arr[i] + ' is my friend.');
    };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 코드를 고차함수를 이용하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const arr = ['Tom', 'Jane', 'Bob'];
    const arr2 = arr.map(elem =&amp;gt; elem + ' is my friend.');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 간단하게 표현이 가능하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배열 내장 메서드에서의 고차함수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고차함수들은 배열에 내재된 메소드들에서도 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;map, reduce, filter는 워낙 유명하니 그 외 다른 메소드들을 하나씩 살펴보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Array.prototype.forEach&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;    const print = (n) =&amp;gt; console.log(n);

    [print, print, print, print].forEach((fn, i) =&amp;gt; fn(i));
    // 0 1 2 3 이 출력된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열을 순회하는 for문과 유사하게 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, forEach 문에서는 break, continue를 사용할 수 없으며&lt;br /&gt;반드시 모든 처음부터 끝까지 모든 원소를 순회한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 for문에 비해 성능이 좋지 않지만, 가독성이 좋으므로 사용할 수 있는 상황에서는 사용하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Array.prototype.find&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;    const player = [
    {name : 'Tom', age : '12'},
    {name : 'Bob', age : '23'},
    {name : 'Jane', age : '12'},
    ];

    const findBob = (player) =&amp;gt; player.name === 'Bob';
    player.find(pl=&amp;gt;pl.name === 'Bob');
    player.find(findBob);

    // {name: 'Bob', age: '23'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;find는 배열에서 콜백 함수를 true로 리턴하는 첫 결과에 해당하는 인수를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Array.prototype.some&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;    const player = [
    {name : 'Tom', age : '12'},
    {name : 'Bob', age : '23'},
    {name : 'Jane', age : '12'},
    ];

    const findBob = (player) =&amp;gt; player.name === 'Bob';
    player.some(findBob);
    // true

    player.some(pl =&amp;gt; pl.name === 'Crong');
    // false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;some 메서드는 배열에서 콜백함수를 통과하는 요소가 하나라도 있다면, true를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Array.every&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;    const player = [
    {name : 'Tom', age : '12'},
    {name : 'Bob', age : '23'},
    {name : 'Jane', age : '12'},
    ];

    const findBob = (player) =&amp;gt; player.name === 'Bob';
    player.every(findBob);
    // false

    const isObject = elem =&amp;gt; typeof elem === 'object';
    player.every(isObject);
    // true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;every 메서드는 배열의 요소가 콜백함수를 모두 통과해야(모두 true여야) true를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;findBob의 경우는 Bob만 통과되기 때문에 false를 반환하고, isObject의 경우엔 모든 요소가 객체에 해당하므로 true를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Array.prototype.flatMap&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름에서 알 수 있듯이, map 메서드와 연관되어 있다.&lt;br /&gt;flatMap은 map 함수 결과에 flat() 함수를 적용한 결과를 리턴하는데,&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;    const fruits = ['banana', 'kiwi', 'apple', 'melon'];
    fruits.map((fruit, idx)=&amp;gt;[fruit, idx]);
    /*
    [['banana', 0],
     ['kiwi', 1],
     ['apple', 2],
     ['melon', 3]
    ]
    */

    fruits.flatMap((fruit, idx)=&amp;gt;[fruit, idx]);
    // ['banana', 0, 'kiwi', 1, 'apple', 2, 'melon', 3]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;map 함수의 결과에서 1차원을 줄인다고 생각하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 fruits.map과 fruits.flatMap의 결과로 같은 배열을 반환받고 싶다면,&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;    fruits.map((fruit, idx)=&amp;gt;[fruit, idx]);
    fruits.flatMap((fruit, idx)=&amp;gt;[[fruit, idx]]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 배열을 하나 더 감싸주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Array.prototype.reduceRight&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수도 이름에서 알 수 있뜻이, reduce와 관련되어 있는데,&lt;br /&gt;기존의 reduce가 배열 첫번째 원소부터 순회하며 누산을 진행한다면&lt;br /&gt;reduceRight는 배열 마지막 원소부터 첫번째 원소로 순회하며 누산을 진행한다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;    const order = [1,2,3,4,5,6];
    order.reduce((acc,cur)=&amp;gt; acc+cur, ''); // '123456'
    order.reduceRight((acc,cur)=&amp;gt;acc+cur, ''); // '654321'&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건문과 반복문은 고차함수에 비해서 로직 흐름에 대한 이해가 어렵고 가독성을 해치며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수의 값을 직접 변경하는 행위는 오류의 직접적인 원인이 되기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 이와 같이 순수함수와 보조함수 조합을 통해, 고차함수를 구현하여 코드를 작성하게 되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절차지향에서 남발되는 조건문 / 반복문을 제거할 수 있고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 복잡성을 해결하고 변수의 직접적인 조작을 피함으로서 부수효과를 억제할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 데이터 직접적으로 조작하지 않는 것이, 데이터를 생성하지 않는다는 의미가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 데이터에 변형을 주지 않기 위해, 새로운 데이터를 생성하므로 오히려 메모리에서의 문제가 발생할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 실제로 map, filter, reduce 등의 결과는 기존 배열에 적용되는 것이 아니라 새로운 배열을 리턴하는 형식이니 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 고차함수</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/114</guid>
      <comments>https://gobae.tistory.com/114#entry114comment</comments>
      <pubDate>Thu, 6 Jan 2022 23:20:22 +0900</pubDate>
    </item>
    <item>
      <title>[함수형] 클로저 Closure에 대해 알아보자.</title>
      <link>https://gobae.tistory.com/113</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;클로저&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저란, 자신이 선언된 렉시컬 환경을 기억하고 참조하는 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MDN에서는,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저는 독립적인 (자유) 변수를 가리키는 함수이다.&lt;br /&gt;클로저 안에 정의된 함수는 만들어진 환경을 &amp;lsquo;기억한다&amp;rsquo;. 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저를 이해하기 위해선 우선 스코프를 알아야 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스코프&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스코프에 대해서는 간단한 예시만을 다룰 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 모른다면,&lt;/p&gt;
&lt;figure id=&quot;og_1641471158153&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자바스크립트] 렉시컬 스코프&quot; data-og-description=&quot;스코프와 &amp;nbsp;스코프 체인 스코프란, 식별자의 유효 범위를 말한다. 스코프 체인이란, 이 식별자의 유효 범위를 뜻하는 스코프가 계층적으로 연결된 것을 의미한다. 함수에서 스코프를 결정하는 &quot; data-og-host=&quot;gobae.tistory.com&quot; data-og-source-url=&quot;https://gobae.tistory.com/28&quot; data-og-url=&quot;https://gobae.tistory.com/28&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/NckMQ/hyMZQCEmOQ/GvwL3rROuUPqxenFFjGipk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/krfxz/hyMZSNZl49/4rw2kop3xZBnpBlMPmJip1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://gobae.tistory.com/28&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gobae.tistory.com/28&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/NckMQ/hyMZQCEmOQ/GvwL3rROuUPqxenFFjGipk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/krfxz/hyMZSNZl49/4rw2kop3xZBnpBlMPmJip1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[자바스크립트] 렉시컬 스코프&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스코프와 &amp;nbsp;스코프 체인 스코프란, 식별자의 유효 범위를 말한다. 스코프 체인이란, 이 식별자의 유효 범위를 뜻하는 스코프가 계층적으로 연결된 것을 의미한다. 함수에서 스코프를 결정하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gobae.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 정리한 스코프에 대한 글을 먼저 보는게 낫겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckLnsr/btrp00m5Bjn/4EXVcsXWL8kb2DVkuVP1GK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckLnsr/btrp00m5Bjn/4EXVcsXWL8kb2DVkuVP1GK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckLnsr/btrp00m5Bjn/4EXVcsXWL8kb2DVkuVP1GK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckLnsr%2Fbtrp00m5Bjn%2F4EXVcsXWL8kb2DVkuVP1GK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;468&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시에서 함수 abc는 내부에서 변수 a를 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;함수 스코프와 같은 {} 코드 블럭 안에선 변수 a에 대해 참조를 할 때,&lt;br /&gt;함수 스코프를 먼저 탐색하기 때문에, 매개변수에 해당하는 a를 반환하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 함수 스코프에 지역 변수 a가 없다면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 선언된 스코프를 탐색하며, 발견이 될 때 까지 전역스코프까지 올라가며, 전역에 선언된 a를 발견하면 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 스코프에도 변수 a가 선언되어 있지 않다면, 레퍼런스 에러 'a is not defined' 에러가 출력 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 가지 예를 추가로 이해해보면,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JUfTS/btrpWyS0HTI/YzUDGgZNHIgL6zsvl40fRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JUfTS/btrpWyS0HTI/YzUDGgZNHIgL6zsvl40fRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JUfTS/btrpWyS0HTI/YzUDGgZNHIgL6zsvl40fRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJUfTS%2FbtrpWyS0HTI%2FYzUDGgZNHIgL6zsvl40fRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;500&quot; data-origin-width=&quot;1414&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측 코드의 스코프를 그림으로 나타내면 우측과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 k에서 b를 찾아서 출력해야 하는데,&amp;nbsp;k 함수 스코프에서 b가 존재하지 않으므로 k 함수가 선언된 if 스코프로 올라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if 스코프에 있는 b=3 값을 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8OzC8/btrpZ80BaFZ/sGKo4NlVCty0bgNtDmYqr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8OzC8/btrpZ80BaFZ/sGKo4NlVCty0bgNtDmYqr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8OzC8/btrpZ80BaFZ/sGKo4NlVCty0bgNtDmYqr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8OzC8%2FbtrpZ80BaFZ%2FsGKo4NlVCty0bgNtDmYqr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;669&quot; height=&quot;480&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시에서는, k 함수 스코프에서 b가 존재하지 않으므로 k 함수가 선언된 if 스코프로 올라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;if 스코프에 b가 존재하지 않으므로, if 스코프가 선언된 전역 스코프로 올라간다.&lt;br /&gt;전역 스코프에 b가 선언되어 있으므로, b=2 값을 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄밀히 말하면,&amp;nbsp;const, let 키워드와 var 키워드의 스코프는 다르지만&lt;br /&gt;최근 문법에서는 변수 선언 시 var는 지양되는 편이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서, const,let의 블록레벨스코프를 기준으로 생각해보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클로저의 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 이미 실행이 끝난 스코프를 참조한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행이 끝난 함수의 스코프는, 더이상 참조가 되지 않는다면 가비지컬렉터에 의해 정리된다.&lt;br /&gt;그러나 클로저의 개념에서는 내부 함수가 해당 스코프를 기억하므로, 실행이 끝난 스코프도 계속 참조되어 사라지지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 1의 특징을 활용한다면 은닉화가 가능하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Private 변수의 예시를 생각하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저를 활용한다면 외부에서 상위 환경에 선언한 변수에 직접 접근할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이 변수에 대한 제어를 하고 싶다면, 내부 함수에 get이나 set 등의 인터페이스를 추가해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 재사용이 가능하다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저를 이용하면 같은 함수 정의를 공유하면서도, 렉시컬 환경(상위 스코프)가 다른 함수를 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저를 생성해주는 함수를 실행시키고 그 반환 값을 각각의 변수에 할당하면 끝이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 기능을 하는 통장을 발행해주는 은행을 예로 들면&lt;br /&gt;계좌1, 계좌2, 계좌3 선언할 수 있고 각각 독립된 스코프를 가지므로 독립된 데이터(잔고)를 가지도록 할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클로저의 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 함수에서 외부 함수의 스코프에 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 외부 함수의 스코프란, 렉시컬 스코프를 의미하며 아래의 코드에 나타나 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const outerFunc = () =&amp;gt; {
      const name = 'Tom';
      const innerFunc = () =&amp;gt; console.log('my name is ' + name);
      return innerFunc;
    }

    const inner = outerFunc(); // my name is Tom
    inner(2);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로, 함수 안에 있는 지역변수들은 그 함수가 처리되는 동안에만 존재하고 사라지므로,&lt;br /&gt;outerFunc 함수의 name 변수 또한 사라질 것이라고 예상하는 것이 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 outerFunc 함수의 실행 결과로 inner 함수를 반환 받게 되는데,&lt;br /&gt;inner 함수가 존재하는 한, inner함수가 outerFunc 함수의 스코프에 있는 name을 참조하기 때문에,&lt;br /&gt;outerFunc 함수의 스코프에 있는 name 변수는 사라지지 않게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주절주절 얘기했지만, 결국 inner 함수가 존재하는 한 outer 함수의 스코프에 선언된 변수를 이용할 수 있다는 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클로저의 예시2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징3의 계좌의 예시와 같이, 각각 독립된 변수를 다뤄야 한다면&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const outerFunc = () =&amp;gt; {
    let money = 0;
    let cnt = 0;
    const innerFunc = (amount) =&amp;gt; {
        if(amount){
            money += amount;
            cnt++;
        }
        console.log('계좌 잔액 : ', money);
        console.log('계좌 입금 횟수 :', cnt);
    };
    return innerFunc;
}

const account = outerFunc();
account(10000);
account(10000);
// 계좌 잔액 :  10000
// 계좌 입금 횟수 : 1
// 계좌 잔액 :  20000
// 계좌 입금 횟수 : 2

const account2 = outerFunc();
account2(50000);
account2(30000);
// 계좌 잔액 :  50000
// 계좌 입금 횟수 : 1
// 계좌 잔액 :  80000
// 계좌 입금 횟수 : 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 선언하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 account, account2는 둘 다 클로저이며, 서로 다른 렉시컬 환경을 가진다.&lt;br /&gt;각각의 변수마다 다른 상위 스코프를 가진다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클로저의 주의사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 예시에서 살펴 봤듯이, 이미 실행이 끝난 외부함수의 스코프가 내부함수에 의해 참조되어야 하므로, 사라지지 않고 남아있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조가 더이상 되지 않는 스코프가 가비지컬렉터에 의해 정리가 되는 것이 정상적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 클로저를 이용하면, 이 외부함수의 스코프는 계속 남아있을 것이고 가비지컬렉터가 작동하지 않을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 클로저의 남용은 결국 메모리 문제를 일으킬 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 클로저의 장단점을 파악하여 적재적소에 사용해야 하며,&lt;br /&gt;라이프사이클이 끝난 함수는 참조를 제거해서 메모리가 회수될 수 있도록 돕는 것이 좋겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고 사이트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://poiemaweb.com/js-scope&quot;&gt;https://poiemaweb.com/js-scope&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hanamon.kr/javascript-클로저/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hanamon.kr/javascript-클로저/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 클로저</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/113</guid>
      <comments>https://gobae.tistory.com/113#entry113comment</comments>
      <pubDate>Thu, 6 Jan 2022 21:23:47 +0900</pubDate>
    </item>
    <item>
      <title>[객체] 자바스크립트에서 객체를 생성하는 다양한 방법</title>
      <link>https://gobae.tistory.com/112</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체는 데이터를 key:value 쌍으로 저장하는 자료구조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 하나의 변수(식별자)에 여러 데이터들을 저장하여 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, 자바스크립트의 거의 모든 것들은 객체다.&lt;br /&gt;배열, 함수, 객체, 날짜, 정규표현식 등등!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원시값을 제외한 값들은 모두 객체다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 모든 객체들에 대해 다루기보단, 모듈의 기반이 되며 key:value의 쌍을 가지는 객체를 중심으로 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 객체를 생성하는 다양한 방법들에 대해서 알아본다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 객체 리터럴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 친근한 {}를 이용해서 객체를 만드는 방법이다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;    const coder = {
      name: 'jiho',
      hobby: 'coding',
      selfIntroduction(){
        console.log('안녕하세요? 제 이름은 ' + this.name + '이며 취미는 ' + this.hobby +'입니다.');
      }
    };

    coder.selfIntroduction()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 생성자 패턴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 정의하고, new 키워드로 함수를 호출하면 그 함수는 생성자 함수가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 함수로서 호출된 함수는, return이 없다면 암묵적인 return this로 인스턴스 객체를 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;생성자 함수나 클래스 등을 선언할 때는 이름을 PascalCase로 정의하는 것이 관례다!&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    function Coder(name, hobby){
        this.name = name;
          this.hobby = hobby;
          this.selfIntroduction = function(){
             console.log('안녕하세요? 제 이름은 ' + this.name + '이며 취미는 ' + this.hobby +'입니다.');
        }
    }

    const jiho = new Coder('jiho', 'coding');
    jiho.selfIntroduction();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 프로토타입을 이용한 객체&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토타입을 이용해서 공통 메서드들을 프로토타입에 정의할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    function Coder(name, hobby){
        this.name = name;
          this.hobby = hobby;
    }

    Coder.prototype.selfIntroduction = function(){
             console.log('안녕하세요? 제 이름은 ' + this.name + '이며 취미는 ' + this.hobby +'입니다.');
    }

    const jiho = new Coder('jiho', 'coding');
    jiho.selfIntroduction();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 패턴과 무척 유사해보이지만, 프로토타입을 이용한 객체 생성은 메모리 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 패턴에서는?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자를 통해 생선된 객체들이 여러개 있다면, 객체마다 selfIntroduction 메서드를 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토타입에서는?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체들을 여러 개 생성해도, 같은 프로토타입을 공유한다. selfIntroduction 메서드는 프로토타입에 하나 존재한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 객체들을 생성해도 객체들의 프로토타입은 Coder.prototype으로 같으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토타입의 selfIntroduction 메서드를 재사용하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크롬 개발자 도구를 통해 직접 비교해보면,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/swDxv/btrpLuCjW8d/xj4ZDS0ZkyTaqSQt06SL81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/swDxv/btrpLuCjW8d/xj4ZDS0ZkyTaqSQt06SL81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/swDxv/btrpLuCjW8d/xj4ZDS0ZkyTaqSQt06SL81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FswDxv%2FbtrpLuCjW8d%2Fxj4ZDS0ZkyTaqSQt06SL81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;280&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 패턴을 통해 생성한 객체는 객체 내부에 메서드를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rO0eO/btrpKl6Isii/AmLKVtHJwjiqnqg3HGvmi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rO0eO/btrpKl6Isii/AmLKVtHJwjiqnqg3HGvmi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rO0eO/btrpKl6Isii/AmLKVtHJwjiqnqg3HGvmi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrO0eO%2FbtrpKl6Isii%2FAmLKVtHJwjiqnqg3HGvmi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;272&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토타입을 이용한 객체는, 생성한 객체와 연결된 프로토타입에 메서드를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Object.create&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prototype 키워드를 직접적으로 이용하진 않지만, 프로토타입을 이용한 객체를 만들 때 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;    const coderMethod = {
        selfIntroduction : function(){
             console.log('안녕하세요? 제 이름은 ' + this.name + '이며 취미는 ' + this.hobby +'입니다.');
        }
    }

    const jiho = Object.create(coderMethod, {
          name: { value:'jiho'},
          hobby: { value: 'coding'}
    });

    jiho.selfIntroduction();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;create 메서드는 프로토타입 기반 상속을 매끄럽게 하기 위해 탄생했다고 한다.&lt;br /&gt;class가 ES6에서 추가된 이후, extends를 통해 상속을 구현할 수 있어서 현재는 잘 쓰이진 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 클래스 패턴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES6(2015)에서 자바스크립트에 클래스가 추가되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class 키워드를 이용해 클래스를 정의하고, new 키워드로 클래스를 호출하면 생성자(constructor)가 호출된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자는 인스턴스를 만들어 반환하며, 이 인스턴스가 객체다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 class 키워드는 내부적으로 프로토타입을 통해 구현되어있다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;    class Coder{
        constructor(name, hobby){
            this.name = name;
            this.hobby = hobby;
        }
        selfIntroduction(){
            console.log('안녕하세요? 제 이름은 ' + this.name + '이며 취미는 ' + this.hobby +'입니다.');
        }
    }

    const jiho = new Coder('jiho', 'coding');
    jiho.selfIntroduction();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토타입을 이용한다면&amp;nbsp;selfIntroduction 메서드가 프로토타입에 담겨있지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qkg7p/btrpIyen4Ha/0V73KCxCPwZgvk08Oav3b0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qkg7p/btrpIyen4Ha/0V73KCxCPwZgvk08Oav3b0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qkg7p/btrpIyen4Ha/0V73KCxCPwZgvk08Oav3b0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQkg7p%2FbtrpIyen4Ha%2F0V73KCxCPwZgvk08Oav3b0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;736&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;736&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스를 통해 생성한 객체는 프로토타입을 이용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 다양한 방법으로 객체를 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 특성을 이해하고 적재적소에 활용할 줄 알면 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반화 없이 하나의 객체만을 생성해야 할 때.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자를 사용하기 보다는, 1번의 리터럴만을 사용하는 것이 가장 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person 등의 일반화된 개념으로부터 인스턴스를 생성해야 한다면&lt;br /&gt;class 키워드나 생성자 함수 + 프로토타입을 적극 이용하여 반복과 메모리 누수를 줄일 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript/theory</category>
      <category>자바스크립트 객체 생성</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/112</guid>
      <comments>https://gobae.tistory.com/112#entry112comment</comments>
      <pubDate>Tue, 4 Jan 2022 12:36:45 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 거리두기 확인하기</title>
      <link>https://gobae.tistory.com/111</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nNfRz/btrxaPmEFWt/lZ3U5ybCOAITTGm5xRsLKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nNfRz/btrxaPmEFWt/lZ3U5ybCOAITTGm5xRsLKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nNfRz/btrxaPmEFWt/lZ3U5ybCOAITTGm5xRsLKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnNfRz%2FbtrxaPmEFWt%2FlZ3U5ybCOAITTGm5xRsLKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;290&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;figure id=&quot;og_1639634291986&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코딩테스트 연습 - 거리두기 확인하기&quot; data-og-description=&quot;[[&amp;quot;POOOP&amp;quot;, &amp;quot;OXXOX&amp;quot;, &amp;quot;OPXPX&amp;quot;, &amp;quot;OOXOX&amp;quot;, &amp;quot;POXXP&amp;quot;], [&amp;quot;POOPX&amp;quot;, &amp;quot;OXPXP&amp;quot;, &amp;quot;PXXXO&amp;quot;, &amp;quot;OXXXO&amp;quot;, &amp;quot;OOOPP&amp;quot;], [&amp;quot;PXOPX&amp;quot;, &amp;quot;OXOXP&amp;quot;, &amp;quot;OXPOX&amp;quot;, &amp;quot;OXXOP&amp;quot;, &amp;quot;PXPOX&amp;quot;], [&amp;quot;OOOXX&amp;quot;, &amp;quot;XOOOX&amp;quot;, &amp;quot;OOOXX&amp;quot;, &amp;quot;OXOOX&amp;quot;, &amp;quot;OOOOO&amp;quot;], [&amp;quot;PXPXP&amp;quot;, &amp;quot;XPXPX&amp;quot;, &amp;quot;PXPXP&amp;quot;, &amp;quot;XPXPX&amp;quot;, &amp;quot;PXPXP&amp;quot;]] [1, 0, 1, 1, 1]&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/81302&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/81302&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/AupdQ/hyMIStLZPS/mORAZjoKop4Lbi9UoW6RO0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bcS3aj/hyMI2iPkzL/iNLKVrEXnvTTEj5wR7qRy0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/81302&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/81302&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/AupdQ/hyMIStLZPS/mORAZjoKop4Lbi9UoW6RO0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bcS3aj/hyMI2iPkzL/iNLKVrEXnvTTEj5wR7qRy0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - 거리두기 확인하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[[&quot;POOOP&quot;, &quot;OXXOX&quot;, &quot;OPXPX&quot;, &quot;OOXOX&quot;, &quot;POXXP&quot;], [&quot;POOPX&quot;, &quot;OXPXP&quot;, &quot;PXXXO&quot;, &quot;OXXXO&quot;, &quot;OOOPP&quot;], [&quot;PXOPX&quot;, &quot;OXOXP&quot;, &quot;OXPOX&quot;, &quot;OXXOP&quot;, &quot;PXPOX&quot;], [&quot;OOOXX&quot;, &quot;XOOOX&quot;, &quot;OOOXX&quot;, &quot;OXOOX&quot;, &quot;OOOOO&quot;], [&quot;PXPXP&quot;, &quot;XPXPX&quot;, &quot;PXPXP&quot;, &quot;XPXPX&quot;, &quot;PXPXP&quot;]] [1, 0, 1, 1, 1]&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;function solution(places) {
  const w = 5;
  const h = 5;

  function checkDistance(x, y, place) {
    const [dx1, dy1] = [
      [-1, 0, 1, 0],
      [0, 1, 0, -1],
    ];
    for (let i = 0; i &amp;lt; 4; i++) {
      const nx = x + dx1[i];
      const ny = y + dy1[i];
      if (nx &amp;lt; 0 || nx &amp;gt;= w || ny &amp;lt; 0 || ny &amp;gt;= h) continue;
      if (place[nx][ny] === 'P') return 0;
    }

    const [dx2, dy2] = [dx1.map((v) =&amp;gt; 2 * v), dy1.map((v) =&amp;gt; 2 * v)];
    for (let i = 0; i &amp;lt; 4; i++) {
      const nx = x + dx2[i];
      const ny = y + dy2[i];
      if (nx &amp;lt; 0 || nx &amp;gt;= w || ny &amp;lt; 0 || ny &amp;gt;= h) continue;
      if (place[nx][ny] === 'P' &amp;amp;&amp;amp; place[x + dx1[i]][y + dy1[i]] !== 'X')
        return 0;
    }

    const [dx3, dy3] = [
      [-1, 1, 1, -1],
      [1, 1, -1, -1],
    ];
    for (let i = 0; i &amp;lt; 4; i++) {
      const nx = x + dx3[i];
      const ny = y + dy3[i];
      const nIdx = (i + 1) % 4;
      if (nx &amp;lt; 0 || nx &amp;gt;= w || ny &amp;lt; 0 || ny &amp;gt;= h) continue;
      if (
        place[nx][ny] === 'P' &amp;amp;&amp;amp;
        (place[x + dx1[i]][y + dy1[i]] !== 'X' ||
          place[x + dx1[nIdx]][y + dy1[nIdx]] !== 'X')
      )
        return 0;
    }
    return 1;
  }

  return places.map((place) =&amp;gt; {
    for (let i = 0; i &amp;lt; w; i++) {
      for (let j = 0; j &amp;lt; h; j++) {
        if (place[i][j] === 'P') {
          if (!checkDistance(i, j, place)) return 0;
        }
      }
    }
    return 1;
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyfiWq/btrn06Ys7X5/k82DiDweI65wOa7s4Tf7L0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyfiWq/btrn06Ys7X5/k82DiDweI65wOa7s4Tf7L0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyfiWq/btrn06Ys7X5/k82DiDweI65wOa7s4Tf7L0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyfiWq%2Fbtrn06Ys7X5%2Fk82DiDweI65wOa7s4Tf7L0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;638&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건을 보면, 한 응시자의 2만큼의 맨하튼 거리에는 다른 응시자가 있으면 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 두 응시자 끼리의 접근이 파티션 'X' 로 막혀 있다면, 허용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정보를 바탕으로, 3가지 경우를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 1만큼의 맨하튼 거리에 응시자가 있는가?&lt;br /&gt;2. 2만큼의 맨하튼 거리에 응시자가 있는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 2.1 현재 위치로부터 대각선에 응시자가 있는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 2.2 상하좌우 2칸 거리에 응시자가 있는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림으로 나타내보면,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NLsiB/btrn0kXhNCR/X6uTcEWX9kPJ8MrvVvDYTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NLsiB/btrn0kXhNCR/X6uTcEWX9kPJ8MrvVvDYTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NLsiB/btrn0kXhNCR/X6uTcEWX9kPJ8MrvVvDYTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNLsiB%2Fbtrn0kXhNCR%2FX6uTcEWX9kPJ8MrvVvDYTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;408&quot; height=&quot;770&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;1196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cELNu4/btrn4rtXhtQ/pTQkpzm57bZSPkxUQimKs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cELNu4/btrn4rtXhtQ/pTQkpzm57bZSPkxUQimKs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cELNu4/btrn4rtXhtQ/pTQkpzm57bZSPkxUQimKs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcELNu4%2Fbtrn4rtXhtQ%2FpTQkpzm57bZSPkxUQimKs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;409&quot; height=&quot;343&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;1196&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkEpCy/btrn1ZEzXNB/JV1oLMwhCBHfSF5d2Qyzwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkEpCy/btrn1ZEzXNB/JV1oLMwhCBHfSF5d2Qyzwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkEpCy/btrn1ZEzXNB/JV1oLMwhCBHfSF5d2Qyzwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkEpCy%2Fbtrn1ZEzXNB%2FJV1oLMwhCBHfSF5d2Qyzwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;413&quot; height=&quot;355&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;766&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매우 조잡하지만 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 순서대로 검증한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;    const [dx1, dy1] = [
      [-1, 0, 1, 0],
      [0, 1, 0, -1],
    ];
    for (let i = 0; i &amp;lt; 4; i++) {
      const nx = x + dx1[i];
      const ny = y + dy1[i];
      if (nx &amp;lt; 0 || nx &amp;gt;= w || ny &amp;lt; 0 || ny &amp;gt;= h) continue;
      if (place[nx][ny] === 'P') return 0;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 맨하탄 거리에 응시자가 있다면, 거리두기를 위반했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;    const [dx2, dy2] = [dx1.map((v) =&amp;gt; 2 * v), dy1.map((v) =&amp;gt; 2 * v)];
    for (let i = 0; i &amp;lt; 4; i++) {
      const nx = x + dx2[i];
      const ny = y + dy2[i];
      if (nx &amp;lt; 0 || nx &amp;gt;= w || ny &amp;lt; 0 || ny &amp;gt;= h) continue;
      if (place[nx][ny] === 'P' &amp;amp;&amp;amp; place[x + dx1[i]][y + dy1[i]] !== 'X')
        return 0;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상하좌우로 2 맨하탄 거리에 응시자가 있고, 그 사이에 파티션이 없다면 거리두기를 위반했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;    const [dx3, dy3] = [
      [-1, 1, 1, -1],
      [1, 1, -1, -1],
    ];
    for (let i = 0; i &amp;lt; 4; i++) {
      const nx = x + dx3[i];
      const ny = y + dy3[i];
      const nIdx = (i + 1) % 4;
      if (nx &amp;lt; 0 || nx &amp;gt;= w || ny &amp;lt; 0 || ny &amp;gt;= h) continue;
      if (
        place[nx][ny] === 'P' &amp;amp;&amp;amp;
        (place[x + dx1[i]][y + dy1[i]] !== 'X' ||
          place[x + dx1[nIdx]][y + dy1[nIdx]] !== 'X')
      )
        return 0;
    }
    return 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대각선에 응시자가 있고, 대각선 위치로 상/하/좌/우 등으로 이동하는 2개의 경로에 파티션이 없다면 거리두기를 위반했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 3가지의 조건에서 모두 통과했다면, 해당 위치에 있는 응시자는 거리두기를 위반하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 대기실에 위치한 모든 응시자가 이 3가지 검증 과정을 통과할 때 이 대기실은 거리두기를 위반하지 않았다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;  return places.map((place) =&amp;gt; {
    for (let i = 0; i &amp;lt; w; i++) {
      for (let j = 0; j &amp;lt; h; j++) {
        if (place[i][j] === 'P') {
          if (!checkDistance(i, j, place)) return 0;
        }
      }
    }
    return 1;
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;places에는 몇 개의 대기실들의 정보가 있으며, 각 인덱스에 해당하는 place에는 하나의 대기실에 있는 응시자 위치 정보가 있다.&lt;br /&gt;하나의 대기실 안에서 한명이라도 거리두기를 위반했다면, 0을 반환하며 종료하고, 모두 거리두기를 지켰다면 1을 반환한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;여기는 헤맸던 부분인데, 오류인지 아닌지 정확한 원인을 잘 모르겠다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응시자 간의 거리두기를 확인하면서, 처음에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;place[x+dx1[i]][y+dy1[i]] === '0'&lt;/code&gt; 로 빈 테이블을 검증했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 첫 번째 1 맨하탄 거리를 검증하면서, 상하좌우에 응시자가 존재한다면 거리두기가 실패이므로,&lt;br /&gt;2,3번째의 거리 검증에서는 1 맨하탄 거리에 다른 응시자는 위치할 수가 없다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 1 맨하탄 거리에는 빈 테이블(0) 와 파티션(X) 둘 중 하나만 올 수 있어서 빈 테이블인지를 검증했고,&lt;br /&gt;3?4가지 정도의 테스트케이스가 실패했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 시도를 하다가,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;place[x+dx1[i]][y+dy1[i]] !== 'X'&lt;/code&gt; 로 파티션이 없을때를 검증하였더니, 테스트케이스가 통과하더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 쓰인 '0'문자 문제인지, 아니면 미처 고려하지 못한 부분이 있는건지..? 아직 어떤게 문제인지는 잘 모르겠다.&lt;/p&gt;</description>
      <category>DS &amp;amp; Algorithm/programmers</category>
      <category>프로그래머스 거리두기 확인하기</category>
      <category>프로그래머스 카카오 기출</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/111</guid>
      <comments>https://gobae.tistory.com/111#entry111comment</comments>
      <pubDate>Thu, 16 Dec 2021 15:03:17 +0900</pubDate>
    </item>
    <item>
      <title>[HTML] script 태그의 async, defer 속성</title>
      <link>https://gobae.tistory.com/110</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Script 태그&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM이 생성될 때, Script 태그를 만나면 DOM 생성을 멈추고 Script를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src 속성을 포함한 외부 스크립트를 만났을 때에도 마찬가지이다.&lt;br /&gt;해당 스크립트를 다운 받고 실행할 때 까지 Script 태그 아래에 선언된 DOM 요소들은 대기해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 DOM 생성을 중단하지 않고, 스크립트를 동시에 내려받게 할 수 있는 방법으로&lt;br /&gt;defer, async가 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DOM 생성 중단이 일어난다면 어떤 문제가 발생하는가.&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위에서 언급한 대로, 스크립트 태그 아래에 있는 DOM 요소는 대기하므로, 해당 DOM 요소들에 접근할 수 없다.&lt;/li&gt;
&lt;li&gt;querySelector와 같이 DOM을 직접적으로 조작하여 핸들러등을 추가하는 등의 행위가 불가능하다.&lt;/li&gt;
&lt;li&gt;스크립트의 용량이 크다면, 페이지 'Block' 현상이 일어난다.&lt;/li&gt;
&lt;li&gt;스크립트 실행 전까지 사용자는 스크립트 아래의 콘텐츠를 볼 수 없으며, 페이지를 정상적으로 이용할 수도 없다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통의 경우에서는 다음과 같은 지연이 눈에 잘 띄지 않으나&lt;br /&gt;네트워크 환경이 열악한 지역 혹은 모바일 네트워크 등등에서는 문제가 될 가능성이 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 통해 확인해보자.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
    &amp;lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;p&amp;gt;스크립트 태그 이전&amp;lt;/p&amp;gt;

    &amp;lt;script src=&quot;https://javascript.info/article/script-async-defer/long.js?speed=1&quot;&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;p&amp;gt;스크립트 태그 이후&amp;lt;/p&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;normal_example.gif&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7JEvb/btrnJdoB1kp/zkNX0s3zndgAS0qCShFwfk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7JEvb/btrnJdoB1kp/zkNX0s3zndgAS0qCShFwfk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7JEvb/btrnJdoB1kp/zkNX0s3zndgAS0qCShFwfk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/7JEvb/btrnJdoB1kp/zkNX0s3zndgAS0qCShFwfk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;900&quot; data-filename=&quot;normal_example.gif&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;script를 다운로드 / 실행할 때, script 태그 아래의 콘텐츠는 보이지 않는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;normal, defer, async 스크립트의 동작에 대한 이해&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;normal, defer, async의 차이에 대한 이해를 도울만한 시각적인 자료가 있어서 가져왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;1790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QP5X6/btrnDnS5vIv/G8okRNfciVSbojXBwD3gb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QP5X6/btrnDnS5vIv/G8okRNfciVSbojXBwD3gb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QP5X6/btrnDnS5vIv/G8okRNfciVSbojXBwD3gb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQP5X6%2FbtrnDnS5vIv%2FG8okRNfciVSbojXBwD3gb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;714&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;1790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 그림을 통해 각 속성별 동작의 차이를 느끼고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 나오는 각 속성별로 정리한 내용을 통해 좀 더 자세한 내용을 파악해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;defer 속성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;defer 속성은 스크립트를 '백그라운드'에서 다운로드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그러므로 HTML 파싱(DOM 생성)을 중단시키지 않으며 defer 속성이 있는 스크립트의 다운로드가 끝나더라도&lt;br /&gt;DOM 생성을 마칠 때 까지 지연된 후 스크립트를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유의할 점으로는 DOM 준비 이후 바로 스크립트를 실행하며, 이 시점은 DOMContentLoaded 이벤트가 발생하기 전이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 defer 스크립트는 일반 스크립트와 마찬가지로,&lt;br /&gt;순서를 지키며 실행되므로 앞선 script가 무겁고 뒤의 스크립트가 가볍다고 해도 순서대로 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 통해 확인해보자.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
    &amp;lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;p&amp;gt;스크립트 태그 이전&amp;lt;/p&amp;gt;

    &amp;lt;script&amp;gt;
      document.addEventListener('DOMContentLoaded', () =&amp;gt;alert('defer 스크립트가 실행된 후에, DOMContentLoaded 이벤트가 발생시 실행할 콜백이 실행된다.!'));
    &amp;lt;/script&amp;gt;

    &amp;lt;script defer src=&quot;https://javascript.info/article/script-async-defer/long.js?speed=1&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!--무거운 스크립트--&amp;gt;

    &amp;lt;script defer src=&quot;https://javascript.info/article/script-async-defer/small.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
     &amp;lt;!--가벼운 스크립트--&amp;gt;

    &amp;lt;p&amp;gt;스크립트 태그 이후&amp;lt;/p&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;defer_example.gif&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfkT1O/btrnGj3VJ7p/krSmHinIQOBHJkvpEoPvl0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfkT1O/btrnGj3VJ7p/krSmHinIQOBHJkvpEoPvl0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfkT1O/btrnGj3VJ7p/krSmHinIQOBHJkvpEoPvl0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dfkT1O/btrnGj3VJ7p/krSmHinIQOBHJkvpEoPvl0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;900&quot; data-filename=&quot;defer_example.gif&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;페이지의 콘텐츠는 DOM을 파싱하며 바로 출력된다.&lt;/li&gt;
&lt;li&gt;defer 속성이 담긴 script는 백그라운드에서 다운로드되며, DOM 생성이 완료될 때 까지 지연된다.&lt;/li&gt;
&lt;li&gt;DOM 생성이 완료되고, DOMContentLoaded 이벤트가 발생하기 전에 script가 순서대로 실행된다.&lt;/li&gt;
&lt;li&gt;defer에 의해 지연되던 script가 모두 실행되면, DOMContentLoaded 이벤트가 발생한다.&lt;/li&gt;
&lt;li&gt;defer 속성은 외부 스크립트(src 속성을 이용하는 스크립트)에만 유효하므로, src 속성이 없다면 defer를 이용할 수 없다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;async 속성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async 속성은 페이지 작업과 완전히 독립적으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;defer와 마찬가지로, async 속성을 통하면 스크립트는 백그라운드에서 다운로드된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 스크립트는 다운로드 즉시 실행되며, 스크립트 실행 도중에는 DOM 생성을 중단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드 즉시 실행되기 때문에, DOMContentLoaded 이벤트 이전의 실행을 보장하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심지어는 DOMContentLoaded 이후에 스크립트가 실행될 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 async는 '다운로드 완료 순서'에 의존하므로,&lt;br /&gt;defer가 선언된 순서를 지키며 스크립트를 실행 했던 것과는 달리, async는 선언된 순서와 실행 순서가 무관하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 'load first order' 라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async 스크립트를 비동기 스크립트라고 하는데,&amp;nbsp;이는 각각 스크립트가 독립적인 역할을 하는 광고 등의 서드파티 스크립트 등을&lt;br /&gt;현재 개발중인 스크립트에 통합할 때 유용하다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt;&amp;gt; async 스크립트는 다른 스크립트에 의존하지 않는 독립성을 보장하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async 스크립트를 실행해보면,&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
    &amp;lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;p&amp;gt;스크립트 태그 이전&amp;lt;/p&amp;gt;

    &amp;lt;script&amp;gt;
      document.addEventListener('DOMContentLoaded', () =&amp;gt; alert('DOMContentLoaded!'));
    &amp;lt;/script&amp;gt;

    &amp;lt;script async src=&quot;https://javascript.info/article/script-async-defer/long.js?speed=1&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- 무거운 스크립트 --&amp;gt;

    &amp;lt;script defer src=&quot;https://javascript.info/article/script-async-defer/small.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
     &amp;lt;!-- 가벼운 스크립트 --&amp;gt;

    &amp;lt;p&amp;gt;스크립트 태그 이후&amp;lt;/p&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;async_example.gif&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O3Dvd/btrnDoxFdgX/PDQR0CET5MZVFnAosuemgK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O3Dvd/btrnDoxFdgX/PDQR0CET5MZVFnAosuemgK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O3Dvd/btrnDoxFdgX/PDQR0CET5MZVFnAosuemgK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/O3Dvd/btrnDoxFdgX/PDQR0CET5MZVFnAosuemgK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;900&quot; data-filename=&quot;async_example.gif&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOMContentLoaded -&amp;gt; Small Script -&amp;gt; Long Script 의 순으로 실행되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 요약하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;페이지의 콘텐츠는 DOM을 파싱하며 바로 출력된다.&lt;/li&gt;
&lt;li&gt;asnyc 속성이 담긴 script는 백그라운드에서 다운로드되며, 다운로드 즉시 실행되어 실행 도중에는 DOM 파싱을 중단한다.&lt;/li&gt;
&lt;li&gt;DOMContentLoaded 이벤트는 async 스크립트가 실행되기 이전에도 실행될 수 있다. = 정확한 순서 예측이 불가능하다.&lt;/li&gt;
&lt;li&gt;async 스크립트는 서로를 기다리지 않으므로, HTML에서 선언 순서와 실행 순서는 무관하다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async, defer 스크립트는 다운로드 시 페이지 렌더링(DOM 생성)을 막지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그래서 적절한 async, defer 사용은 사용자의 UX를 향상시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;defer 속성은 보통 DOM 전체가 필요한 스크립트나 실행 순서가 중요할 때(B 스크립트는 A 스크립트 이후에 실행되어야 할 때) 적용한다.&lt;br /&gt;async 속성은 방문자 수 카운터 혹은 광고 스크립트 등과 같이 독립적으로 작동하는 스크립트에 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 DOM 파싱을 막지 않는 옵션을 사용할 때는, 페이지가 출력 되었으나, 스크립트가 실행되지 않은 시점을 조심해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로, 로딩 스피너를 사용하고 비즈니스 로직을 처리해야 하는 버튼등을 비활성화 시키는 등의 추가 조치를 통해&lt;br /&gt;사용자에게 아직 페이지가 완전히 불러와지지 않았다는 것을 시각적으로 보여줄 수 있어야 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고 사이트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://parksb.github.io/article/0.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://parksb.github.io/article/0.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/script-async-defer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.javascript.info/script-async-defer&lt;/a&gt;&lt;/p&gt;</description>
      <category>HTML &amp;amp; CSS</category>
      <category>script defer async tag</category>
      <author>jiho_bae</author>
      <guid isPermaLink="true">https://gobae.tistory.com/110</guid>
      <comments>https://gobae.tistory.com/110#entry110comment</comments>
      <pubDate>Sun, 12 Dec 2021 17:17:53 +0900</pubDate>
    </item>
  </channel>
</rss>