<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발자 메론빵</title>
    <link>https://ddiaezzang.tistory.com/</link>
    <description>웹/앱 개발 및 수업합니다!  
문의는 visveryver2@gmail.com</description>
    <language>ko</language>
    <pubDate>Thu, 21 May 2026 02:36:20 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>ddiae</managingEditor>
    <image>
      <title>개발자 메론빵</title>
      <url>https://tistory1.daumcdn.net/tistory/7223131/attach/c5fd4d0936574eee9f3419e2c75e8c13</url>
      <link>https://ddiaezzang.tistory.com</link>
    </image>
    <item>
      <title>[초등/저학년] 컴퓨터 입력 장치 알기</title>
      <link>https://ddiaezzang.tistory.com/18</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2448&quot; data-origin-height=&quot;1728&quot;&gt;&lt;a href=&quot;https://computer-input-basics.vercel.app/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UMmNs/dJMb997oX8W/HKaXvgi1KJMTujl1nOtf1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUMmNs%2FdJMb997oX8W%2FHKaXvgi1KJMTujl1nOtf1k%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;2448&quot; height=&quot;1728&quot; data-origin-width=&quot;2448&quot; data-origin-height=&quot;1728&quot;/&gt;&lt;/a&gt;&lt;figcaption&gt;이미지를 클릭하면 사이트로 이동합니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-link-islinknewwindow=&quot;true&quot; data-link=&quot;https://coding-personality.vercel.app/&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; data-ke-style=&quot;alignCenter&quot; data-ke-type=&quot;image&quot;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;활용 문의는 visveryver2@gmail.com 으로 연락 바랍니다.&lt;/p&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div id=&quot;reaction-17&quot; data-tistory-react-app=&quot;Reaction&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>수업</category>
      <author>ddiae</author>
      <guid isPermaLink="true">https://ddiaezzang.tistory.com/18</guid>
      <comments>https://ddiaezzang.tistory.com/18#entry18comment</comments>
      <pubDate>Sun, 10 May 2026 19:10:58 +0900</pubDate>
    </item>
    <item>
      <title>[초등/고학년] 오리엔테이션: 자기소개 및 성향테스트</title>
      <link>https://ddiaezzang.tistory.com/17</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2454&quot; data-origin-height=&quot;1738&quot;&gt;&lt;a href=&quot;https://coding-personality.vercel.app/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cA1bNw/dJMb990BKaw/qKhpFG5OTkDm9NqwImHOWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcA1bNw%2FdJMb990BKaw%2FqKhpFG5OTkDm9NqwImHOWk%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;2454&quot; height=&quot;1738&quot; data-origin-width=&quot;2454&quot; data-origin-height=&quot;1738&quot;/&gt;&lt;/a&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;figure data-ke-type=&quot;image&quot; data-ke-style=&quot;alignCenter&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; data-link=&quot;https://coding-personality.vercel.app/&quot; data-link-islinknewwindow=&quot;true&quot;&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;활용 문의는 visveryver2@gmail.com 으로 연락 바랍니다.&lt;/p&gt;</description>
      <category>수업</category>
      <author>ddiae</author>
      <guid isPermaLink="true">https://ddiaezzang.tistory.com/17</guid>
      <comments>https://ddiaezzang.tistory.com/17#entry17comment</comments>
      <pubDate>Tue, 5 May 2026 17:51:48 +0900</pubDate>
    </item>
    <item>
      <title>Phaser : 설치 및 개발 환경 설정, Preload, Object 추가 방법</title>
      <link>https://ddiaezzang.tistory.com/16</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  설치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm init 프로젝트 폴더의 패키지 관리자 초기화. vite를 사용할 경우 npm init vite&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm i phaser Phaser 설치&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  개발 환경 설정&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;body {
    margin: 0;
}

