<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>hsunny study blog</title>
    <link>https://withhsunny.tistory.com/</link>
    <description>공부합시다!</description>
    <language>ko</language>
    <pubDate>Tue, 12 May 2026 23:25:59 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>헤써니</managingEditor>
    <image>
      <title>hsunny study blog</title>
      <url>https://tistory1.daumcdn.net/tistory/1617134/attach/185676f4bc1b4515b03be774fb33f6d9</url>
      <link>https://withhsunny.tistory.com</link>
    </image>
    <item>
      <title>[Angular] 컴포넌트가 누적되어 메모리에서 제거되지 않는 경우들 (즉, 메모리 누수)</title>
      <link>https://withhsunny.tistory.com/114</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;규모가 큰 서비스에서는 메모리 해제가 중요하다. 메모리가 쌓이다보면 앱이 느려지는 문제가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Angular로 개발을 하면서, 메모리의 누수를 확인하기 위해 Google Development Tool 안에 있는 Memory 탭과 Performace monitor를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Angular의 경우 이 누수를 피하기 위해 어떤 것들을 봐야하는지 살펴보자.&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;1. Component 데코레이터의 provider에 직접 추가한 service가 destory 되지 않은 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 서비스라면 Component 단에서 서비스를 주입했다면, Component의 생명주기를 따라간다. 하지만 예외가 있다.&lt;/p&gt;
&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_1711466226016&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root' // ****
})
export class MyService {
  constructor() { }

  sayHello() {
    console.log('Hello, Angular!');
  }
}&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;MyService 클래스는 root 단에 주입한 서비스다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점을 간과하고, Component에서 주입을 추가하면..&lt;/p&gt;
&lt;pre id=&quot;code_1711466562394&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Component } from '@angular/core';
import { MyService } from './my.service';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  providers: [MyService] // ****
})
export class MyComponent {
  constructor(private myService: MyService) {
    myService.sayHello();
  }
}&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;Component 생명주기를 따라가지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로 인해 이 서비스를 주입한 Component가 계속해서 살아있고, 심지어 컴포넌트에 들어오는(Init) 수만큼 컴포넌트가 쌓이는 문제가 발생한다. 컴포넌트만 쌓이냐? 아니다. 컴포넌트에서 주입한 서비스들도 덩달아 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 인터넷이 끊겼다가 다시 연결되는 로직이 서비스에 있다면, 컴포넌트에 들어온 횟수만큼 API 통신을 하게 된다.&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;1. root 단에서 주입하지 않거나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. root 단에서 주입할거면, component 단에서 주입하지 않거나&lt;/p&gt;
&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;2. Component의 nativeElement에 접근하여, DOM을 직접 수정한 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Angular에서는 Renderer2 를 사용하여 DOM을 수정해주어야 한다. 그러지 않으면 Angular의 생명주기대로 관리가 되지 않는다.&lt;/p&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 id=&quot;code_1711466822937&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ElementRef } from '@angular/core';

//...

this.elementRef.nativeElement.classList.remove('is-selected');
this.elementRef.nativeElement.classList.add('is-selected');&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;pre id=&quot;code_1711466892581&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ElementRef, Renderer2 } from '@angular/core';

//...

this.renderer.removeClass(this.elementRef.nativeElement, 'is-selected');
this.renderer.addClass(this.elementRef.nativeElement, 'is-selected');&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;이외에도 여러 케이스가 있지만, 최근에 경험했던 케이스들을 공유해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가에게는 도움이 되길 바라며!&lt;/p&gt;</description>
      <category>programming/ERROR</category>
      <author>헤써니</author>
      <guid isPermaLink="true">https://withhsunny.tistory.com/114</guid>
      <comments>https://withhsunny.tistory.com/114#entry114comment</comments>
      <pubDate>Wed, 27 Mar 2024 00:36:20 +0900</pubDate>
    </item>
    <item>
      <title>Glob을 사용해서 원하는 확장자 파일 안에 있는 문자열들을 치환하기 (+@ 정규식)</title>
      <link>https://withhsunny.tistory.com/113</link>
      <description>&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;h3 data-ke-size=&quot;size23&quot;&gt;작업을 위한 선행과정으로 before, after를 구분할 수 있는 JSON 파일을 만든다.&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-02-19 오후 9.09.15.png&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5H1jE/btrZKo2Ayfk/MKcfk8U5vyP3RJ8sp8Hj8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5H1jE/btrZKo2Ayfk/MKcfk8U5vyP3RJ8sp8Hj8K/img.png&quot; data-alt=&quot;포스팅을 위한 예시 데이터&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5H1jE/btrZKo2Ayfk/MKcfk8U5vyP3RJ8sp8Hj8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5H1jE%2FbtrZKo2Ayfk%2FMKcfk8U5vyP3RJ8sp8Hj8K%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;335&quot; height=&quot;223&quot; data-filename=&quot;스크린샷 2023-02-19 오후 9.09.15.png&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;372&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;나의 경우 before을 path 로, after를 to-be로 이름지었다.&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;원하는 확장자의 파일만 수정할 수 있도록 'glob' 라이브러리를 사용한다.&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;glob이 실제 프로젝트에 쓰인다면 package.json에 목적에 맞게 넣으면 되지만, 나의 경우에는 치환 작업만 진행할 것이라 작업을 완료하고 삭제하였다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676808808159&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm i glob&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;내가 변경할 내용을 담은 스크립트를 작성한다.&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;pre id=&quot;code_1676810283471&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const path = require('path');
const fs = require('fs');
const glob = require('glob');

function convertString() {
  function explodeFolder() { } // 타겟 폴더 안에 타겟 확장자 파일을 탐색함
  
  function convertTarget() { } // 실제 변경을 진행하는 곳
  
  function generateFileIncludeAffectedFileList() { } // 변경내역을 확인하려는 목적으로 파일을 생성함
}

function getBaseFile() { } // 가장 처음에 만든 JSON 파일을 불러오기 위해 사용

function convertToRegPattern() { } // 밑에서 설명

function addWrapRegPattern() { } // 밑에서 설명

function replacePattern() { // 개인 작업 특이사항을 진행하기 위해 작성
	function replacer() { }  
}

function modifyFile() { } // 파일을 수정함**


exports.changeString = convertString; // 스크립트 실행을 위해 export&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;size18&quot;&gt;일부 과정을 상세 소개해보자면..&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색을 시작할 경로를 지정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1676808953377&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const rootDir = path.join(__dirname, '../');&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;pre id=&quot;code_1676809018451&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fs.readdirSync(rootDir) // 폴더 리스트를 배열 형태로 반환 (동기)
      .filter(folder =&amp;gt; folder === 'projects' || folder === 'src') // projects, src 폴더만 탐색
      .forEach((value) =&amp;gt; {
        explodeFolder(rootDir + value, version); // 폴더 내에 있는 파일을 모두 탐색
      });&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;explodeFolder() 메소드로 들어온 값을 확인하여, 파일인 경우 원하는 변경 작업을 진행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1676809263583&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function explodeFolder(path, version) {
  let dirList = fs.readdirSync(path);
  for (const file of dirList) {
    let pullPath = path + '/' + file;
    const isDir = fs.statSync(pullPath).isDirectory();

    if (isDir) {
      explodeFolder(pullPath, version); // 폴더인 경우 재귀
      const fileList = glob.sync(pullPath + '/' + '*.ts'); // typescript 파일들만 배열 형태로 반환
      fileList.forEach(file =&amp;gt; convertTarget(file, version)); // 문자열 변경 진행
    }
  }
}&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;convertTarget() 메소드에서 문자열 변경을 진행하고, 변경사항을 반영한다.&lt;/p&gt;
&lt;pre id=&quot;code_1676809393009&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sheetData = getBaseFile(); // 처음에 만들어둔 JSON 파일