#app {
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-align: center;
    color: #2c3e50;
    margin: 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;new Phaser.Game({
    type: Phaser.WEBGL,
    width: '100%',
    height: '100%',
    physics: {
        default: 'arcade',
        arcade: {
            debug: import.meta.env.DEV,
            gravity: { y: 2500 }
        }
    },
    **scene: [Preloader, ...]**
});
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;type : 렌더러를 선택. AUTO, CANVAS, WEBGL 중에 하나를 선택.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AUTO: Phaser가 사용 가능한 최상의 렌더러를 자동으로 선택. WebGL을 우선으로 확인.&lt;/li&gt;
&lt;li&gt;CANVAS: 모든 환경에서 사용할 수 있으나 WebGL에 비해 성능이 떨어짐&lt;/li&gt;
&lt;li&gt;WEBGL: 하드웨어 가속을 사용하여 더 높은 성공을 제공하나 일부 환경 지원하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;physics : 물리엔진과 관련된 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;arcade는 가볍고 간단한 2D 물리엔진으로 빠른 설정이 가능함&lt;/li&gt;
&lt;li&gt;그 외의 물리엔진으로는 matter, impact, box2d가 있음&lt;/li&gt;
&lt;li&gt;debug를 true로 설정하면 물리엔진 디버깅이 가능&lt;/li&gt;
&lt;li&gt;es-module에서는 Process.env 대신 impot.meta.env를 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Scene&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;export default class DisplayScene extends Phaser.Scene {
  constructor() {
    super({ key: &quot;display&quot; });
    /*key 설정 및 Phaser.Scene의 생성자 호출*/
  }
  init() {
    /*Scene이 생성될 때 초기화*/
  }
  preload() {
    /*리소스 로드*/
  }
  create() {
    /*Scene 생성 후 초기화 담당 : 게임 객체 생성 및 초기상태 설정*/
  }
  update(): void {
    /*매 프레임마다 호출: 게임 상태 업데이트 로직 구현*/
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;scene을 사용하기 위해서는 반드시 key를 생성&lt;/li&gt;
&lt;li&gt;scene의 주요 생명 주기 메서드 : init, preload, create, update
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;preload는 따로 파일을 만들어 관리해주면 편리함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;scene의 주요 상태 변경 메서드 : stop, pause, start, resume, restart&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.notion.so/Phaser-e549ceb8220342f48602d28bd83cb51d?pvs=21&quot;&gt;DisplayeScene.ts&lt;/a&gt; 작성 후 &lt;a href=&quot;https://www.notion.so/Phaser-e549ceb8220342f48602d28bd83cb51d?pvs=21&quot;&gt;main.ts&lt;/a&gt;의 scene 속성에 해당 scene 추가&lt;/li&gt;
&lt;li&gt;create에서 this.game.scene.start('display')로 scene을 실행 (display는 scene의 key)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Preload&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;this.load.setBaseURL('assets');
this.load.image('bg1', 'background/bg1.png');
this.load.image('selectPlayer', 'selectPlayer/select.png');
this.load.atlas('player', 'player/poy/poy.png', 'player/poy/poy.json');
this.load.tilemapTiledJSON('map', 'map/main/map.json');
this.load.audio('scene0', 'sounds/scene0.mp3');
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;setBaseURL : resource가 모두 들어있는 폴더를 지정하여 이후 작성하는 resource들의 경로를 쉽게 설정 가능&lt;/li&gt;
&lt;li&gt;image(key, 이미지파일) : 이미지를 로드&lt;/li&gt;
&lt;li&gt;atlas(key, 이미지파일, json파일) : 스프라이트 이미지와 JSON파일을 로드&lt;/li&gt;
&lt;li&gt;tilemapTiledJSON(key, json파일) : TileMap을 만들 JSON파일을 로드. 이 때 TileSet이 되는 이미지 또한 &lt;a href=&quot;https://www.notion.so/Phaser-e549ceb8220342f48602d28bd83cb51d?pvs=21&quot;&gt;image메서드&lt;/a&gt;를 통해 로드해야 함&lt;/li&gt;
&lt;li&gt;audio(key, 오디오파일) : 오디오 파일을 로드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Preload에서 설정한 key를 가지고 Create에서 assets를 호출&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  GameObject&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;this.add.rectangle(x, y, width, height);
this.add.image(x, y, key);
this.add.sprite(x, y, key);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;add : 각각 사각형, 이미지, 스프라이트를 호출&lt;/li&gt;
&lt;li&gt;동일한 방식으로 circle, text 등 호출 가능&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Dev</category>
      <author>ddiae</author>
      <guid isPermaLink="true">https://ddiaezzang.tistory.com/16</guid>
      <comments>https://ddiaezzang.tistory.com/16#entry16comment</comments>
      <pubDate>Sun, 1 Dec 2024 17:46:23 +0900</pubDate>
    </item>
    <item>
      <title>정규식 패턴과 대표 예제</title>
      <link>https://ddiaezzang.tistory.com/15</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;정규식을 사용할 때 마다 서치를 하게 돼서 내가 보려고 쓰는 포스팅..&lt;br /&gt;이 포스팅을 제일 자주, 많이 들여다볼 듯 하다! 그리고 지속적으로 업데이트 될 예정 &lt;/blockquote&gt;
&lt;h2 id=&quot;-기초-패턴&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  기초 패턴&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;.&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 임의의 한 문자에 매칭 (개행 문자 제외)&lt;br /&gt;^&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 문자열의 시작&lt;br /&gt;$&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 문자열의 끝&lt;br /&gt;*&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 앞의 패턴이 0번 이상 반복됨&lt;br /&gt;+&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 앞의 패턴이 1번 이상 반복됨&lt;br /&gt;?&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 앞의 패턴이 0번 또는 1번 존재&lt;br /&gt;{n}&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 앞의 패턴이 정확히 n번 반복&lt;br /&gt;{n,}&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 앞의 패턴이 n번 이상 반복&lt;br /&gt;{n,m}&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 앞의 패턴이 n번 이상, m번 이하로 반복&lt;/p&gt;
&lt;h2 id=&quot;-문자-클래스&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  문자 클래스&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[abc]&lt;span&gt;&amp;nbsp;&lt;/span&gt;: a, b, c 중 하나의 문자에 매칭&lt;br /&gt;[^abc]&lt;span&gt;&amp;nbsp;&lt;/span&gt;: a, b, c가 아닌 문자에 매칭&lt;br /&gt;[a-z]&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 소문자 a부터 z 중 하나의 문자에 매칭&lt;br /&gt;[A-Z]&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 대문자 A부터 Z 중 하나의 문자에 매칭&lt;br /&gt;[0-9]&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 숫자 0부터 9 중 하나에 매칭&lt;br /&gt;[a-zA-Z0-9]&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 알파벳 대소문자와 숫자에 매칭&lt;/p&gt;
&lt;h2 id=&quot;-특수-문자&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  특수 문자&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;\d&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 숫자와 매칭 ([0-9]와 동일)&lt;br /&gt;\D&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 숫자가 아닌 문자와 매칭 ([^0-9]와 동일)&lt;br /&gt;\w&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 단어 문자에 매칭 (알파벳, 숫자, 언더스코어) ([a-zA-Z0-9_]와 동일)&lt;br /&gt;\W&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 단어 문자가 아닌 것에 매칭 ([^a-zA-Z0-9_]와 동일)&lt;br /&gt;\s&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 공백 문자에 매칭 (스페이스, 탭, 줄바꿈)&lt;br /&gt;\S&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 공백이 아닌 문자에 매칭&lt;/p&gt;
&lt;h2 id=&quot;-그룹&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  그룹&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(abc)&lt;span&gt;&amp;nbsp;&lt;/span&gt;: abc라는 정확한 문자열에 매칭하는 그룹&lt;br /&gt;(a|b)&lt;span&gt;&amp;nbsp;&lt;/span&gt;: a 또는 b 중 하나에 매칭&lt;br /&gt;(?:abc)&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 매칭되지만 캡처되지 않는 그룹&lt;/p&gt;
&lt;h2 id=&quot;-경계&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  경계&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;\b&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 단어 경계 (단어의 시작 또는 끝)&lt;br /&gt;\B&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 비단어 경계&lt;/p&gt;
&lt;h2 id=&quot;-긍정형부정형-탐색&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  긍정형/부정형 탐색&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(?=abc)&lt;span&gt;&amp;nbsp;&lt;/span&gt;: abc 앞에 오는 패턴을 찾음 (긍정형 전방 탐색)&lt;br /&gt;(?!abc)&lt;span&gt;&amp;nbsp;&lt;/span&gt;: abc가 아닌 패턴을 찾음 (부정형 전방 탐색)&lt;br /&gt;(?&amp;lt;=abc)&lt;span&gt;&amp;nbsp;&lt;/span&gt;: abc 뒤에 오는 패턴을 찾음 (긍정형 후방 탐색)&lt;br /&gt;(?&amp;lt;!abc)&lt;span&gt;&amp;nbsp;&lt;/span&gt;: abc가 아닌 패턴을 찾음 (부정형 후방 탐색)&lt;/p&gt;
&lt;h2 id=&quot;-자주-쓰이는-정규식-예제&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  자주 쓰이는 정규식 예제&lt;/b&gt;&lt;/h2&gt;
&lt;h4 id=&quot;이메일&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이메일&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot; style=&quot;color: #212529; text-align: start;&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;/^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;전화번호&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;전화번호&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot; style=&quot;color: #212529; text-align: start;&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;// `-` 제외
/^01([0|1|6|7|8|9])([0-9]{3,4})([0-9]{4})$/

// `-` 포함
/^01([0|1|6|7|8|9])-?([0-9]{3,4})-?([0-9]{4})$/&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;날짜-yyyy-mm-dd&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;날짜 (YYYY-MM-DD)&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot; style=&quot;color: #212529; text-align: start;&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;^\d{4}-\d{2}-\d{2}$&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;생년월일&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;생년월일&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot; style=&quot;color: #212529; text-align: start;&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;//6자리
/^([0-9]{2}(0[1-9]|1[0-2])(0[1-9]|[1,2][0-9]|3[0,1]))$/

//8자리
/^(19[0-9][0-9]|20\d{2})(0[0-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$/&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;한글만-허용&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;한글만 허용&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot; style=&quot;color: #212529; text-align: start;&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;/[a-z0-9]|[ \[\]{}()&amp;lt;&amp;gt;?|`~!@#$%^&amp;amp;*-_+=,.;:\&quot;'\\]/g&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;영문만-허용&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;영문만 허용&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot; style=&quot;color: #212529; text-align: start;&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;/^[a-zA-Z]*$/&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;숫자만-허용&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;숫자만 허용&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot; style=&quot;color: #212529; text-align: start;&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;/^[0-9]*$/&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;비밀번호&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;비밀번호&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot; style=&quot;color: #212529; text-align: start;&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;//영문+숫자 조합 8~15자
/^(?=.*[a-zA-Z])(?=.*[0-9]).{8,15}$/

//영문+숫자+특수문자(!@#$%^*+=-) 조합 8~15자
/^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,15}$/&lt;/code&gt;&lt;/pre&gt;</description>
      <category>FrontEnd</category>
      <category>정규식</category>
      <author>ddiae</author>
      <guid isPermaLink="true">https://ddiaezzang.tistory.com/15</guid>
      <comments>https://ddiaezzang.tistory.com/15#entry15comment</comments>
      <pubDate>Sat, 9 Nov 2024 17:48:35 +0900</pubDate>
    </item>
    <item>
      <title>뷰포트 단위 알아보기 (vh, dvh, svh, lvh)</title>
      <link>https://ddiaezzang.tistory.com/14</link>
      <description>&lt;div style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  반응형 웹을 개발하면서 height를 100vh로 설정하는 경우 일부 화면이 모바일 브라우저 내 상단바 혹은 하단바에 의해 가려지는 문제가 있었다. vh(viewport)는 상하단바를 제외한 영역을 기준으로 한다. 따라서 다음과 같은 viewport 단위를 적절하게 사용해주어야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;511&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6M47d/btsKnhc3dhw/ycswJQZhbnULDyXA63Qzok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6M47d/btsKnhc3dhw/ycswJQZhbnULDyXA63Qzok/img.png&quot; data-alt=&quot;이미지 출처 : https://blanche-toile.com/web/large-small-and-dynamic-viewport-units&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6M47d/btsKnhc3dhw/ycswJQZhbnULDyXA63Qzok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6M47d%2FbtsKnhc3dhw%2FycswJQZhbnULDyXA63Qzok%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;511&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;511&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 출처 : https://blanche-toile.com/web/large-small-and-dynamic-viewport-units&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;vh-viewport-height&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; vh (Viewport Height)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vh는 스크린의 height를 기준으로 하여 백분위로 높이를 나타낸다. 예를들어 스크린 height가 800px인 경우 1vh는 8px이 되는 것이다. 모바일에서는 상하단바 유무와 상관없이 일정한 크기로 동작하는 정적 viewport 단위이다.&lt;/p&gt;
&lt;h2 id=&quot;dvh-dynamic-viewport-height&quot; data-ke-size=&quot;size26&quot;&gt; &lt;b&gt;dvh (Dynamic Viewport Height)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dvh는 동적 viewport height를 기준으로 하여 백분위로 높이를 나타낸다. 동적 viewport를 기준으로 하기 때문에 상하단바 등의 UI요소에 따라 높이가 유동적으로 변동된다. 반응형 웹을 고려할 때 유용하게 사용된다.&lt;/p&gt;
&lt;h2 id=&quot;svh-small-viewport-height&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; svh (Small Viewport Height)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;svh는 최소 viewport 높이를 기준으로 하여 백분위로 높이를 나타낸다. 모바일에서 상하단바가 노출되지 않는 경우에도 상하단바가 있는 경우를 고려하여 최소 viewport를 기준으로 하는 것이다. 예외적인 상황을 고려하여 어떤 경우에서도 콘텐츠가 적절하게 표시되도록 하고싶을 때 유용하다.&lt;/p&gt;
&lt;h2 id=&quot;lvh-large-viewport-height&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; lvh (Large Viewport Height)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lvh는 최대 viewport 높이를 기준으로 하여 백분위로 높이를 나타낸다. fullscreen에 가까운 상태를 가정한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;vh와의 차이점은&lt;span&gt;&amp;nbsp;&lt;/span&gt;vh는 초기 viewport를 기준으로 한 정적 단위이지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;lvh는 계속해서 최대 viewport를 반영하는 동적 단위라는 점이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;&lt;br /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100.581%; height: 169px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;단위&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;기준&lt;/td&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;뷰포트 변화 반영&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;vh&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;고정 뷰포트 높이&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;dvh&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;동적 뷰포트 높이&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;svh&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;최소 뷰포트 높이&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;lvh&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;최대 뷰포트 높이&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CSS</category>
      <category>dvh</category>
      <category>LVH</category>
      <category>svh</category>
      <category>vh</category>
      <category>Viewport</category>
      <author>ddiae</author>
      <guid isPermaLink="true">https://ddiaezzang.tistory.com/14</guid>
      <comments>https://ddiaezzang.tistory.com/14#entry14comment</comments>
      <pubDate>Wed, 30 Oct 2024 11:35:47 +0900</pubDate>
    </item>
    <item>
      <title>[Type Challenges] Tuple to Object, First of Array, Length of Tuple</title>
      <link>https://ddiaezzang.tistory.com/13</link>
      <description>&lt;h2 id=&quot;tuple-to-object&quot; style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Tuple to Object&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열(튜플)을 받아, 각 원소의 값을 key/value로 갖는 오브젝트 타입을 반환하는 타입을 구현하세요.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

type result = TupleToObject&amp;lt;typeof tuple&amp;gt; // expected { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반복문이므로 Mapped type을 사용해야 한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;T&lt;span&gt;&amp;nbsp;&lt;/span&gt;배열의 n번째 요소를&lt;span&gt;&amp;nbsp;&lt;/span&gt;K라고 하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;K가 key일 때 value도&lt;span&gt;&amp;nbsp;&lt;/span&gt;K로 정의할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;참고로 튜플을 순회할 때에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;number를 사용하면 된다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;type TupleToObject&amp;lt;T extends readonly any[]&amp;gt; = {
  [K in T[number]]: K;
}&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;h2 id=&quot;first-of-array&quot; style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; First of Array&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열(튜플)&amp;nbsp;T를 받아 첫 원소의 타입을 반환하는 제네릭&amp;nbsp;First&amp;lt;T&amp;gt;를 구현하세요.&lt;/p&gt;
&lt;pre class=&quot;fsharp&quot;&gt;&lt;code&gt;type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]

type head1 = First&amp;lt;arr1&amp;gt; // expected to be 'a'
type head2 = First&amp;lt;arr2&amp;gt; // expected to be 3&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 문제의 경우 단순하게&lt;span&gt;&amp;nbsp;&lt;/span&gt;type First&amp;lt;T extends any[]&amp;gt; = T[0]으로 정의해주었다. 더불어 빈 배열의 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;never&lt;span&gt;&amp;nbsp;&lt;/span&gt;타입을 가지도록 다음과 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;First&amp;lt;T&amp;gt;를 정의한다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;type First&amp;lt;T extends any[]&amp;gt; = T extends [] ? never : T[0];&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그 외의 답안으로는 아래와 같은 것들이 있었다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;먼저&lt;span&gt;&amp;nbsp;&lt;/span&gt;T['length']로 배열의 길이를 먼저 판단하고 타입을 지정해주는 방식이다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;type First&amp;lt;T extends any[]&amp;gt; = T['length'] extends 0 ? never : T[0]&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다음 방법은&lt;span&gt;&amp;nbsp;&lt;/span&gt;infer를 사용하는 방법이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;infer는 타입을 추론할 때 사용된다. 정확한 타입을 정의하지 않고 어떤 타입을 추론하고 싶을 때 사용한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래 정의한&lt;span&gt;&amp;nbsp;&lt;/span&gt;type First를 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;T extends [infer R, &amp;hellip;infer _]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;rarr; 이 부분은 배열의 첫번째 요소인&lt;span&gt;&amp;nbsp;&lt;/span&gt;R을 추론하는 것이며, 이 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;T는 빈배열일 수 없다. 첫번째 요소인&lt;span&gt;&amp;nbsp;&lt;/span&gt;R이 존재하기 때문!&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;hellip;infer _&lt;span&gt;&amp;nbsp;&lt;/span&gt;는 그 뒤에 오는 배열의 요소들을 나타낸다. 따라서&lt;span&gt;&amp;nbsp;&lt;/span&gt;T의 배열 첫번째 요소가 존재할 때에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;R, 빈 배열일 때에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;never&lt;span&gt;&amp;nbsp;&lt;/span&gt;타입을 가지게 되는 것이다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;type First&amp;lt;T extends any[]&amp;gt; = T extends [infer R, ...infer _] ? R : never&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;h2 id=&quot;length-of-tuple&quot; style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Length of Tuple&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열(튜플)을 받아 길이를 반환하는 제네릭&amp;nbsp;Length&amp;lt;T&amp;gt;를 구현하세요.&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']

type teslaLength = Length&amp;lt;tesla&amp;gt;  // expected 4
type spaceXLength = Length&amp;lt;spaceX&amp;gt; // expected 5&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞선 문제의 답안에서 힌트를 얻어서&lt;span&gt;&amp;nbsp;&lt;/span&gt;type Length&amp;lt;T extends any[]&amp;gt; = T['length']&lt;span&gt;&amp;nbsp;&lt;/span&gt;라고 작성하였으나,&lt;/p&gt;
&lt;p style=&quot;background-color: #fbfdfc; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;The type&lt;span&gt;&amp;nbsp;&lt;/span&gt;readonly [&amp;hellip;]' is 'readonly' and cannot be assigned to the mutable type 'any[]'.&lt;span&gt;&amp;nbsp;&lt;/span&gt;라는 에러가 나서 배열 타입 앞에&lt;span&gt;&amp;nbsp;&lt;/span&gt;readonly를 붙여주어 해결하였다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;type Length&amp;lt;T extends readonly any[]&amp;gt; = T['length']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;b&gt;왜 arr.length는 안되고 arr['length']는 될까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 문제들을 풀어나가며 궁금했던 점은 배열의 길이를 참조할 때 arr.length 형태는 사용 불가능하고 arr['length'] 형태는 사용 가능한 점이었다.&lt;br /&gt;그 이유는 TypeScript의 타입 시스템은 정적 타입 검사기로 런타임 속성을 다루지 않기 때문이라고 한다. .length는 런타임에 배열의 길이를 참조하기 떄문에 사용할 수 없으나 ['length']의 경우 객체의 속성에 접근하는 방법이기 때문에 타입 레벨에서 사용할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>TypeScript</category>
      <category>type-challenges</category>
      <author>ddiae</author>
      <guid isPermaLink="true">https://ddiaezzang.tistory.com/13</guid>
      <comments>https://ddiaezzang.tistory.com/13#entry13comment</comments>
      <pubDate>Tue, 22 Oct 2024 16:56:38 +0900</pubDate>
    </item>
    <item>
      <title>[Type Challenges] Pick, Readonly</title>
      <link>https://ddiaezzang.tistory.com/12</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Pick&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;Q. &lt;b&gt;&lt;b&gt;T에서 K프로퍼티만 선택해 새로운 오브젝트 타입을 만드는 내장 제네릭 Pick&amp;lt;T, K&amp;gt;를 이를 사용하지 않고 구현하세요.&lt;/b&gt;&lt;/b&gt; &lt;br /&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;
&lt;pre id=&quot;code_1729209163189&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyPick&amp;lt;Todo, 'title' | 'completed'&amp;gt;

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
}&lt;/code&gt;&lt;/pre&gt;
&lt;b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pick을 사용하지 않고 내장 제네릭 Pick을 구현하는 문제이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 문제를 풀기 전 Pick 타입에 대해 확인해보고자 한다. Pick 타입이란 기존 인터페이스나 객체 타입에서 필요한 속성들만 선택적으로 사용할 수 있도록 한다. &lt;b&gt;Pick&amp;lt;T, K&amp;gt;&lt;/b&gt;일 때 T는 원본 타입, K는 선택할 속성들의 Key 집합에 해당한다. 예를 들어 위 문제에서 아래와같이 type TodoTest를 정의한다고 가정해본다.&lt;/p&gt;
&lt;pre id=&quot;code_1729057728015&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type TodoTest = Pick&amp;lt;Todo, 'title' | 'completed'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 TodoTest를 정의했을 때, &lt;b&gt;todo&lt;/b&gt;의 타입을 &lt;b&gt;TodoTest&lt;/b&gt;라고 정의할 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Pick을 사용하여 반복적인 타입 선언을 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, Pick을 위 문제를 해결하기 위해서는 type MyPick을 정의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;K extends keyof T&lt;/b&gt; 형태로 T의 key를 확장하는 K를 선언해준 뒤 &lt;b&gt;Mapped type&lt;/b&gt;을 활용하여 &lt;b&gt;type MyPick&lt;/b&gt;을 아래와 같이 정의될 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1729057841292&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type MyPick&amp;lt;T, K extends keyof T&amp;gt; = { [P in K] : T[P]}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;+) Mapped type?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 타입의 속성을 변환하여 새로운 타입을 만든다. 주어진 타입의 모든 키를 반복하면서 그 키와 관련된 값을 수정하므로 &lt;i&gt;map()&lt;/i&gt; 메서드와 유사한 기능을 하며 타입을 만든다고 볼 수 있다. 중복되는 타입 선언을 방지할 수 있다. Pick 유틸 타입도 Mapped Type을 바탕으로 제작된 것이다.&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Readonly&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;Q. T의 모든 프로퍼티를 읽기 전용(재할당 불가)으로 바꾸는 내장 제네릭 'Readonly&amp;lt;T&amp;gt;'를 이를 사용하지 않고 구현하세요.&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;
&lt;pre id=&quot;code_1729127220271&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Todo {
  title: string
  description: string
}