...

function convertTarget(pullPath, version) {
  let fileContent = fs.readFileSync(pullPath, 'utf-8'); // 파일 내용 반환 (동기)

  sheetData = sheetData.reduce((acc, v, i) =&amp;gt; {
    const regPattern = convertToRegPattern(v.path); // **
    const matchList = getMatchedString(fileContent, regPattern); // ** 변경된 파일을 요소로 포함하는 배열을 반환
   
    ...

    fileContent = replacePattern(fileContent, regPattern, version);

    acc.push(v);
    return acc;
  }, []);

  modifyFile(fileContent, pullPath); // fs.writeFileSync(filePath, fileContent);
}&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;h3 data-ke-size=&quot;size23&quot;&gt;Regular expression()&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;pre id=&quot;code_1676809866249&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function convertToRegPattern(path) {
  let fullPattern = path;
  const regex = /:[a-z]+[_]?[a-z]+/g;
  const searchRegex = '(&quot;\\s?\\+\\s?|\\$(?=\\{)\\{)[\\w.]+(\\s?\\+\\s?&quot;|})';
  fullPattern = fullPattern.replace(regex, searchRegex);
  fullPattern = addWrapRegPattern(fullPattern);
  return new RegExp(fullPattern, 'g');
}

function addWrapRegPattern(regPattern) {
  return `(path:\\s?)?(\'|\&quot;|\`|\})\\s?(${regPattern}[\'\&quot;\`\{\?])`;
}&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;convertToRegPattern()&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;span style=&quot;background-color: #ffc1c8;&quot;&gt;:p_target&lt;/span&gt;, &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;:id&lt;/span&gt;, &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;${p.id}&lt;/span&gt;, &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;${target}&lt;/span&gt;, &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&quot; + target + &quot;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;+: 한 번 이상 나올 수 있음을 의미&lt;/li&gt;
&lt;li&gt;\$(?=\{): 무조건 { 앞에 $가 있어야 함을 의미&lt;/li&gt;
&lt;li&gt;\w. : 모든 문자열을 의미 (숫자 제외)&lt;/li&gt;
&lt;li&gt;\: 특수문자의 경우 앞에 \를 붙여 특수문자임을 알려줌
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;3&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;/li&gt;
&lt;li&gt;\s: 공백문자를 의미&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;addWrapRegPattern()&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676810847392&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return `(path:\s?)?(\'|\&quot;|\`|\})\s?(${regPattern}[\'\&quot;\`\{\?])`;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;3&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;()로 묶은 것: 하나의 그룹으로 취급 (순서대로 $1, $2, $3,,,)&lt;/li&gt;
&lt;li&gt;[]: 내부에 있는 문자는 각각 대응
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 케이스에 대응하기 위한 문자열을 넣을 때.. () 안에서는 or(|)이 필요하지만 [] 안에서는 필요 없음&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addWrapRegPattern()에서 그룹화된 결과물은 아래와 같은 방식으로 처리하는데 사용했다.&lt;/p&gt;
&lt;pre id=&quot;code_1676811213809&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function replacer(match, p1, p2, p3) {
  // 'path:'를 포함하는 경우 'path:'가 p1이 되고, 없는 경우 p1은 undefined
  if (p1) {
    return `${p1}${p2}${p3}`;
  } else {
    return `${p2}/v1${p3}`;
  }
}&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;나의 경우는 걸프를 사용하고 있어, package.json에 아래와 같이 스크립트를 추가했다.&lt;/p&gt;
&lt;pre id=&quot;code_1676811449012&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
...
&quot;change-string&quot;: &quot;gulp changeString --gulpfile build-config/gulpfile.js&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1676811478478&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm run change-tring&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고사이트&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규식을 만드는데 정말 큰 도움이 된 사이트! &lt;a href=&quot;https://regexr.com/&quot;&gt;https://regexr.com/&lt;/a&gt;&amp;nbsp;&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;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Eyk0E/btrZJdgah0c/bf7CoNa0VSs6ldZSfDL2ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Eyk0E/btrZJdgah0c/bf7CoNa0VSs6ldZSfDL2ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Eyk0E/btrZJdgah0c/bf7CoNa0VSs6ldZSfDL2ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEyk0E%2FbtrZJdgah0c%2Fbf7CoNa0VSs6ldZSfDL2ok%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;393&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;Tests 활용을 많이 했다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&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>
      <author>헤써니</author>
      <guid isPermaLink="true">https://withhsunny.tistory.com/113</guid>
      <comments>https://withhsunny.tistory.com/113#entry113comment</comments>
      <pubDate>Sun, 19 Feb 2023 22:01:32 +0900</pubDate>
    </item>
    <item>
      <title>toPromise? firstValueFrom? lastValueFrom?</title>
      <link>https://withhsunny.tistory.com/112</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;RxJS 7으로 가면서, toPromise() 메소드에 변경점이 생기고, 대체 메소드들이 나왔다.&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;일단 toPromise() 메소드는 Observable을 Promise와 같이 동작하도록 해주는&amp;nbsp; RxJS Operator다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 58px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 18px;&quot;&gt;&lt;b&gt;Observable&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;b&gt;Promise&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;공통점&lt;/td&gt;
&lt;td style=&quot;width: 75%; height: 20px;&quot; colspan=&quot;2&quot;&gt;비동기를 처리하는 방식으로, 시간이 지남에 따라 값을 방출한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;차이점&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;값을 생성하지 않거나, 하나 이상의 값을 방출할 수 있다.&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;resolve&lt;/span&gt;될 때, 그때의 값만 한번 방출할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observable이 값을 방출하지 않는 사실을 반영하기 위해, RxJS 7에서는 toPromise() 메소드의 반환 형태를 Promise&amp;lt;T&amp;gt;에서 Promise&amp;lt;T | undefined&amp;gt;로 변경했다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;RxJS 6 toPromise(): Promise&amp;lt;T&amp;gt;&amp;nbsp; &amp;rarr; RxJS 7 toPromise(): Promise&amp;lt;T | undefined&amp;gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 기존 toPromise() 메소드는 Observable이 시간이 지남에 따라 여러값을 생성할 수 있는 것에 대응되지 않았다. Promise로 변경될 때 첫번째 값 또는 마지막 값중에 선택해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모든 문제를 해결하기 위해 Promise로 변환하기 위한 두가지 새로운 함수를 도입하였고, 이 두 함수가 lastValueFrom과 firstValueFrom 이다. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;toPromise() 메소드는 추후 없어질 예정&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 214px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;lastValueFrom&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;firstValueFrom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 118px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 118px;&quot;&gt;Observable이 완료되어야 하는가?&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 118px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 118px;&quot;&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;사용하는 값&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;마지막 값&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;첫번째 값&lt;br /&gt;* 값을 받고 즉시 구독을 취소한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;완료되었을 때 값이 없으면?&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;&lt;b&gt;reject&lt;/b&gt; / EmptyError 발생&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;&lt;b&gt;reject&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;/ EmptyError 발생&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 18px;&quot;&gt;Observable에 오류가 발생했으면?&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 66.6666%;&quot; colspan=&quot;2&quot;&gt;&lt;b&gt;reject&lt;/b&gt; / Error 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;주의사항&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Observable이 끝난다는 걸 확실히 알 때만 사용할 것&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Observable이 적어도 하나의 값을 방출하거나, 끝난다는 것을 알 때만 사용할 것 &lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;주의사항에 어긋나는 경우, 중단된 Promise로 끝나면서, 잠재적으로 비동기 함수의 모든 상태가 메모리에 남는다.&lt;br /&gt;이 상황을 막기 위해 timeout, take, takeWhie, takeUntil 등을 사용해야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료되었을 때 값이 없는걸로 EmptyError 발생하는 것이 싫다면, 두번째 인자로 기본값을 넣어주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1675472132306&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { firstValueFrom, EMPTY } from 'rxjs';

const result = await firstValueFrom(EMPTY, { defaultValue: 0 });
console.log(result);

// Expected output:
// 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observable이 방출된 값이 없을 때 종료되면 DefaultValue로 지정한 값이 방출되고, EmptyError가 발생하지 않는다.&lt;/p&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;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://rxjs.dev/deprecations/to-promise&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://rxjs.dev/deprecations/to-promise&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&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>programming/RxJS</category>
      <author>헤써니</author>
      <guid isPermaLink="true">https://withhsunny.tistory.com/112</guid>
      <comments>https://withhsunny.tistory.com/112#entry112comment</comments>
      <pubDate>Sat, 4 Feb 2023 10:02:26 +0900</pubDate>
    </item>
    <item>
      <title>InputEvent 알아보기</title>
      <link>https://withhsunny.tistory.com/111</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa;&quot;&gt;웹기반 텍스트 에디터를 만들 때 브라우저마다 다른 스펙에 대응이 어려운점, 편집 핸들링 어려운 점, 사용자마다 사용이 다양한 점 등을 해소하기 위해 나왔다고 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;자바스크립트 코드도 많이 써야하는데 브라우저마다 대응을 해야겠냐! 조금이라도 쉽게 할수 있도록 하자! 이런 느낌&lt;/i&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;InputEvent는 편집가능한 콘텐츠의 내용이 변경되었을 때 발생하는 이벤트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 편집이 가능한 콘텐츠는&lt;/p&gt;
&lt;pre id=&quot;code_1675349150750&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;input type=&quot;text&quot; id=&quot;input-box&quot; /&amp;gt;
&amp;lt;textarea  id=&quot;text-area&quot;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;편집가능한 콘텐츠이기 때문에 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;contenteditable 속성이 true인 것도 물론 포함이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675349203597&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;   &amp;lt;p contenteditable=&quot;true&quot;&amp;gt;
      contenteditable
    &amp;lt;/p&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;Feb-02-2023 23-48-29.gif&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eqeHyC/btrXVkN5s2g/jdH0fKmkx5H8whkqGknv5K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eqeHyC/btrXVkN5s2g/jdH0fKmkx5H8whkqGknv5K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eqeHyC/btrXVkN5s2g/jdH0fKmkx5H8whkqGknv5K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/eqeHyC/btrXVkN5s2g/jdH0fKmkx5H8whkqGknv5K/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;326&quot; height=&quot;100&quot; data-filename=&quot;Feb-02-2023 23-48-29.gif&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;InputEvent는 UIEvent와 Event를 상속받는다.&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;table style=&quot;border-collapse: collapse; width: 98.8374%; height: 72px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 350.312px; height: 18px;&quot;&gt;InputEvent.data&lt;/td&gt;
&lt;td style=&quot;width: 350.312px; height: 18px;&quot;&gt;삽입된 글자를 반환한다.&lt;br /&gt;문자가 삭제된 경우는 null이다.&lt;/td&gt;
&lt;td style=&quot;width: 350.375px; height: 18px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Feb-02-2023 23-58-47.gif&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k26sM/btrXXzDp4TO/kkE062Lj2ZPGwGLibtTia1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k26sM/btrXXzDp4TO/kkE062Lj2ZPGwGLibtTia1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k26sM/btrXXzDp4TO/kkE062Lj2ZPGwGLibtTia1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/k26sM/btrXXzDp4TO/kkE062Lj2ZPGwGLibtTia1/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;404&quot; height=&quot;166&quot; data-filename=&quot;Feb-02-2023 23-58-47.gif&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 350.312px; height: 18px;&quot;&gt;InputEvent.dataTransfer&lt;/td&gt;
&lt;td style=&quot;width: 350.312px; height: 18px;&quot;&gt;리치텍스트나 일반 텍스트 데이터에 대한 정보를 반환한다.&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;td style=&quot;width: 350.375px; height: 18px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Feb-03-2023 00-06-47.gif&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgvBVF/btrXXgKGQdh/XeUhfOvbBiIju6dwYn5hnk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgvBVF/btrXXgKGQdh/XeUhfOvbBiIju6dwYn5hnk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgvBVF/btrXXgKGQdh/XeUhfOvbBiIju6dwYn5hnk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dgvBVF/btrXXgKGQdh/XeUhfOvbBiIju6dwYn5hnk/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;954&quot; height=&quot;358&quot; data-filename=&quot;Feb-03-2023 00-06-47.gif&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 350.312px; height: 18px;&quot;&gt;InputEvent.inputType&lt;/td&gt;
&lt;td style=&quot;width: 350.312px; height: 18px;&quot;&gt;만들어진 입력 유형을 포함한다.&lt;br /&gt;삽입: insertText&lt;br /&gt;삭제: deleteContentBackward&lt;br /&gt;붙여넣기: insertFromPaste&lt;br /&gt;굵게: formatBold&lt;br /&gt;더 다양한 타입은 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType#result&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이곳&lt;/a&gt; 예제를 실행해본다.&lt;/td&gt;
&lt;td style=&quot;width: 350.375px; height: 18px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Feb-03-2023 00-10-34.gif&quot; data-origin-width=&quot;434&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zYk7D/btrXV1AxkuV/5K1LvaTVH99JkhjVDei980/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zYk7D/btrXV1AxkuV/5K1LvaTVH99JkhjVDei980/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zYk7D/btrXV1AxkuV/5K1LvaTVH99JkhjVDei980/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/zYk7D/btrXV1AxkuV/5K1LvaTVH99JkhjVDei980/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;434&quot; height=&quot;240&quot; data-filename=&quot;Feb-03-2023 00-10-34.gif&quot; data-origin-width=&quot;434&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 350.312px; height: 18px;&quot;&gt;InputEvent.isComposing&lt;/td&gt;
&lt;td style=&quot;width: 350.312px; height: 18px;&quot;&gt;구성시작 후와 구성 종료 전에 이벤트가 시작되는 여부를 나타내는 Boolean형의 데이터를 반환한다.&lt;br /&gt;&lt;br /&gt;*글을 작성하는 현재(2023.02.03)&lt;br /&gt;사파리: 실험중&lt;br /&gt;사파리 on iOS: 지원안함&lt;/td&gt;
&lt;td style=&quot;width: 350.375px; height: 18px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;사용자가 입력한 값을 읽는 방법으로 많이 사용하는 것이 KeyBoardEvent 일 것인데, 만든 이의 의도처럼 InputEvent를 적극 활용한다면 더 수월하게 입력값을 다룰 수 있을 것이다. &lt;i&gt;예를 들어 mac 시스템 명령을 통한 이모지 입력... (ctrl + cmd + space ..) &lt;span style=&quot;color: #9d9d9d;&quot;&gt;이 포스팅의 계기..&lt;/span&gt;&lt;/i&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;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://w3c.github.io/input-events&quot;&gt;https://w3c.github.io/input-events&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675352181533&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;Input Events&quot; data-og-description=&quot;Acknowledgements Thanks to: Michael Aufreiter, Adrian Bateman, Oliver Buchtala, Robin Berjon, Enrica Casucci, Bo Cupp, Domenic Denicola, Emil Eklund, Olivier Forget, Aryeh Gregor, Marijn Haverbeke, Yoshifumi Inoue, Koji Ishii, Gary Kacmarcik, Ian Kilpatric&quot; data-og-host=&quot;w3c.github.io&quot; data-og-source-url=&quot;https://w3c.github.io/input-events&quot; data-og-url=&quot;https://w3c.github.io/input-events/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://w3c.github.io/input-events&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://w3c.github.io/input-events&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Input Events&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Acknowledgements Thanks to: Michael Aufreiter, Adrian Bateman, Oliver Buchtala, Robin Berjon, Enrica Casucci, Bo Cupp, Domenic Denicola, Emil Eklund, Olivier Forget, Aryeh Gregor, Marijn Haverbeke, Yoshifumi Inoue, Koji Ishii, Gary Kacmarcik, Ian Kilpatric&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;w3c.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/InputEvent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/InputEvent&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675352194312&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;InputEvent - Web APIs | MDN&quot; data-og-description=&quot;The InputEvent interface represents an event notifying the user of editable content changes.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/InputEvent&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/InputEvent&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Chf2i/hyRt5a9M2h/hCAnRVlwe07i8MqldHN8pk/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/en-US/docs/Web/API/InputEvent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/InputEvent&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Chf2i/hyRt5a9M2h/hCAnRVlwe07i8MqldHN8pk/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;InputEvent - Web APIs | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The InputEvent interface represents an event notifying the user of editable content changes.&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;/div&gt;
&lt;/div&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>programming/javascript</category>
      <author>헤써니</author>
      <guid isPermaLink="true">https://withhsunny.tistory.com/111</guid>
      <comments>https://withhsunny.tistory.com/111#entry111comment</comments>
      <pubDate>Fri, 3 Feb 2023 00:36:48 +0900</pubDate>
    </item>
    <item>
      <title>Firebase 무료 웹호스팅 사용하기</title>
      <link>https://withhsunny.tistory.com/110</link>
      <description>&lt;p data-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;a href=&quot;https://console.firebase.google.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://console.firebase.google.com/&lt;/a&gt; (Firebase Console) 로 이동한다. 로그인 필요&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 프로젝트를 생성한다.&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;832&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VCXuV/btrWzP1F0YW/5YKQKzhJSfreMPKxWkulmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VCXuV/btrWzP1F0YW/5YKQKzhJSfreMPKxWkulmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VCXuV/btrWzP1F0YW/5YKQKzhJSfreMPKxWkulmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVCXuV%2FbtrWzP1F0YW%2F5YKQKzhJSfreMPKxWkulmk%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;382&quot; height=&quot;318&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 우측 '빌드' 하위 메뉴에 있는 Hosting을 누르고, 나오는 화면에서 '시작하기' 버튼을 누른다.&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;1580&quot; data-origin-height=&quot;1146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C8X3G/btrWuPJkjAO/Th86WvgGRv2mflCYdSqfjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C8X3G/btrWuPJkjAO/Th86WvgGRv2mflCYdSqfjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C8X3G/btrWuPJkjAO/Th86WvgGRv2mflCYdSqfjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC8X3G%2FbtrWuPJkjAO%2FTh86WvgGRv2mflCYdSqfjk%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;402&quot; height=&quot;292&quot; data-origin-width=&quot;1580&quot; data-origin-height=&quot;1146&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. Firebase 호스팅 설정에 따라 진행한다.&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;h3 data-ke-size=&quot;size23&quot;&gt;Firebase 호스팅 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 다음 npm 명령어를 실행하여 CLI를 설치하거나 최신 CLI 버전으로 업데이트한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1673967915974&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install -g firebase-tools&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 웹 앱의 루트 디렉터리로 이동하거나, 루트 디렉터리를 만든 후 아래 명령어를 실행한다.&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1673968016353&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;firebase login&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 로그인을 해두었다면, '&lt;i&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Already logged in as {로그인한 메일주소}&lt;/span&gt;&lt;/i&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;/ul&gt;
&lt;pre id=&quot;code_1673968159356&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;firebase init&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;1115&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cu0lvu/btrWu6qNGoB/b7qri6rfgL0M5KEbdvt2c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu0lvu/btrWu6qNGoB/b7qri6rfgL0M5KEbdvt2c0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu0lvu/btrWu6qNGoB/b7qri6rfgL0M5KEbdvt2c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu0lvu%2FbtrWu6qNGoB%2Fb7qri6rfgL0M5KEbdvt2c0%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;562&quot; height=&quot;217&quot; data-origin-width=&quot;1115&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;&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;font-family: 'Noto Serif KR';&quot;&gt;Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices.&amp;nbsp;&lt;/span&gt; 에 대한 답으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; background-color: #dddddd;&quot;&gt;Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diGNkR/btrWw8unmvy/peKaMUVTAVT1NKoHhQTPRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diGNkR/btrWw8unmvy/peKaMUVTAVT1NKoHhQTPRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diGNkR/btrWw8unmvy/peKaMUVTAVT1NKoHhQTPRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiGNkR%2FbtrWw8unmvy%2FpeKaMUVTAVT1NKoHhQTPRK%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;417&quot; height=&quot;118&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;146&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;그 이후 호스팅을 위해 어떤 폴더명을 사용할 것인지 물어보는데, 그냥 enter를 친다면, 기본 설정인 'public'폴더가 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;37&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw1ZZh/btrWwLzsQcX/zCYcbkTsfUeQH575GkteI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw1ZZh/btrWwLzsQcX/zCYcbkTsfUeQH575GkteI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw1ZZh/btrWwLzsQcX/zCYcbkTsfUeQH575GkteI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw1ZZh%2FbtrWwLzsQcX%2FzCYcbkTsfUeQH575GkteI1%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;416&quot; height=&quot;35&quot; data-origin-width=&quot;440&quot; data-origin-height=&quot;37&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배포할 파일들을 'public'폴더에 넣어야 함&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-origin-width=&quot;650&quot; data-origin-height=&quot;89&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8NCnu/btrWuOKsjhB/IHiAyYxGE0pAfd7gT1ZiXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8NCnu/btrWuOKsjhB/IHiAyYxGE0pAfd7gT1ZiXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8NCnu/btrWuOKsjhB/IHiAyYxGE0pAfd7gT1ZiXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8NCnu%2FbtrWuOKsjhB%2FIHiAyYxGE0pAfd7gT1ZiXK%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;431&quot; height=&quot;59&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;89&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;이렇게 답을 마치면, 3개의 파일이 생성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kSYKd/btrWwLlTwoN/3ylJQMAXWCKjOK8UZeFib1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kSYKd/btrWwLlTwoN/3ylJQMAXWCKjOK8UZeFib1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kSYKd/btrWwLlTwoN/3ylJQMAXWCKjOK8UZeFib1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkSYKd%2FbtrWwLlTwoN%2F3ylJQMAXWCKjOK8UZeFib1%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;333&quot; height=&quot;89&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;124&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;b&gt;3. 웹 빌드결과물을 위에서 설정한 공개 폴더에 넣는다.&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;375&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H2q8G/btrWwpwC656/fswOMol9h2t2sFlq9FYNXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H2q8G/btrWwpwC656/fswOMol9h2t2sFlq9FYNXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H2q8G/btrWwpwC656/fswOMol9h2t2sFlq9FYNXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH2q8G%2FbtrWwpwC656%2FfswOMol9h2t2sFlq9FYNXK%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;299&quot; height=&quot;185&quot; data-origin-width=&quot;375&quot; data-origin-height=&quot;232&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;4. 앱의 루트 디렉터리에서 아래 명령어를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1673968855762&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;firebase deploy&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;605&quot; data-origin-height=&quot;479&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OxBcK/btrWzRkSLH0/zKop9tbSutz1ink6JVTzfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OxBcK/btrWzRkSLH0/zKop9tbSutz1ink6JVTzfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OxBcK/btrWzRkSLH0/zKop9tbSutz1ink6JVTzfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOxBcK%2FbtrWzRkSLH0%2FzKop9tbSutz1ink6JVTzfK%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;340&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;479&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포가 완료되면 Hosting URL에 나오는 주소로 들어가서 배포된 내용을 확인할 수 있다.&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;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfzCX8/btrWu5rSPiF/lWj9zjPBxHfKIVI6N1AjDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfzCX8/btrWu5rSPiF/lWj9zjPBxHfKIVI6N1AjDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfzCX8/btrWu5rSPiF/lWj9zjPBxHfKIVI6N1AjDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfzCX8%2FbtrWu5rSPiF%2FlWj9zjPBxHfKIVI6N1AjDK%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;457&quot; height=&quot;290&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;728&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2602&quot; data-origin-height=&quot;1228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVfVaI/btrWx81IPko/S8bYn02xkpYd7Ib0gP7OPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVfVaI/btrWx81IPko/S8bYn02xkpYd7Ib0gP7OPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVfVaI/btrWx81IPko/S8bYn02xkpYd7Ib0gP7OPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVfVaI%2FbtrWx81IPko%2FS8bYn02xkpYd7Ib0gP7OPk%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;638&quot; height=&quot;301&quot; data-origin-width=&quot;2602&quot; data-origin-height=&quot;1228&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;h2 data-ke-size=&quot;size26&quot;&gt;참고사이트&lt;/h2&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https://firebase.google.com/docs/hosting?authuser=0&amp;amp;hl=ko&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>programming/etc</category>
      <author>헤써니</author>
      <guid isPermaLink="true">https://withhsunny.tistory.com/110</guid>
      <comments>https://withhsunny.tistory.com/110#entry110comment</comments>
      <pubDate>Wed, 18 Jan 2023 00:28:37 +0900</pubDate>
    </item>
    <item>
      <title>브라우저의 기본 동작 막기</title>
      <link>https://withhsunny.tistory.com/109</link>
      <description>&lt;p data-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;위 동작들을 막기 위한 방법으로는 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. event 객체 사용한다. event 객체 안에 있는 preventDefault() 메서드를 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 핸들러가 on&amp;lt;event&amp;gt;에 할당되었을 경우, return false를 반환하게 해 기본 동작을 막는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1673788956024&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;a href=&quot;/&quot; onclick=&quot;return false&quot;&amp;gt;첫번째 방식&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;/&quot; onclick=&quot;event.preventDefault()&quot;&amp;gt;두번째 방식&amp;lt;/a&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;위와 같이 기본동작이 막힌 경우, event 객체 안에 있는 defaultPrevented의 값은 true이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event.defaultPrevented 값을 사용하여, 버블링을 막는 방법 으로 사용하는 event.stopPropagation()을 용도를 대체할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event.stopPropagation()은 사용하는 구간을 dead zone으로 만들기 때문에, event.defaultPrevented를 활용하는 것이 좋다.&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;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/default-browser-action&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.javascript.info/default-browser-action&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>programming/javascript</category>
      <author>헤써니</author>
      <guid isPermaLink="true">https://withhsunny.tistory.com/109</guid>
      <comments>https://withhsunny.tistory.com/109#entry109comment</comments>
      <pubDate>Sun, 15 Jan 2023 23:14:52 +0900</pubDate>
    </item>
    <item>
      <title>[Angular] HostBinding</title>
      <link>https://withhsunny.tistory.com/108</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;@angular/core 안에 존재하는 API로&amp;nbsp;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;DOM 속성을 호스트 바인딩 속성으로 표시하고 구성 메타데이터를 제공하는 데코레이터이다. Angular는 변경 감지 중에 호스트 속성 바인딩을 자동으로 검사하고 바인딩이 변경되면 디렉티브의 호스트 요소를 업데이트한다.&lt;/span&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;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;사용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ngModel 디렉티브가 있는 DOM 요소에 valid와 invalid 속성을 설정하는 디렉티브&lt;/p&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1652020012683&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Directive({selector: '[ngModel]'})
class NgModelStatus {
  constructor(public control: NgModel) {}
  @HostBinding('class.valid') get valid() { return this.control.valid; }
  @HostBinding('class.invalid') get invalid() { return this.control.invalid; }
}

@Component({
  selector: 'app',
  template: `&amp;lt;input [(ngModel)]=&quot;prop&quot;&amp;gt;`,
})
class App {
  prop;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;HostBinding을 이용하여 글자를 강조하는 css를 적용해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, HighlightedDirective를 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1652022116570&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Directive, HostBinding } from '@angular/core';

@Directive({
  selector: '[highlighted]',
})
export class HighlightedDirective {
//...

  @HostBinding('className')
  get cssClasses() {
    return 'highlighted';
  }
}&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;생성한 디렉티브는 HTML 템플릿에서 highlighted라는 이름으로 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1652022247589&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p highlighted&amp;gt;highlighted text&amp;lt;/p&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;이 디렉티브를 사용함으로써 적용하려고 하는 css는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1652022217917&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.highlighted {
  color: red;
}&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;A.&lt;/b&gt; Angular에서 스타일을 적용하는 방법으로는 element에 className을 주는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 HostBinding으로 사용한다면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1652022306974&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//...

export class HighlightedDirective {
//...


  @HostBinding('className')  // **
  get cssClasses() { // **
    return 'highlighted'; // **
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 디렉티브를 사용하는 DOM Element들은 모두 &lt;i&gt;.highlighted&lt;/i&gt; css 설정이 적용된다.&lt;/p&gt;
&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;B.&lt;/b&gt; 특정 class 이름을 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;highlighted를 무조건 클래스 이름으로 바인딩한다.&lt;/p&gt;
&lt;pre id=&quot;code_1652022582929&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//...

export class HighlightedDirective {
//...

  @HostBinding('class.highlighted') // **
  get cssClasses() { // **
    return 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;&lt;b&gt;C.&lt;/b&gt; 아 물론 스타일을 직접 주는 것도 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1652022742902&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//...

export class HighlightedDirective {
//...

  @HostBinding('style.border') // **
  get cssClasses() { // **
    return '1px solid red'; // **
  }
}&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;D.&lt;/b&gt; 디렉티브를 사용하는 HTML 템플릿 내에서 프로퍼티를 바인딩함으로써 선택적으로 class 를 적용할 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유연하니 좋은 방법이다!&lt;/p&gt;
&lt;pre id=&quot;code_1652023009489&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//...
export class HighlightedDirective {
  @Input('highlighted')
  isHighligted = false;
  
  constructor() {
  }

  @HostBinding('class.highlighted')
  get cssClasses() {
    return this.isHighligted;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 디렉티브를 사용하기 위해서는 HTML 템플릿에서 프로퍼티를 바인딩 해주어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1652023056997&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p [highlighted]=&quot;true&quot;&amp;gt;highlighted text&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게하면 선택적으로 css를 적용할 수 있다.&lt;/p&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;a href=&quot;https://stackblitz.com/edit/angular-ivy-f6ybp9?file=src/app/app.component.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&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;&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;
&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;h3 data-ke-size=&quot;size23&quot;&gt;참고사이트&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://angular.kr/api/core/HostBinding&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://angular.kr/api/core/HostBinding&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652022432480&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;Angular 가이드&quot; data-og-description=&quot;Angular 가이드&quot; data-og-host=&quot;angular.kr&quot; data-og-source-url=&quot;https://angular.kr/api/core/HostBinding&quot; data-og-url=&quot;https://angular.kr&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/6XD3k/hyOk3eXZzc/oKxoV7S7DMjPvTlZqRgzKK/img.png?width=250&amp;amp;height=250&amp;amp;face=0_0_250_250&quot;&gt;&lt;a href=&quot;https://angular.kr/api/core/HostBinding&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://angular.kr/api/core/HostBinding&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/6XD3k/hyOk3eXZzc/oKxoV7S7DMjPvTlZqRgzKK/img.png?width=250&amp;amp;height=250&amp;amp;face=0_0_250_250');&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;Angular 가이드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Angular 가이드&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;angular.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://angular-university.io/lesson/angular-host-binding&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://angular-university.io/lesson/angular-host-binding&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652022432610&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;Angular University&quot; data-og-description=&quot;&quot; data-og-host=&quot;angular-university.io&quot; data-og-source-url=&quot;https://angular-university.io/lesson/angular-host-binding&quot; data-og-url=&quot;https://angular-university.io/lesson/angular-host-binding&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bTqtZs/hyOi5Fs0Ir/GkCMv86PvJkNSZ1kK6kgr0/img.png?width=160&amp;amp;height=160&amp;amp;face=0_0_160_160,https://scrap.kakaocdn.net/dn/F6AfY/hyOjbyVf0c/UOUxmq1mQb1ZPNzjzwVqB0/img.jpg?width=168&amp;amp;height=168&amp;amp;face=0_0_168_168&quot;&gt;&lt;a href=&quot;https://angular-university.io/lesson/angular-host-binding&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://angular-university.io/lesson/angular-host-binding&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bTqtZs/hyOi5Fs0Ir/GkCMv86PvJkNSZ1kK6kgr0/img.png?width=160&amp;amp;height=160&amp;amp;face=0_0_160_160,https://scrap.kakaocdn.net/dn/F6AfY/hyOjbyVf0c/UOUxmq1mQb1ZPNzjzwVqB0/img.jpg?width=168&amp;amp;height=168&amp;amp;face=0_0_168_168');&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;Angular University&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;angular-university.io&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;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>programming/Angular</category>
      <author>헤써니</author>
      <guid isPermaLink="true">https://withhsunny.tistory.com/108</guid>
      <comments>https://withhsunny.tistory.com/108#entry108comment</comments>
      <pubDate>Mon, 9 May 2022 00:19:14 +0900</pubDate>
    </item>
    <item>
      <title>[Angular] 이벤트 바인딩</title>
      <link>https://withhsunny.tistory.com/107</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;이벤트 바인딩&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;149&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmqZGh/btrqAbCnW6b/Si2mmPThLvGOqhGhzAAWJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmqZGh/btrqAbCnW6b/Si2mmPThLvGOqhGhzAAWJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmqZGh/btrqAbCnW6b/Si2mmPThLvGOqhGhzAAWJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmqZGh%2FbtrqAbCnW6b%2FSi2mmPThLvGOqhGhzAAWJ1%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;559&quot; height=&quot;137&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;149&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;passive 이벤트 바인딩&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Angular는 passive 이벤트 리스터를 지원한다. 아래대로 따라하면 스크롤 이벤트를 passive하게 사용가능하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. src 폴더 하위에 zone-flags.ts 파일을 만든다.&lt;br /&gt;2. 아래 코드를 파일에 추가한다.&lt;br /&gt;
&lt;pre id=&quot;code_1641999174716&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(window as any)['__zone_symbol__PASSIVE_EVENTS'] = ['scroll'];​&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;3. src/polyfilles.ts 파일에 zone.js 를 import 하기 전에 새로만든 파일인 zone-flag.ts 를 import 한다.&lt;br /&gt;
&lt;pre id=&quot;code_1641999238199&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './zone-flags';
import 'zone.js';  // Included with Angular CLI.​&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 과정을 모두 마치면 scroll 이벤트를 위한 이벤트 리스너를 추가할 때, 리스너는 passive 해진다.&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;passive 이벤트?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://withhsunny.tistory.com/106&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://withhsunny.tistory.com/106&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1642002318823&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;EventTarget.addEventListener()의 Passive 옵션 알아보기&quot; data-og-description=&quot;EventTarget.addEventListener()의 옵션 중 하나로 passive가 지원된다. passive를 true로 설정함으로써 스크롤 성능을 향상시킬 수 있다. // Chrome 49 부터 EventListenerOptions 지원 document.addEventListene..&quot; data-og-host=&quot;withhsunny.tistory.com&quot; data-og-source-url=&quot;https://withhsunny.tistory.com/106&quot; data-og-url=&quot;https://withhsunny.tistory.com/106&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/rFp4U/hyM4EWAeNl/JI2JVrfYpDTtr6wjTXkm4k/img.png?width=140&amp;amp;height=140&amp;amp;face=0_0_140_140,https://scrap.kakaocdn.net/dn/LTR9g/hyM5IXjMbS/RWeCmLlwktEv5ZQ84HHjO0/img.png?width=140&amp;amp;height=140&amp;amp;face=0_0_140_140,https://scrap.kakaocdn.net/dn/PLIee/hyM4xXp2uk/rjGRaVZ06xko20zxC8K4F1/img.jpg?width=402&amp;amp;height=414&amp;amp;face=159_152_311_318&quot;&gt;&lt;a href=&quot;https://withhsunny.tistory.com/106&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://withhsunny.tistory.com/106&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/rFp4U/hyM4EWAeNl/JI2JVrfYpDTtr6wjTXkm4k/img.png?width=140&amp;amp;height=140&amp;amp;face=0_0_140_140,https://scrap.kakaocdn.net/dn/LTR9g/hyM5IXjMbS/RWeCmLlwktEv5ZQ84HHjO0/img.png?width=140&amp;amp;height=140&amp;amp;face=0_0_140_140,https://scrap.kakaocdn.net/dn/PLIee/hyM4xXp2uk/rjGRaVZ06xko20zxC8K4F1/img.jpg?width=402&amp;amp;height=414&amp;amp;face=159_152_311_318');&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;EventTarget.addEventListener()의 Passive 옵션 알아보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;EventTarget.addEventListener()의 옵션 중 하나로 passive가 지원된다. passive를 true로 설정함으로써 스크롤 성능을 향상시킬 수 있다. // Chrome 49 부터 EventListenerOptions 지원 document.addEventListene..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;withhsunny.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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EventEmitter로 커스텀 이벤트 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디렉티브는 Angular의 EventEmmiter를 다음과 같이 사용함으로써 커스텀 이벤트를 발생시킨다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. 디렉티브는 EventEmitter를 만들고 프로퍼티로써 이를 노출시킨다.&lt;br /&gt;2. 그런 다음 디렉티브는 EventEmitter.emit(data)를 호출하여 이벤트를 내보내고 data를 전달한다.&lt;br /&gt;3. 상위 디렉티브는 이 프로퍼티를 바인딩함으로써 이벤트를 수신하고, $event 객체를 통해 데이터에 접근한다.&lt;/blockquote&gt;
&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;span style=&quot;color: #9d9d9d;&quot;&gt;커스텀 이벤트: deleteRequest&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1642002762156&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;app-item-detail (deleteRequest)=&quot;deleteItem($event)&quot; [item]=&quot;currentItem&quot;&amp;gt;&amp;lt;/app-item-detail&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;h2 data-ke-size=&quot;size26&quot;&gt;참고사이트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://angular.io/guide/event-binding&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://angular.io/guide/event-binding&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1642002786839&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;Angular&quot; data-og-description=&quot;&quot; data-og-host=&quot;angular.io&quot; data-og-source-url=&quot;https://angular.io/guide/event-binding&quot; data-og-url=&quot;https://angular.io/guide/event-binding&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://angular.io/guide/event-binding&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://angular.io/guide/event-binding&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Angular&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;angular.io&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;&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>programming/Angular</category>
      <author>헤써니</author>
      <guid isPermaLink="true">https://withhsunny.tistory.com/107</guid>
      <comments>https://withhsunny.tistory.com/107#entry107comment</comments>
      <pubDate>Thu, 13 Jan 2022 00:56:52 +0900</pubDate>
    </item>
    <item>
      <title>EventTarget.addEventListener()의 Passive 옵션 알아보기</title>
      <link>https://withhsunny.tistory.com/106</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;EventTarget.addEventListener()의 옵션 중 하나로 passive가 지원된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;passive를 true로 설정함으로써 스크롤 성능을 향상시킬 수 있다.&lt;/p&gt;
&lt;pre data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// Chrome 49 부터 EventListenerOptions 지원
document.addEventListener(&quot;touchstart&quot;, handler, {
  capture: false, // Chrome 49
  once: false, // Chrome 55
  passive: false // Chrome 51
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떻게 향상하는걸까?&lt;span style=&quot;background-color: #fafafa;&quot;&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa;&quot;&gt;스크롤 할때 발생하는 터치 이벤트들이 브라우저에서 발생하면, 브라우저는 터치 이벤트 수신기가 스크롤을 취소할지 여부를 알 수 없으므로 항상 수신기가 끝날 때까지 기다렸다가 페이지를 스크롤한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa;&quot;&gt; passive 옵션을 사용함으로써, 브라우저에게 스크롤을 기다리지 않고 즉시 스크롤해도 됨을 알리는거다.&lt;/span&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;span style=&quot;background-color: #fafafa;&quot;&gt;자세히 알아보기&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;touchstart, touchmove 핸들러의 결과를 기다릴 필요가 있기 때문에 preventDefault()를 이용하여 부분적으로 무효화한다. 분석 결과에 따르면 웹 상의 터치 이벤트 핸들러 대다수는 실제로 preventDefault()를 호출하지 않으므로 브라우저는 불필요한 스크롤을 차단하는 경우가 있다. &lt;br /&gt;&lt;br /&gt;예를 들어, 안드로이드용 크롬에서는 스크롤을 차단하는 터치 이벤트의 80%가 실제로 이를 막지 못한다. 이러한 이벤트의 10%는 스크롤 시작에 100ms 이상의 지연을 추가하며, 스크롤의 1%에서 최소 500ms의 치명적인 지연이 발생한다.&lt;br /&gt;&lt;br /&gt;이 문제는 터치 이벤트 뿐만 아니라 휠 이벤트에서도 동일한 문제가 발생한다.&lt;br /&gt;&lt;br /&gt;이를 해결하기 위해 passive 옵션을 사용한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #fafafa;&quot;&gt;사용 가능한 버전&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Chrome 51+, Firefox 49+ 부터 세번째 인자를 useCapture 속성을 포함한 객체로 사용할 수 있도록 변경되었다. (이외는 boolean)&lt;br /&gt;Chrome 51+ 업데이트사항 보러가기&amp;nbsp; -&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developers.google.com/web/updates/2016/06/passive-event-listeners&quot;&gt;https://developers.google.com/web/updates/2016/06/passive-event-listeners&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;passive 옵션을 권장하는 경우, 콘솔에 경고로 뜬다.&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Handling of 'touchstart' input event was delayed for X ms due to main thread being busy. Consider marking event handler as 'passive' to make the page more responsive.&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고사이트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md&quot;&gt;https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1642002148723&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - WICG/EventListenerOptions: An extension to the DOM event pattern to allow authors to disable support for preventDefault&quot; data-og-description=&quot;An extension to the DOM event pattern to allow authors to disable support for preventDefault - GitHub - WICG/EventListenerOptions: An extension to the DOM event pattern to allow authors to disable ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md&quot; data-og-url=&quot;https://github.com/WICG/EventListenerOptions&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cTdScR/hyM5JaQ4ws/7KvK4kygZi3NlBpU2K7YD1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cTdScR/hyM5JaQ4ws/7KvK4kygZi3NlBpU2K7YD1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - WICG/EventListenerOptions: An extension to the DOM event pattern to allow authors to disable support for preventDefault&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;An extension to the DOM event pattern to allow authors to disable support for preventDefault - GitHub - WICG/EventListenerOptions: An extension to the DOM event pattern to allow authors to disable ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/EventTarget/addEventListener&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/EventTarget/addEventListener&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1642002182223&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;EventTarget.addEventListener() - Web API | MDN&quot; data-og-description=&quot;EventTarget의&amp;nbsp;addEventListener() 메서드는 지정한 이벤트가 대상에 전달될 때마다 호출할 함수를 설정합니다.&amp;nbsp;일반적인 대상은 Element, Document, Window지만, XMLHttpRequest와&amp;nbsp;같이 이벤트를 지원하는 모든 &quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/EventTarget/addEventListener&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/EventTarget/addEventListener&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b3TPv8/hyM4C5v62g/8FkUGGmtkUDyrpMUa0THE1/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/API/EventTarget/addEventListener&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/EventTarget/addEventListener&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b3TPv8/hyM4C5v62g/8FkUGGmtkUDyrpMUa0THE1/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;EventTarget.addEventListener() - Web API | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;EventTarget의&amp;nbsp;addEventListener() 메서드는 지정한 이벤트가 대상에 전달될 때마다 호출할 함수를 설정합니다.&amp;nbsp;일반적인 대상은 Element, Document, Window지만, XMLHttpRequest와&amp;nbsp;같이 이벤트를 지원하는 모든&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>programming/javascript</category>
      <author>헤써니</author>
      <guid isPermaLink="true">https://withhsunny.tistory.com/106</guid>
      <comments>https://withhsunny.tistory.com/106#entry106comment</comments>
      <pubDate>Thu, 13 Jan 2022 00:44:26 +0900</pubDate>
    </item>
    <item>
      <title>[Angular] 파이프- Pipes</title>
      <link>https://withhsunny.tistory.com/105</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문자열, 통화, 일자와 같은 데이터를 원하는 형태로 바꾸기 위하여 파이프를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Angula는 기본 파이프를 제공하며 이외에 데이터를 변환하는 로직이 별도로 필요하면 커스텀 파이프를 만들어서 사용할 수 있다.&lt;/p&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;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;2. 표현식의 오른쪽에 파이프(|)를 적고 파이프 이름을 적는다.&lt;/p&gt;
&lt;pre id=&quot;code_1637588695463&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- AsyncPipe 사용 예 --&amp;gt;
&amp;lt;span&amp;gt;Wait for it... {{ greeting | async }}&amp;lt;/span&amp;gt;&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;파라미터와 체이닝 파이프로 데이터를 변경하기&lt;/h3&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;아래 예제는 CurrencyPipe 를 사용하는 예이다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CurrencyPipe&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - locale 구성을 결정하는 locale 규칙에 따라 숫자를 통화 문자열로 변환하는 파이프&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - {{ value_expression |&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://angular.io/api/common/CurrencyPipe&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;currency&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[ : currencyCode [ : display [ : digitsInfo [ : locale ] ] ] ] }}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**locale: 사용자의 언어, 국가뿐 아니라 사용자 인터페이스에서 사용자가 선호하는 사항을 지정하는 매개 변수의 모임&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1637589819692&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{{ amount | currency:'EUR' }}&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;위와 같이 작성하면 amount에 있는 숫자가 유로 단위로 표시된다.&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;2개 이상의 파라미터를 전달할 때에는 콜론(:)을 이어 붙이면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1637589905811&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{{ amount | currency:'EUR':'Euros '}}&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;파라미터를 반드시 지정해야 하는 경우가 있다. 한 예로는 Angular에서 기본적으로 제공하는 SlicePipe가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1637589960321&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{{ slice:1:5 }}&lt;/code&gt;&lt;/pre&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 id=&quot;code_1637590039748&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{{ birthday | date | uppercase}}&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;커스텀 파이프 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스에 @Pipe 데코레이터를 지정하고 필요한 내용을 메타데이터에 작성하면 클래스를 파이프로 만들 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파이프 클래스 이름은 일반적인 스타일로 UpperCamelCase로 작성한다.&lt;/li&gt;
&lt;li&gt;메타데이터 name에 해당하는 문자열은 camelCalse로 작성한다. (name에 하이픈을 사용하지 말 것)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;새롭게 만들 파이프는 NgModule 메타데이터의 declarations 필드에 등록해야 한다.&lt;/u&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;h4 data-ke-size=&quot;size20&quot;&gt;변화를 감지하는 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이프가 변화를 감지하는 방식을 알기 전에 어떤 때에 Angular 변화 감지가 일어나는지 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Angular 변화감지&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;키입력 등의 event, 타이머, 서버 응답과 같은 DOM 이벤트가 발생할 때마다 변화 감지 동작을 실행하고, 바인딩된 데이터가 변경되었는지 검사함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Angular의 변화감지 때마다 파이프가 계속 실행되면 앱 성능을 저하할 수 있다. 따라서 Angular의 변화감지와는 조금 다른 변화 감지를 사용한다.&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;h4 data-ke-size=&quot;size20&quot;&gt;순수한(pure) 파이프&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이프가 변경을 탐지하는 기본 옵션은 pure이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pure인 경우에 탐지하는 변경은 아래와 같다.&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;순수한(pure) 파이프는 순수함수로 구현해야 한다. 순수한 파이프를 사용하면 Angular는 객체 안에서 발생한 변화를 무시한다. 이로 인해 순수한 파이프에 배열을 인자로 전달하면 예상한대로 파이프가 실행되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;이유: Angular change detector가 배열의 항목이 변경된 것을 감지하지 않아 파이프도 실행되지 않음&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&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;위의 해결방식으로 풀고 싶지 않다면, 객체안에 변경된 내용을 감지하기 위해서는 impure 를 선언하여 파이프를 사용해야 한다.&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;순수하지 않은(impure) 파이프&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순수하지 않은 파이프는 Angular 변화 감지가 동작하는 시점마다 실행된다. 순수하지 않은 파이프를 정의하려면 pure 플래그에 false 값을 지정하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1637591155333&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Pipe({
  name: 'flyingHeroesImpure',
  pure: false
})&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;옵저버블에서 데이터 추출하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Angular가 제공하는 AsyncPipe를 사용해서 파이프 인자를 옵저버블로 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/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;위 특징들로 AsyncPipe를 사용해서 옵저버블을 편리하게 다룰 수 있다.&lt;/p&gt;
&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;AsyncPipe는 옵저버블 객체를 처리할 수 있도록 순수하지 않은 파이프로 구현되었다.&lt;/u&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;HTTP 요청 캐싱하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Angular에서 서버통신에 사용하는 HttpClient 서비스가 제공하는 HttpClient.get() 과 같은 함수는 서버로부터 받은 데이터를 옵저버블 형태로 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 앞서 알아본 AsyncPipe의 방식을 활용하면 HTTP 요청을 보내고 캐싱하는 파이프를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵저버블의 객체를 처리하기 위해서는 순수하지 않은 파이프를 사용하고, 이는 컴포넌트 변화감지 로직이 동작할 때마다 실행되기 때문에 불필요하게 실행될 수 있다. 이때 성능이 저하되는 것을 피하기 위해 서버로 요청하는 URL이 변경될 때마 파이프 로직을 실행하고 서버에서 받은 응답을 캐싱하는 방법을 사용하는 것이 좋다.&lt;/p&gt;
&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://angular.kr/guide/pipes#http-%EC%9A%94%EC%B2%AD-%EC%BA%90%EC%8B%B1%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서&lt;/a&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;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;a ? b : c | x&lt;/span&gt; 의 구문이 &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;a ? b : ( c | x )&lt;/span&gt; 로 해석된다는 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼항연산자의 결과에 파이프를 적용하고 싶다면 전체 식을 괄호로 바꾸어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1637591931726&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(a ? b : c) | x&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;h3 data-ke-size=&quot;size23&quot;&gt;참고사이트&lt;/h3&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%A1%9C%EC%BC%80%EC%9D%BC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.wikipedia.org/wiki/%EB%A1%9C%EC%BC%80%EC%9D%BC&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1637589388762&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;ko.wikipedia.org&quot; data-og-source-url=&quot;https://ko.wikipedia.org/wiki/%EB%A1%9C%EC%BC%80%EC%9D%BC&quot; data-og-url=&quot;https://ko.wikipedia.org/wiki/%EB%A1%9C%EC%BC%80%EC%9D%BC&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%A1%9C%EC%BC%80%EC%9D%BC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.wikipedia.org/wiki/%EB%A1%9C%EC%BC%80%EC%9D%BC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;ko.wikipedia.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://angular.kr/guide/pipes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://angular.kr/guide/pipes&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1637590347439&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;Angular 가이드&quot; data-og-description=&quot;Angular 가이드&quot; data-og-host=&quot;angular.kr&quot; data-og-source-url=&quot;https://angular.kr/guide/pipes&quot; data-og-url=&quot;https://angular.kr&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cPq4TG/hyMqWKGRQ5/ZqmrwW8KZy0ejKd8KadZdk/img.png?width=250&amp;amp;height=250&amp;amp;face=0_0_250_250&quot;&gt;&lt;a href=&quot;https://angular.kr/guide/pipes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://angular.kr/guide/pipes&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cPq4TG/hyMqWKGRQ5/ZqmrwW8KZy0ejKd8KadZdk/img.png?width=250&amp;amp;height=250&amp;amp;face=0_0_250_250');&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;Angular 가이드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Angular 가이드&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;angular.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;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>programming/Angular</category>
      <author>헤써니</author>
      <guid isPermaLink="true">https://withhsunny.tistory.com/105</guid>
      <comments>https://withhsunny.tistory.com/105#entry105comment</comments>
      <pubDate>Mon, 22 Nov 2021 23:39:01 +0900</pubDate>
    </item>
  </channel>
</rss>