const todo: MyReadonly&amp;lt;Todo&amp;gt; = {
  title: &quot;Hey&quot;,
  description: &quot;foobar&quot;
}

todo.title = &quot;Hello&quot; // Error: cannot reassign a readonly property
todo.description = &quot;barFoo&quot; // Error: cannot reassign a readonly property&lt;/code&gt;&lt;/pre&gt;
&lt;b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞선 Pick 문제에서 사용한 것과 동일하게 Mapped Type을 사용하여 Readonly&amp;lt;T&amp;gt;를 다음과 같이 구현할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729127173740&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type MyReadonly&amp;lt;T&amp;gt; = { readonly [P in keyof T] : T[P] }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TypeScript</category>
      <category>Mapped type</category>
      <category>Pick</category>
      <category>readonly</category>
      <category>type-challenges</category>
      <category>type-challenges-easy</category>
      <category>Typescript</category>
      <author>ddiae</author>
      <guid isPermaLink="true">https://ddiaezzang.tistory.com/12</guid>
      <comments>https://ddiaezzang.tistory.com/12#entry12comment</comments>
      <pubDate>Thu, 17 Oct 2024 09:02:08 +0900</pubDate>
    </item>
    <item>
      <title>HTTP, WebSocket, WebRTC 개념</title>
      <link>https://ddiaezzang.tistory.com/11</link>
      <description>&lt;h2 id=&quot;http&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HTTP&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저와 서버가 http를 통해 통신할 때 통신 과정은 다음과 같다
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;browser에서 server에 httpRequest(요청)을 보낸다.&lt;/li&gt;
&lt;li&gt;요청을 받은 server는 brower에 요청에 해당하는 httpResponse(응답)을 보낸다.&lt;/li&gt;
&lt;li&gt;통신이 종료된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;http 통신의 경우 server는 browser의 요청을 받아야만 응답할 수 있고 server가 주체적으로 browser에 데이터를 보낼 수 없다.&lt;/li&gt;
&lt;li&gt;server의 응답 후에는 http 통신이 종료되기 때문에 실시간으로 데이터를 업데이트해주기 위해서는 일정 시간 간격으로 계속해서 요청을 보내야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;websocket&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;WebSocket&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WebSocket(WS)은 http 방식과는 다르게 Connection Open-Close 여부에 따라 통신한다.&lt;/li&gt;
&lt;li&gt;browser와 server를 연결하는 WS이 open 상태이면 browser와 server가 자유롭게 통신 가능하다.&lt;/li&gt;
&lt;li&gt;따라서 server도 실시간으로 update되는 정보를 browser에 주체적으로 보낼 수 있게 된다.&lt;/li&gt;
&lt;li&gt;WS는 server의 모든 통신을 추적하기 위해 메모리 파워가 중요하다.&lt;/li&gt;
&lt;li&gt;user가 많을 수록 서버 비용이 많이 들고, 서버가 내려가면 통신이 불가능하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;webrtc&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;WebRTC&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;webRTC는 Server를 통하지 않고 browser간의 연결을 가능하게 한다. &amp;rarr; P2P!!&lt;span&gt;&amp;nbsp;&lt;/span&gt;따라서 JS만으로도 구현 가능하다..&lt;/li&gt;
&lt;li&gt;peer가 많을 수록 그 수만큼 Data를 업로드하고, 다운로드 해야하므로 확장성에 제약이 있다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;출처 : YouTube&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.youtube.com/watch?v=5EhsjtBE7I4&amp;amp;pp=ygUGd2ViUlRD&quot;&gt;노마드 코더 Nomad Coders &amp;lt;WebRTC? WebSockets? 5분 개념정리!&amp;gt;&lt;/a&gt;&lt;/i&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=5EhsjtBE7I4&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/IDjRm/hyXazSt7rk/IaVdCdPSqcjM7oV9u1cwMK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=86_146_1182_440,https://scrap.kakaocdn.net/dn/cxHzQR/hyXaHwfMsN/c7wehZ5JsnyhS9YnFRV6J0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=86_146_1182_440&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;WebRTC? WebSockets? 5분 개념정리!&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/5EhsjtBE7I4&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>WebRTC</category>
      <category>HTTP</category>
      <category>webrtc</category>
      <category>websocket</category>
      <author>ddiae</author>
      <guid isPermaLink="true">https://ddiaezzang.tistory.com/11</guid>
      <comments>https://ddiaezzang.tistory.com/11#entry11comment</comments>
      <pubDate>Sun, 6 Oct 2024 19:38:16 +0900</pubDate>
    </item>
    <item>
      <title>Mock Service Worker (MSW 2.0)</title>
      <link>https://ddiaezzang.tistory.com/10</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기획서가 나온 후 API 개발과 디자인이 나오기까지 프론트 엔드는 사실상 할 수 있는 일이 별로 없다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;심지어 API 개발과 디자인이 원래 지정된 기간을 지키지 못하고 밀리게 되는 일이 때때로 생기는데, 그렇다고 최종 마감 기한도 같이 밀리느냐 하면 그건 또 아니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어쨌든 마감은 점점 다가오고 가만히 있을 수는 없으니 프로젝트 구조를 설정한 후 현재 기획서를 보고 페이지를 나누고 UI는 어느 정도 틀만 잡아둔다. 더불어 API가 필요 없는 기능적인 부분 모두 구현하고도 시간이 남는다면 가짜 데이터 및 API를 만들어 요청을 보내볼 수 있는데 이 때 사용할 수 있는 것이 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;MSW(Mock Service Worker)&lt;/span&gt;다.&lt;/p&gt;
&lt;h2 id=&quot;mswmock-service-worker&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; MSW(Mock Service Worker)&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MSW는 Service Worker를 사용해 실제 API 요청을 가로채는 모킹 라이브러리이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Service Worker를 사용하는 이유는 네트워크 요청을 가로채서 실제 서버에 API 요청을 보내지 않고도 응답을 모킹하여 프론트엔드에서 테스트할 수 있게 하기 위함이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 백엔드가 아직 준비가 되지 않았을 때 유용하게 사용할 수 있는 라이브러리이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;참고로 본 포스팅은 23년도 11월에 업데이트된 MSW 2.0을 바탕으로 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;(Node.js 18 이전 버전에서는 지원되지 않음)&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&quot;설치&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;설치&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;coffeescript&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;npm install msw@latest&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;사용&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;사용&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따로 관리 규칙이 존재하는 것은 아니지만 단일 디렉토리에 Mock을 정의하는 것이 좋다. 나는 현재 진행중인 프로젝트의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;src/app/mock&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;위치에 정의해주었다.&lt;/p&gt;
&lt;h3 id=&quot;핸들러&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;핸들러&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 요청 핸들러를 관리하는 모듈을 제작해준다. 핸들러는 mock 폴더에 바로 생성해도 좋고, 핸들러의 종류가 다양하다면 handler 디렉토리를 따로 만들어서 관리해주어도 좋다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;선언하는 방식은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;import { http, HttpResponse } from 'msw';

http.get('요청주소', ({ request }) =&amp;gt; {
		...
		return HttpResponse.json(body, { status: 200 });
})&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt; &amp;nbsp;http&lt;/b&gt;&lt;br /&gt;http는 HTTP 요청을 가로채기 위한 요청 핸들러를 생성할 때 사용한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt; &amp;nbsp;HttpResponse&lt;/b&gt;&lt;br /&gt;HttpResponse는 표준&amp;nbsp;Response&amp;nbsp;클래스에서 몇가지 기능들이 추가되어있는 클래스이며 2.0 버전에서는&amp;nbsp;HttpResponse를 기본으로 사용해야 한다.&lt;br /&gt;HttpReponse에는 body와 options 객체를 전달할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;HttpResponse&lt;span&gt;&amp;nbsp;&lt;/span&gt;클래스는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;class HttpResponse {
  constructor(
    body:
      | Blob
      | ArrayBuffer
      | TypedArray
      | DateView
      | FormData
      | ReadableStream
      | URLSearchParams
      | string
      | null
      | undefined
    options?: {
      status?: number
      statusText?: string
      headers?: HeadersInit
    }
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핸들러 함수가 여러개라면 배열에 넣어준다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예시로 다음과 같이 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;handler.ts&lt;/span&gt;를 작성해주었다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;import { http, HttpResponse } from 'msw'

const signinInfo = {
    id: 'ddiaezzang',
    password: 'ddiaezzang'
};
const userInfo = {
    id: `ddiaezzang`,
    name: '짱짱'
    apiToken: 'sign-in api token !!!'
};
 
export const handlers = [
    http.post('/test/sign-in', async ({ request }) =&amp;gt; {
        const data = await request.json();
        if (data.id === signinInfo.id &amp;amp;&amp;amp; data.password === signinInfo.password) {
            return HttpResponse.json(userInfo, { status: 403 });
        } else return HttpResponse.json({ status: 403 });
    }),
    http.get('/test/userInfo', () =&amp;gt; {
        return HttpResponse.json(userInfo);
    })
];&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;서비스-워커-생성&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서비스 워커 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;browser.ts&lt;/span&gt;에 서비스 워커를 생성한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';

export const worker = setupWorker(...handlers);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;서비스-워커-활성화&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서비스 워커 활성화&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;worker.start()&lt;/span&gt;를 사용해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;App.tsx&lt;/span&gt;에서 서비스 워커를 활성화하고 모킹을 시작할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;나는 Vite를 사용하였기에 다음과 같이 작성해주었다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;if (import.meta.env.DEV) {
    const enableMocking = async function () {
        const { worker } = await import('./mock/browser');
        return worker.start({
            onUnhandledRequest: 'bypass'
        });
    };
    await enableMocking();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;실행&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실행&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 실행 시 Console에 다음과 같은 문구가 뜨면 MSW가 성공적으로 활성화되었다는 의미이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;25&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPpeOJ/btsI9se8KD9/4jhePl76pFkc9zMo9Z0JU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPpeOJ/btsI9se8KD9/4jhePl76pFkc9zMo9Z0JU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPpeOJ/btsI9se8KD9/4jhePl76pFkc9zMo9Z0JU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPpeOJ%2FbtsI9se8KD9%2F4jhePl76pFkc9zMo9Z0JU1%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;441&quot; height=&quot;25&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;25&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 id=&quot;후기&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;b&gt; 후기&lt;/b&gt;&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MSW를 사용해 백엔드 작업 이전에 구조를 잡아볼 수도 있지만, 특히 에러케이스 또는 데이터에 따른 처리를 해주어야 할 경우 백엔드에 테스트 데이터 요청 전에 미리 실제 서버로 가는 API 요청을 가로채 사용해볼 수 있다는 이점이 있었다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또 실제 API 요청 형식을 그대로 사용해 테스트할 수 있어 추후에 코드를 대거 수정해야하는 일도 없었다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이전에도 MSW를 사용해본 적 있지만, 2.0 버전의 경우 더 쉽고 간단하게 사용할 수 있어서 편리했다. 다음 개인 프로젝트 작업 시에 꼭 다시 활용해보고자 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 출처 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://mswjs.io/&quot;&gt;Mock Service Worker&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>FrontEnd</category>
      <category>mock-service-worker</category>
      <category>MSW</category>
      <category>msw2.0</category>
      <author>ddiae</author>
      <guid isPermaLink="true">https://ddiaezzang.tistory.com/10</guid>
      <comments>https://ddiaezzang.tistory.com/10#entry10comment</comments>
      <pubDate>Wed, 21 Aug 2024 10:58:58 +0900</pubDate>
    </item>
    <item>
      <title>[TanStack Query] QueryFactory로 API 관리하기</title>
      <link>https://ddiaezzang.tistory.com/9</link>
      <description>&lt;div style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FSD 구조를 사용할 때 TanStack Query를 더 효율적으로 사용하기 위해 QueryFactory 형식으로 API요청 관리해주면 좋다는 FSD 공식문서 내용을 보고 queryFactory를 적용 해보기로 한다. qeuryFactory는 queryOptions로 구성한다.&lt;/p&gt;
&lt;h2 id=&quot;queryoptions&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; queryOptions&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;queryKey와 queryFn을 재설정 없이 공유하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;queryOptions를 사용할 수 있다. 한 곳에서 공통 옵션을 정의해주어 반복 설정해주지 않아도 된다는 이점이 있다.&lt;/p&gt;
&lt;pre class=&quot;moonscript&quot;&gt;&lt;code&gt;const getLearnerStatus = (learnerId: string) =&amp;gt;
    queryOptions({
        queryKey: ['learnerStatus', learnerId],
        queryFn: () =&amp;gt; client.get('...'),
        staleTime: 1000,
        ...
    });&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;queryoptions&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;mutationOptions&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 mutation option을 따로 지정해주는 헬퍼 함수는 없었다. 그래서 처음에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;setMutationDefaults을 사용하고자 했다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;queryClient.setMutationDefaults(['mutationKey'], { ...mutationOptions })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;queryClient에&lt;span&gt;&amp;nbsp;&lt;/span&gt;setMutationDefaults&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 사용해 mutationKey와 mutationOption을 각각 넣어주면 key만으로 해당 옵션을 불러와 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편리하기는 했지만 목적은&lt;span&gt;&amp;nbsp;&lt;/span&gt;queryFactory를 만들기 위함이었기에 통일성을 주고자&lt;span&gt;&amp;nbsp;&lt;/span&gt;queryOptions와 똑같이 기능하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;mutationOptions를 정의해주었다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;export function mutationOptions&amp;lt;TData = unknown, TError = DefaultError, TVariables = void, TContext = unknown&amp;gt;(
    options: UseMutationOptions&amp;lt;TData, TError, TVariables, TContext&amp;gt;
): UseMutationOptions&amp;lt;TData, TError, TVariables, TContext&amp;gt; {
    return options;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mutation의 옵션을 넣어&lt;span&gt;&amp;nbsp;&lt;/span&gt;queryOptions와 똑같이 작성해주면 된다.&lt;/p&gt;
&lt;pre class=&quot;moonscript&quot;&gt;&lt;code&gt;const postLearnerStatus = () =&amp;gt;
    queryOptions({
        mutationKey: ['postLearnerStatus'],
        mutationFn: (learnerId:string) =&amp;gt; client.get('...', {learnerId}),
        onSuccess,
        onError,
        ...
    });&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;queryoptions&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;queryFactory&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 앞서 작성한&lt;span&gt;&amp;nbsp;&lt;/span&gt;queryOptions,&lt;span&gt;&amp;nbsp;&lt;/span&gt;mutationOptions를 활용해 하나의 queryFactory로 관리할 수 있다.&lt;/p&gt;
&lt;h4 id=&quot;queryfactory-작성&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;queryFactory 작성&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;moonscript&quot;&gt;&lt;code&gt;export const learnerAPIQueries = {
    getLearnerStatus: (learnerId: string) =&amp;gt;
        queryOptions({
            queryKey: ['learnerStatus', learnerId],
			      queryFn: () =&amp;gt; client.get('...'),
			      staleTime: 1000,
        }),
    postLearnerStatus = () =&amp;gt;
		    queryOptions({
		        mutationKey: ['postLearnerStatus'],
		        mutationFn: (learnerId:string) =&amp;gt; client.get('...', {learnerId})
		    })
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;queryfactory-사용&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;queryFactory 사용&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;nimrod&quot;&gt;&lt;code&gt;const { data } = useQuery(learnerAPIQueries.getLernerStatus());
const { mutate } = useMutation(learnerAPIQueries.postLearnerStatus())
// 개별 옵션이 필요할 경우 다음과 같이 작성한다.
const { mutate } = useMutation({...learnerAPIQueries.postLearnerStatus(), onSuccess : () =&amp;gt; {...}})&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>React</category>
      <category>mutationoptions</category>
      <category>queryfactory</category>
      <category>queryoptions</category>
      <category>tanstack-query</category>
      <author>ddiae</author>
      <guid isPermaLink="true">https://ddiaezzang.tistory.com/9</guid>
      <comments>https://ddiaezzang.tistory.com/9#entry9comment</comments>
      <pubDate>Fri, 16 Aug 2024 16:54:51 +0900</pubDate>
    </item>
  </channel>
</rss>