<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>한장현입니다.</title>
    <link>https://han41858.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 14 May 2026 18:32:59 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>한장현</managingEditor>
    <image>
      <title>한장현입니다.</title>
      <url>https://t1.daumcdn.net/cfile/tistory/2167754157791CB623</url>
      <link>https://han41858.tistory.com</link>
    </image>
    <item>
      <title>Angular v17 릴리즈 노트</title>
      <link>https://han41858.tistory.com/57</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;안녕하세요. angular.kr를 운영하고 있는 한장현입니다.&lt;/p&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;그동안 Angular 새 버전이 나올 때마다 angular.kr은 업데이트 하고 있었습니다만,&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;이 블로그는 운영을 잠시 멈췄습니다.&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;&amp;nbsp;얼마전부터 Angular 새 버전이 나오는 것을 기다리고 있었는데, 이번 버전은 RC, next 버전이 오래 걸리는 것 같더라고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그러다 17 버전이 나왔는데 아이쿠, 많은 것이 변했습니다 ㅎㅎㅎㅎㅎ&lt;/p&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;원문은 Angular blog 사이트의 &lt;a href=&quot;https://blog.angular.io/introducing-angular-v17-4d7033312e4b&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Introducing Angular v17&lt;/a&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;지난 달은 Angular의 붉은 방패가 등장한지 13주년 되는 달이었습니다. AngularJS는 풍부한 웹 경험을 제공하는 JavaScript 프레임워크의 출발점이었습니다. 오늘날 저희는 새로운 모습과 미래 지향적인 기능을 갖춘 17 버전을 소개하면서, 새로운 개발자 경험과 성능의 새로운 표준을 제시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_DTy8tlNHXGc0g4sB.webp&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kQitE/btsAJ9tgdSK/6T6rxAKkfBuDf2Qm6AulY0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kQitE/btsAJ9tgdSK/6T6rxAKkfBuDf2Qm6AulY0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kQitE/btsAJ9tgdSK/6T6rxAKkfBuDf2Qm6AulY0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkQitE%2FbtsAJ9tgdSK%2F6T6rxAKkfBuDf2Qm6AulY0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;405&quot; data-filename=&quot;0_DTy8tlNHXGc0g4sB.webp&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;405&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;17 버전에는 이런 기능들이 추가되었습니다:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;지연 구성되는 뷰를 활용해서 성능과 개발자 경험을 한 단계 끌어 올립니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기본 컨트롤 루프를 개선해서 &lt;a href=&quot;https://krausest.github.io/js-framework-benchmark/current.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;최대&amp;nbsp;90%&amp;nbsp;더&amp;nbsp;빠른&amp;nbsp;런타임&amp;nbsp;속도&lt;/a&gt;를&amp;nbsp;제공합니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빌드 속도가 하이브리드 렌더링을 통해 최대 87%, 클라이언트 사이드 렌더링을 통해 최대 67% 빨라졌습니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;미래 지향적인 기능을 반영한 새로운 디자인&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;완전히 새로운 방식으로 제공되는 대화형 학습 코스&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;...그리고 수많은 기능들이 추가되고 개선되었습니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;미래&amp;nbsp;지향적인&amp;nbsp;정체성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular의 르네상스는 지난 몇 개 버전에서 본격적으로 진행되었습니다. 신호 기반의 반응성, 하이드레이션, 독립 컴포넌트, 디렉티브 연결 등 수십가지 기능을 개선하면서 모멘텀을 계속 얻고 있었습니다. 하지만 Angular가 이렇게 발전하는 동안에도 브랜드는 그대로 유지되어 왔습니다. AngularJS 초기부터 거의 변하지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;이제 오늘, 수많은 사람들이 사랑하고 치열하게 사용했던 이 프레임워크는 미래 지향적인 개발자 경험과 성능을 반영하여 새롭게 태어났습니다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_UC-tiSyyd6b2JNaA.gif&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/teBVB/btsABAmbgpl/hswcvDKIPudgj8Kp9SYmfk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/teBVB/btsABAmbgpl/hswcvDKIPudgj8Kp9SYmfk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/teBVB/btsABAmbgpl/hswcvDKIPudgj8Kp9SYmfk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/teBVB/btsABAmbgpl/hswcvDKIPudgj8Kp9SYmfk/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;960&quot; height=&quot;540&quot; data-filename=&quot;0_UC-tiSyyd6b2JNaA.gif&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;미래 지향적인 문서&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular를 새롭게 브랜딩하면서 Angular 문서를 위한 새로운 홈, &lt;a href=&quot;https://angular.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;angular.dev&lt;/a&gt; 를 개발했습니다. 이 웹사이트는 이전과 완전히 다른 구조이며, 새로운 가이드 문서를 제공하고, 보기 편하며, 브라우저에서 직접 Angular CLI를 실행하는 것처럼 학습자의 속도에 온전히 맞추는 대화형 학습 코스를 제공합니다.이&amp;nbsp;새로운&amp;nbsp;대화형&amp;nbsp;학습&amp;nbsp;경험은&amp;nbsp;&lt;a href=&quot;https://webcontainers.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;WebContainers&lt;/a&gt;를&amp;nbsp;활용한&amp;nbsp;것이며,&amp;nbsp;이제는&amp;nbsp;Angular&amp;nbsp;CLI의&amp;nbsp;강력한&amp;nbsp;기능을&amp;nbsp;최신&amp;nbsp;웹&amp;nbsp;브라우저에서&amp;nbsp;바로&amp;nbsp;실행해&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_aYaIlq4QLLwvNqud.gif&quot; data-origin-width=&quot;1063&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AGaT2/btsAKReUFkC/xAi7eJG8qKuMNG3hsvUJiK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AGaT2/btsAKReUFkC/xAi7eJG8qKuMNG3hsvUJiK/img.gif&quot; data-alt=&quot;WebContainers를 활용해서 대화형으로 제공되는 Angular 튜토리얼&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AGaT2/btsAKReUFkC/xAi7eJG8qKuMNG3hsvUJiK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/AGaT2/btsAKReUFkC/xAi7eJG8qKuMNG3hsvUJiK/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;1063&quot; height=&quot;810&quot; data-filename=&quot;0_aYaIlq4QLLwvNqud.gif&quot; data-origin-width=&quot;1063&quot; data-origin-height=&quot;810&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WebContainers를 활용해서 대화형으로 제공되는 Angular 튜토리얼&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;오늘 저희는 angular.dev의 베타 프리뷰를 오픈했습니다. Angular v18에서는 이 홈페이지를 Angular의 기본 웹사이트로 만들 예정입니다.&lt;/b&gt; &lt;a href=&quot;http://angular.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;angular.dev&lt;/a&gt;에&amp;nbsp;대해&amp;nbsp;자세하게&amp;nbsp;알아보려면&amp;nbsp;&lt;a href=&quot;https://blog.angular.io/announcing-angular-dev-1e1205fa3039&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&quot;angular.dev를&amp;nbsp;소개합니다&quot;&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;이제는 더 기다릴 것 없이 v17에서 제공하는 기능을 자세하게 파봅시다!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기본 제어 흐름&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개발자 경험을 개선하기 위해 간단하고 직관적이지만 강력한 기능을 제공하는 블록 템플릿 문법을 도입했습니다. 사용하기엔 간단하지만, 내부적으로는 Angular 컴파일러가 제어 흐름을 파악하고, 필요하면 지연 로딩도 하면서 효율적인&amp;nbsp;JavaScript&amp;nbsp;코드로&amp;nbsp;변환합니다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 새로운 블록 문법은 기본 제어 흐름 개선을 위해, 즉 최적화하기 위해 사용합니다. 저희가 조사해보니, 많은 개발자들이 &lt;code class=&quot;language-typescript&quot;&gt;*ngIf&lt;/code&gt;, &lt;code class=&quot;language-typescript&quot;&gt;*ngSwitch&lt;/code&gt;, &lt;code class=&quot;language-typescript&quot;&gt;*ngFor&lt;/code&gt;를 사용하면서 많은 어려움을 겪고 있다는 것을 확인했습니다. 개인적으로는 2016년부터 Angular를 사용하고 있고, Angular 팀으로는 5년동안 근무했지만, 저도 아직 &lt;code class=&quot;language-typescript&quot;&gt;*ngFor&lt;/code&gt;, &lt;code class=&quot;language-typescript&quot;&gt;trackBy&lt;/code&gt; 문법을 쓸때마다 문서를 찾아봅니다. 저희는 커뮤니티, 파트너사, &lt;a href=&quot;https://blog.angular.io/meet-angulars-new-control-flow-a02c6eee7843&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;UX 리서치 스터디&lt;/a&gt;를 통해 다양한 피드백을 수집했고, 이제 새로운 기본 제어 흐름을 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;이 기본 제어 흐름을 활용하면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전에 사용하던 템플릿 문법보다 좀 더 JavaScript에 가까운 문법을 사용하기 때문에 문서를 찾지 않아도 될 정도로 직관적입니다.&lt;/li&gt;
&lt;li&gt;타입 구체화에 적합하기 때문에 타입 검사가 더 강력해집니다.&lt;/li&gt;
&lt;li&gt;빌드 시점에만 필요하고 실행 시점에는 필요없는 코드를 제거해서 최대 30kB까지 빌드 결과물 크기를 줄일 수 있으며, 이 과정을 통해 Core Web Vital 점수를 향상시킬 수 있습니다.&lt;/li&gt;
&lt;li&gt;별도로 로드하지 않아도 템플릿에서 바로 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;잠시 후에 다루겠지만, 성능이 눈에 띄게 개선됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;조건문&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;code class=&quot;language-typescript&quot;&gt;*ngIf&lt;/code&gt;를 예를 들어 비교해 봅시다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div *ngIf=&quot;loggedIn; else anonymousUser&quot;&amp;gt;
  The user is logged in
&amp;lt;/div&amp;gt;
&amp;lt;ng-template #anonymousUser&amp;gt;
  The user is not logged in
&amp;lt;/ng-template&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;이제 기본 제어 문법으로 if를 구현하면 이렇게 구현할 수 있습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;@if (loggedIn) {
  The user is logged in
} @else {
  The user is not logged in
}&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;code class=&quot;language-typescript&quot;&gt;@else&lt;/code&gt;를 사용하면 원래 사용하던 &lt;code class=&quot;language-typescript&quot;&gt;*ngIf&lt;/code&gt; 문법을 아주 직관적으로 대체할 수 있습니다. 그리고&amp;nbsp;새로운&amp;nbsp;제어&amp;nbsp;흐름에서는&amp;nbsp;이전까지&amp;nbsp;불가능하던&amp;nbsp;&lt;code class=&quot;language-typescript&quot;&gt;@else if&lt;/code&gt;도&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;code class=&quot;language-typescript&quot;&gt;*ngSwitch&lt;/code&gt;는 더 눈에 띄게 개선됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div [ngSwitch]=&quot;accessLevel&quot;&amp;gt;
  &amp;lt;admin-dashboard *ngSwitchCase=&quot;admin&quot;/&amp;gt;
  &amp;lt;moderator-dashboard *ngSwitchCase=&quot;moderator&quot;/&amp;gt;
  &amp;lt;user-dashboard *ngSwitchDefault/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 코드를 기본 제어 흐름으로 변경하면 이렇습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;@switch (accessLevel) {
  @case ('admin') { &amp;lt;admin-dashboard/&amp;gt; }
  @case ('moderator') { &amp;lt;moderator-dashboard/&amp;gt; }
  @default { &amp;lt;user-dashboard/&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;code class=&quot;language-typescript&quot;&gt;@switch&lt;/code&gt;의 개별 구문에서 타입 구체화를 훨씬 효율적으로 적용할 수 있습니다. 이&amp;nbsp;점은&amp;nbsp;&lt;code class=&quot;language-typescript&quot;&gt;*ngSwitch&lt;/code&gt;에서는&amp;nbsp;불가능했던&amp;nbsp;것입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기본 for 루프&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개인적으로 이번 업데이트에서 가장 마음에 드는 기능은 기본 for 루프입니다. 이&amp;nbsp;기능은&amp;nbsp;개발자&amp;nbsp;경험을&amp;nbsp;개선하는&amp;nbsp;것&amp;nbsp;외에도&amp;nbsp;Angular의&amp;nbsp;렌더링&amp;nbsp;속도를&amp;nbsp;완전히&amp;nbsp;새로운&amp;nbsp;단계로&amp;nbsp;끌어&amp;nbsp;올렸습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본 문법은 이렇습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;@for (user of users; track user.id) {
  {{ user.name }}
} @empty {
  Empty list of users
}&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;code class=&quot;language-typescript&quot;&gt;*ngFor&lt;/code&gt;를 사용할 때 &lt;code class=&quot;language-typescript&quot;&gt;*trackBy&lt;/code&gt;를 사용하지 않으면 성능 문제가 발생하는 것을 종종 확인했습니다. &lt;code class=&quot;language-typescript&quot;&gt;@for&lt;/code&gt;는 빠른 비교 성능을 보장하기 위해 &lt;code class=&quot;language-typescript&quot;&gt;trackBy&lt;/code&gt;에 해당하는 구문을 필수로 지정해야 합니다. 그리고 이제는 컴포넌트 클래스의 메서드를 템플릿에 연결하는 것이 아니라, 템플릿에서 평가식을 직접 사용하기 때문에 사용하기 편해졌습니다. 항목이 비어 있을 수 있는 컬렉션을 다룬다면 &lt;code class=&quot;language-typescript&quot;&gt;@empty&lt;/code&gt; 블록을 사용해볼 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;code class=&quot;language-typescript&quot;&gt;@for&lt;/code&gt; 구문은 &lt;code class=&quot;language-typescript&quot;&gt;*ngFor&lt;/code&gt;와 달리 새로운 비교 알고리즘을 사용하며, 최적화에도 신경썼기 때문에 이전보다 &lt;b&gt;최대 90% 빠른 런타임 성능을 자랑합니다!&lt;/b&gt; &lt;a href=&quot;https://krausest.github.io/js-framework-benchmark/current.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;벤치마크&amp;nbsp;결과&lt;/a&gt;를&amp;nbsp;확인해&amp;nbsp;보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1_NWgsHKl5Zy5dNrOHIU7AAg.webp&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;1480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XzxBD/btsAFw4Dsgz/d9qcImjyMxxrBgfsfcbi7k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XzxBD/btsAFw4Dsgz/d9qcImjyMxxrBgfsfcbi7k/img.webp&quot; data-alt=&quot;*ngFor 구현방식에 따른 성능 비교 (https://krausest.github.io/js-framework-benchmark/current.html)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XzxBD/btsAFw4Dsgz/d9qcImjyMxxrBgfsfcbi7k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXzxBD%2FbtsAFw4Dsgz%2Fd9qcImjyMxxrBgfsfcbi7k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;1480&quot; data-filename=&quot;1_NWgsHKl5Zy5dNrOHIU7AAg.webp&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;1480&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;*ngFor 구현방식에 따른 성능 비교 (https://krausest.github.io/js-framework-benchmark/current.html)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;직접 사용해 보세요!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개선된 기본 제어 흐름은 v17에서 개발자 프리뷰로 직접 사용해 볼 수 있습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 기본 제어 흐름은 설계 단계때부터 완전히 자동화되는 마이그레이션을 지원하도록 개발되었습니다. 기존&amp;nbsp;프로젝트에&amp;nbsp;이&amp;nbsp;명령을&amp;nbsp;실행하면&amp;nbsp;마이그레이션이&amp;nbsp;동작합니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ng generate @angular/core:control-flow&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;앞으로는?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본 제어 흐름은 이번 버전에 나온 언어 지원 서비스에도 적용되었기 때문에, JetBrain 제품군과 긴밀하게 협력해서 이전보다 나은 지원을 제공합니다. 그리고&amp;nbsp;Prettier를&amp;nbsp;만든&amp;nbsp;&lt;a href=&quot;https://github.com/sosukesuzuki&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Sosuke&amp;nbsp;Suzuki&lt;/a&gt;와 협력해서 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;&amp;nbsp;새로 도입된 기본 제어 흐름은 컨텐츠를 프로젝션하는 방식이 기존에 사용하던 &lt;code class=&quot;language-typescript&quot;&gt;*ngIf&lt;/code&gt;, &lt;code class=&quot;language-typescript&quot;&gt;*ngFor&lt;/code&gt;, &lt;code class=&quot;language-typescript&quot;&gt;*ngSwitch&lt;/code&gt;와는 조금 다른데, 이부분을 맞추는 작업은 앞으로 몇달에 걸쳐 진행될 예정입니다. 이 점을 고려하더라도 이번에 도입된 기본 제어 흐름에 대해서는 안정성 측면에서 자랑할만한 수준입니다. 직접 실행해 보세요! 다만&amp;nbsp;작업이&amp;nbsp;진행되는&amp;nbsp;동안&amp;nbsp;개발자&amp;nbsp;경험이&amp;nbsp;더&amp;nbsp;나아지는&amp;nbsp;방법이&amp;nbsp;있는지,&amp;nbsp;하위&amp;nbsp;호환성이&amp;nbsp;잘&amp;nbsp;유지되고&amp;nbsp;있는지&amp;nbsp;좀&amp;nbsp;더&amp;nbsp;확인하기&amp;nbsp;위해&amp;nbsp;아직은&amp;nbsp;개발자&amp;nbsp;프리뷰로&amp;nbsp;진행하고&amp;nbsp;있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;뷰 지연 로딩(Deferrable views)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번에는 지연 로딩의 미래에 대해 이야기해 봅시다. 위에서 설명했던 블록 문법을 활용해서 앱을 더 빠르게 개선할 수 있는 강력한 메커니즘을 구성했습니다. 블로그&amp;nbsp;시작&amp;nbsp;부분에서&amp;nbsp;저는,&amp;nbsp;아름다울&amp;nbsp;정도로&amp;nbsp;훌륭하게&amp;nbsp;설계된&amp;nbsp;뷰&amp;nbsp;지연&amp;nbsp;로딩을&amp;nbsp;활용하면&amp;nbsp;명시적이면서&amp;nbsp;강력한&amp;nbsp;지연&amp;nbsp;로딩을&amp;nbsp;구현할&amp;nbsp;수&amp;nbsp;있기&amp;nbsp;때문에&amp;nbsp;성능과&amp;nbsp;개발자&amp;nbsp;경험을&amp;nbsp;다른&amp;nbsp;수준으로&amp;nbsp;끌어올린다고&amp;nbsp;언급했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_HFC_1HTlO0pN-_KN.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pUfve/btsABBSYVzS/OKw6VwHhrFNeIiEBq1pzY0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pUfve/btsABBSYVzS/OKw6VwHhrFNeIiEBq1pzY0/img.webp&quot; data-alt=&quot;왼쪽 가지에 뷰 지연 로딩이 적용되었습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pUfve/btsABBSYVzS/OKw6VwHhrFNeIiEBq1pzY0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpUfve%2FbtsABBSYVzS%2FOKw6VwHhrFNeIiEBq1pzY0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;1173&quot; data-filename=&quot;0_HFC_1HTlO0pN-_KN.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1173&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;왼쪽 가지에 뷰 지연 로딩이 적용되었습니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;블로그를 구현하는데 사용자들의 댓글은 지연 로딩한다고 합시다. 이전까지는 댓글을 불러오다가 발생할 수 있는 에러를 처리하거나, 플레이스 홀더를 표시하는 등 이런저런 복잡성을 관리하며 &lt;code class=&quot;language-typescript&quot;&gt;ViewContainerRef&lt;/code&gt;를 사용해야 했습니다. 다양하게 발생할 수 있는 에러를 처리하다보면 아름답지 못한 코드를 작성하게 되고, 결국 테스트하거나 디버깅하기 어려운 코드가 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;이번에 새로 도입된 뷰 지연 로딩를 활용하면 댓글을 지연 로딩하면서 모든 종속성을 한 번에 처리하는 기능을 코드 단 한 줄로 구현할 수 있습니다:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;@defer (on viewport) {
  &amp;lt;comment-list /&amp;gt;
} @placeholder {
  &amp;lt;!-- 댓글이 로딩되는 동안 표시할 플레이스 홀더 --&amp;gt;
  &amp;lt;img src=&quot;comments-placeholder.png&quot;&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;code class=&quot;language-typescript&quot;&gt;@defer&lt;/code&gt; 블록을 사용하면 이 블록 안에 사용되는 컴포넌트, 디렉티브, 파이프의 복잡성을 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;&amp;nbsp;이전까지는 어떤 DOM 엘리먼트가 뷰 포트에 표시되기 시작하면서 컴포넌트를 지연로딩하려면 &lt;code class=&quot;language-typescript&quot;&gt;IntersectionObserver&lt;/code&gt; API를 활용해도 상당히 복잡한 로직이 필요했습니다. &lt;b&gt;하지만&amp;nbsp;이제는&amp;nbsp;&lt;code class=&quot;language-typescript&quot;&gt;IntersectionObservers&lt;/code&gt;를&amp;nbsp;활용해서&amp;nbsp;트리거를&amp;nbsp;추가하는&amp;nbsp;방식으로&amp;nbsp;지연&amp;nbsp;로딩을&amp;nbsp;간단하게&amp;nbsp;구현할&amp;nbsp;수&amp;nbsp;있습니다!&lt;/b&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 - 댓글이 로드될 위치를 지정합니다.&lt;/p&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;위 예제를 예로 들면, Angular는 먼저 플레이스 홀더 블록을 렌더링합니다. 그리고 이 블록이 뷰 포트 안에 표시되기 시작하면 &lt;code class=&quot;language-typescript&quot;&gt;&amp;lt;comment-list/&amp;gt;&lt;/code&gt; 컴포넌트를 로드하기 시작합니다. 마지막으로&amp;nbsp;컴포넌트가&amp;nbsp;로딩되고&amp;nbsp;나면,&amp;nbsp;Angular는&amp;nbsp;플레이스&amp;nbsp;홀더를&amp;nbsp;제거하고&amp;nbsp;컴포넌트를&amp;nbsp;렌더링합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;로딩 상태와 에러 상태를 추가한다면 이렇게도 구현할 수 있습니다: &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;@defer (on viewport) {
  &amp;lt;comment-list/&amp;gt;
} @loading {
  Loading…
} @error {
  Loading failed :(
} @placeholder {
  &amp;lt;img src=&quot;comments-placeholder.png&quot;&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;이게 전부입니다! 다양한&amp;nbsp;상황&amp;nbsp;때문에&amp;nbsp;발생할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;복잡성은&amp;nbsp;이제&amp;nbsp;모두&amp;nbsp;Angular가&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code class=&quot;language-typescript&quot;&gt;on idle&lt;/code&gt; - 브라우저 부하가 심하지 않을 때 블록을 지연로딩합니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-typescript&quot;&gt;on immediate&lt;/code&gt; - 브라우저를 블로킹하지 않는 선에서 자동으로 지연 로딩합니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-typescript&quot;&gt;on timer(&amp;lt;time&amp;gt;)&lt;/code&gt; - 일정 시간동안 로딩을 늦춥니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-typescript&quot;&gt;on viewport&lt;/code&gt;, &lt;code class=&quot;language-typescript&quot;&gt;on viewport(&amp;lt;엘리먼트&amp;gt;)&lt;/code&gt; - 이 방식을 사용하면서 앵커 엘리먼트를 지정할 수도 있습니다. 이 경우에는 앵커 엘리먼트가 화면에 표시되는 시점에 컴포넌트를 지연 로딩하고 렌더링합니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-typescript&quot;&gt;on interaction&lt;/code&gt;, &lt;code class=&quot;language-typescript&quot;&gt;on interaction(&amp;lt;엘리먼트&amp;gt;)&lt;/code&gt; - 사용자가 특정 엘리먼트에 접근할 때 지연 로딩을 시작합니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-typescript&quot;&gt;on hover&lt;/code&gt;, &lt;code class=&quot;language-typescript&quot;&gt;on hover(&amp;lt;엘리먼트&amp;gt;)&lt;/code&gt; - 사용자가 특정 엘리먼트에 커서를 올리면 지연 로딩을 시작합니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-typescript&quot;&gt;when&amp;lt;표현식&amp;gt;&lt;/code&gt; - Promise를 반환하는 특정 표현식으로 커스텀 조건을 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;뷰를 지연 로딩하면 이 뷰를 화면에 렌더링하기 전에 필요한 리소스를 미리 다운받아 둘 수도 있습니다. 바로&amp;nbsp;위에서&amp;nbsp;설명한&amp;nbsp;트리거에&amp;nbsp;&lt;code class=&quot;language-typescript&quot;&gt;prefetch&lt;/code&gt;&amp;nbsp;구문만&amp;nbsp;간단하게&amp;nbsp;추가하면&amp;nbsp;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;@defer (on viewport; prefetch on idle) {
  &amp;lt;comment-list /&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;뷰 지연 로딩은 v17 버전에 개발자 프리뷰로 추가되었으니 지금 당장 사용해 볼 수 있습니다! 더&amp;nbsp;자세하게&amp;nbsp;알아보려면&amp;nbsp;&lt;a href=&quot;https://angular.io/guide/defer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이&amp;nbsp;가이드&amp;nbsp;문서&lt;/a&gt;를&amp;nbsp;참고하세요.&lt;/b&gt; &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;앞으로는?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;뷰 지연 로딩은 이미 활용해 볼 수 있기 때문에 한 번 사용해보는 것을 강력하게 권장합니다! 당장은 API 형태를 확정하기 전에 더 많은 피드백을 받아 보기 위해 이 기능을 개발자 프리뷰로 지정했습니다. 이후에는 다른 코드와 마찬가지로 시맨틱 버저닝을 따르게 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;현재는 서버 사이드 렌더링에서 이 기능을 활용할 수 없기 때문에 플레이스 홀더가 그대로 표시될 것입니다. 그래서&amp;nbsp;이&amp;nbsp;부분을&amp;nbsp;개선하는&amp;nbsp;작업이&amp;nbsp;조금&amp;nbsp;필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;지연 로딩되는 뷰를 서버에서도 렌더링할 수 있게 개선하고, 클라이언트에서는 부분적으로 하이드레이션하는 방법을 연구하고 있습니다. 이 시나리오대로라면 클라이언트는 트리거가 발생하지 않는 이상 지연 로딩 대상이 되는 뷰는 다운로드하지 않게 될 것입니다. 그리고 트리거가 발생하고 나면 해당 부분과 관련된 코드를 다운로드 받고 하이드레이션하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;신호를 주고 받으며 상호 동작하는 방식도 좀 더 재미있어질 예정입니다. 좀&amp;nbsp;더&amp;nbsp;지켜봐&amp;nbsp;주세요! &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;하이브리드 렌더링 개선&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번 버전부터 서버 사이드 렌더링(server-side rendering, SSR)과 정적 사이트 생성(static-site generation, SSG, 사전 렌더링)이 &lt;code class=&quot;language-typescript&quot;&gt;ng new&lt;/code&gt;에 통합되어 개발자들이 접근하기 편해졌습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_PtJVYI9kcRl5MFJ4.gif&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T6Mjc/btsAFrWYEtE/idjq5SH3e5WESz4WNKjEK0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T6Mjc/btsAFrWYEtE/idjq5SH3e5WESz4WNKjEK0/img.gif&quot; data-alt=&quot;Angular 앱을 생성할 때 나오는 SSR, SSG 프롬프트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T6Mjc/btsAFrWYEtE/idjq5SH3e5WESz4WNKjEK0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/T6Mjc/btsAFrWYEtE/idjq5SH3e5WESz4WNKjEK0/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;1600&quot; height=&quot;402&quot; data-filename=&quot;0_PtJVYI9kcRl5MFJ4.gif&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Angular 앱을 생성할 때 나오는 SSR, SSG 프롬프트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그리고 새 프로젝트를 생성할 때 이런 명령을 실행하면 SSR을 지원하는 상태로 앱을 생성할 수 있습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ng new my-app --ssr&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;하이드레이션이 개발자 프리뷰를 끝내고 정식으로 도입됨&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;지난 6개월동안 수천개의 애플리케이션에 하이드레이션이 도입되는 것을 지켜봤습니다. 그리고 이제 하이드레이션은 개발자 프리뷰를 끝내고 서버 사이드 렌더링을 활용하는 앱에 기본으로 활성화된다는 소식을 전할 수 있어 기쁩니다!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;신규 @angular/ssr 패키지&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번 버전부터는 Angular Universal 코드 저장소를 Angular CLI 코드 저장소로 옮기면서 서버 사이드 렌더링을 Angular CLI의 일부로 자연스럽게 통합했습니다!&lt;/p&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;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ng add @angular/ssr&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;이 명령을 실행하면 서버 쪽의 진입점을 생성하며, SSR, SSG 빌드 설정을 추가하고, 하이드레이션을 기본으로 활성화시킵니다. &lt;code class=&quot;language-typescript&quot;&gt;@angular/ssr&lt;/code&gt; 패키지는 이전에 있던 &lt;code class=&quot;language-typescript&quot;&gt;@nguniversal/express-engine&lt;/code&gt;과 기능면에서 동일하며, 이전 패키지는 현재 유지보수 단계로 전환되었습니다. 기존에 있던 애플리케이션이 express-engine 패키지를 사용하고 있었다면, 이 패키지는 Angular CLI 명령이 실행되면서 &lt;code class=&quot;language-typescript&quot;&gt;@angular/ssr&lt;/code&gt; 패키지로 대체됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;Virgin Media O2은 자사 플랫폼을 최신 Angular 하이브리드 렌더링 솔루션으로 전환하고 난 후 매출이 112% 증가했다고 합니다.&lt;/b&gt; 그리고&amp;nbsp;Angular&amp;nbsp;SSR이&amp;nbsp;제공하는&amp;nbsp;DOM&amp;nbsp;하이드레이션과&amp;nbsp;&lt;code class=&quot;language-typescript&quot;&gt;NgOptimizedImage&lt;/code&gt;를&amp;nbsp;사용한&amp;nbsp;경우에는&amp;nbsp;누적&amp;nbsp;레이아웃&amp;nbsp;전환이&amp;nbsp;평균&amp;nbsp;99.4%&amp;nbsp;감소했다는&amp;nbsp;리서치도&amp;nbsp;있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;앱이 SSR을 지원할 수 있도록 배포하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;더 나은 개발자 경험을 위해, 저희는 클라우드 서비스 제공자들과 긴밀하게 협업하며 배포하기 편한 형태를 연구했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 Firebase는 아무 설정 없이도 Angular 애플리케이션을 자동으로 분석해서 배포할 수 있습니다. 이 기능은 &lt;a href=&quot;https://firebase.google.com/docs/hosting/frameworks/angular&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;framework-aware CLI 프리뷰&lt;/a&gt;로 제공되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;firebase experiments:enable webframeworks
firebase init hosting
firebase deploy&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;framework-aware CLI는 Angular 앱을 분석해서 SSR, i18n, 이미지 최적화 등을 자동으로 적용하기 때문에 비용이 중요한 서버리스 인프라에서 더 효율적입니다.&lt;/p&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;복잡한 Angular 단일 코드 저장소가 있거나 네이티브 툴을 선호하는 개발자라면, AngularFire를 설치하고 &lt;code class=&quot;language-typescript&quot;&gt;ng deploy&lt;/code&gt; 명령을 실행하면 Firebase에 앱을 배포할 수도 있습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ng add @angular/fire
ng deploy&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;Angular 팀은 Angular 서버 사이드 렌더링에 ECMAScript 모듈 지원을 추가했고, &lt;a href=&quot;https://github.com/angular/angular/pull/50247&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;HttpClient&lt;/code&gt;를 백엔드에서 활용할 수 있는 기능을 추가&lt;/a&gt;했으며, CloudFlare와 협력해서 &lt;a href=&quot;https://developers.cloudflare.com/pages/framework-guides/deploy-an-angular-site/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;배포 프로세스를 간소화&lt;/a&gt;했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;새로운 라이프싸이클 후킹 함수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular 팀은 Angular의 SSR, SSG 성능을 끌어올리기 위한 장기 계획을 구상하며 DOM 에뮬레이션이나 DOM을 직접 조작하는 방식을 벗어나려고 합니다. 동시에&amp;nbsp;애플리케이션의&amp;nbsp;라이프싸이클&amp;nbsp;전반에&amp;nbsp;걸쳐&amp;nbsp;서드파티&amp;nbsp;라이브러리도&amp;nbsp;엘리먼트와&amp;nbsp;자유롭게&amp;nbsp;상호작용하는&amp;nbsp;자유도를&amp;nbsp;부여하고&amp;nbsp;싶습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;code class=&quot;language-typescript&quot;&gt;afterRender&lt;/code&gt; - 애플리케이션이 렌더링을 끝낸 직후에 실행될 콜백 함수를 등록합니다.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-typescript&quot;&gt;afterNextRender&lt;/code&gt; - 애플리케이션의 첫번째 렌더링 이후에 동작하는 렌더링이 끝날때마다 실행될 콜백 함수를 등록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 후킹 함수들은 브라우저가 실행할 수 있기 때문에 커스텀 컴포넌트 안쪽에 원하는 DOM 조작 로직을 안전하게 구현할 수 있습니다. &lt;code class=&quot;language-typescript&quot;&gt;afterNextRender&lt;/code&gt;를&amp;nbsp;활용해서&amp;nbsp;차트&amp;nbsp;라이브러리를&amp;nbsp;초기화해야&amp;nbsp;한다면&amp;nbsp;이렇게&amp;nbsp;하면&amp;nbsp;됩니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;@Component({
  selector: 'my-chart-cmp',
  template: `&amp;lt;div #chart&amp;gt;{{ ... }}&amp;lt;/div&amp;gt;`,
})
export class MyChartCmp {
  @ViewChild('chart') chartRef: ElementRef;
  chart: MyChart|null;

  constructor() {
    afterNextRender(() =&gt; {
      this.chart = new MyChart(this.chartRef.nativeElement);
    }, {phase: AfterRenderPhase.Write});
  }
}&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;개별 후킹 함수는 불필요한 레이아웃 조작 부하를 줄여 성능을 향상시키기 위해, 콜백 함수를 등록할 때 현재 어떤 단계인지 표시하는 phase 값을 지정할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;새로 만든 Angular 프로젝트는 Vite, esbuild를 기본으로 사용합니다.&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_5G-H5Qx3zyga4QyL.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjGrni/btsAH1pubZ3/mGg804212fltwj8sBo6pPk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjGrni/btsAH1pubZ3/mGg804212fltwj8sBo6pPk/img.webp&quot; data-alt=&quot;ng serve, ng build 명령은 이제 Vite, esbuild로 동작합니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjGrni/btsAH1pubZ3/mGg804212fltwj8sBo6pPk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjGrni%2FbtsAH1pubZ3%2FmGg804212fltwj8sBo6pPk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;520&quot; data-filename=&quot;0_5G-H5Qx3zyga4QyL.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ng serve, ng build 명령은 이제 Vite, esbuild로 동작합니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular CLI 빌드 파이프라인을 근본적으로 변경하지 않았다면 SSR도 도입할 수 없었을 겁니다.&lt;/p&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;v16부터 빌드 경험을 개선하기 위해 esbuild와 Vite를 개발자 프리뷰로 제공했었습니다. 수많은 개발자들이 이를 시험해봤고, 기업 파트너 중에서는 &lt;b&gt;빌드 시간이 67% 감소했다&lt;/b&gt;는 소식도 전해왔습니다! 오늘&amp;nbsp;저희는&amp;nbsp;새로운&amp;nbsp;애플리케이션&amp;nbsp;빌더가&amp;nbsp;개발자&amp;nbsp;프리뷰를&amp;nbsp;졸업하고&amp;nbsp;정식으로&amp;nbsp;도입되었다는&amp;nbsp;소식을&amp;nbsp;전합니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 멈추지 않고 저희는 하이브리드 렌더링에 사용되는 빌드 파이프라인을 개선했습니다. &lt;b&gt;그래서 SSR &amp;amp; SSG를 활용하는 경우 &lt;code class=&quot;language-typescript&quot;&gt;ng build&lt;/code&gt; 명령은 최대 87%, &lt;code class=&quot;language-typescript&quot;&gt;ng serve&lt;/code&gt; 명령은 최대 80% 정도 성능을 개선했습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_QgWDUlZy3ELEJmOS.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmQBWd/btsAFshiMbj/Is6gC3YD5DjeYscsby1g0k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmQBWd/btsAFshiMbj/Is6gC3YD5DjeYscsby1g0k/img.webp&quot; data-alt=&quot;webpack 기반의 기존 파이프라인고 esbuild + vite 파이프라인의 빌드 시간 비교&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmQBWd/btsAFshiMbj/Is6gC3YD5DjeYscsby1g0k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmQBWd%2FbtsAFshiMbj%2FIs6gC3YD5DjeYscsby1g0k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;742&quot; data-filename=&quot;0_QgWDUlZy3ELEJmOS.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;webpack 기반의 기존 파이프라인고 esbuild + vite 파이프라인의 빌드 시간 비교&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하이브리드 렌더링은 SSG나 SSR을 함께 활용하는 클라이언트 사이드 렌더링을 의미합니다. 이후에 나올 마이너 버전 업데이트에서는 하이브리드 렌더링이 적용된 기존 프로젝트를 자동으로 마이그레이션하는 기능을 추가할 예정입니다. 새로운&amp;nbsp;애플리케이션&amp;nbsp;빌더를&amp;nbsp;체험해&amp;nbsp;보려면&amp;nbsp;&lt;a href=&quot;https://angular.io/guide/esbuild&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이&amp;nbsp;문서&lt;/a&gt;를&amp;nbsp;참고하세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DevTools에 의존성 관계 디버깅 기능 추가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;작년에는 Angular DevTools에 의존성 관계를 디버깅할 수 있는 기능을 시범적으로 도입했었습니다. 그&amp;nbsp;이후로&amp;nbsp;몇달이&amp;nbsp;지났고,&amp;nbsp;이번에는&amp;nbsp;프레임워크가&amp;nbsp;실행되는&amp;nbsp;시점에&amp;nbsp;인젝터&amp;nbsp;트리에&amp;nbsp;접근하는&amp;nbsp;디버깅&amp;nbsp;API를&amp;nbsp;새롭게&amp;nbsp;추가했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 API를 활용하면 이런 내용을 확인할 수 있습니다.&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;&amp;nbsp;아래 애니메이션을 확인해 보세요. 그리고&amp;nbsp;자세한&amp;nbsp;내용은&amp;nbsp;&lt;a href=&quot;https://angular.io/guide/devtools&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;angular.io&amp;nbsp;사이트의&amp;nbsp;Angular&amp;nbsp;DevTools&amp;nbsp;문서&lt;/a&gt;를&amp;nbsp;참고하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_4HGr858r5auurUai.gif&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coDSyZ/btsAF2bc5K2/yH6uxNYpK1LdfiVBlpD741/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coDSyZ/btsAF2bc5K2/yH6uxNYpK1LdfiVBlpD741/img.gif&quot; data-alt=&quot;컴포넌트&amp;amp;nbsp;의존성&amp;amp;nbsp;관계,&amp;amp;nbsp;인젝터&amp;amp;nbsp;트리&amp;amp;nbsp;확인하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coDSyZ/btsAF2bc5K2/yH6uxNYpK1LdfiVBlpD741/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/coDSyZ/btsAF2bc5K2/yH6uxNYpK1LdfiVBlpD741/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;1088&quot; height=&quot;810&quot; data-filename=&quot;0_4HGr858r5auurUai.gif&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;810&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;컴포넌트&amp;nbsp;의존성&amp;nbsp;관계,&amp;nbsp;인젝터&amp;nbsp;트리&amp;nbsp;확인하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이제는 UI를 좀 더 다듬으려고 합니다. 인젝터&amp;nbsp;계층,&amp;nbsp;프로바이더,&amp;nbsp;의존성이&amp;nbsp;결정되는&amp;nbsp;과정을&amp;nbsp;좀&amp;nbsp;더&amp;nbsp;나은&amp;nbsp;모습으로&amp;nbsp;제공할&amp;nbsp;수&amp;nbsp;있기를&amp;nbsp;바랍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;독립 API를 처음부터 시작하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;독립 컴포넌트, 디렉티브, 파이프에 대한 피드백을 1년반 동안 받아오면서 개발자 경험을 더 다듬어 왔고, 그 결과물은 새 애플리케이션의 기본 사항으로 도입할 수 있게 되었습니다. 이제 &lt;code class=&quot;language-bash&quot;&gt;ng generate&lt;/code&gt; 명령으로 독립 컴포넌트, 디렉티브, 파이프를 생성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이와 함께 저희는 일관성 있는 학습 경험, 개발 모범 사례, 권장사항을 정리하기 위해 &lt;a href=&quot;http://angular.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;angular.io&lt;/a&gt;와 &lt;a href=&quot;http://angular.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;angular.dev&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;당분간은 NgModule은 유지할 예정이지만, 새로 도입된 독립 API가 어떤 장점을 갖는지 확인하면서 점차 NgModule에서 독립 API를 사용하는 방식으로 이동할 것을 적극 권장합니다. 이&amp;nbsp;과정을&amp;nbsp;자동화하는&amp;nbsp;스키매틱도&amp;nbsp;준비해뒀습니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ng generate @angular/core:standalone&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;a href=&quot;https://angular.io/guide/standalone-migration&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;마이그레이션 가이드 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;반응성의 다음 단계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular의 변경사항 중에서 아주 큰 변화 중 하나는 시그널 기반의 반응형 시스템으로 전환하는 것이었습니다. 이 과정을 진행하면서 이전 버전은 물론이고, Zone.js 기반의 변화 감지 시스템의 호환성도 보장하기 위해 노력했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;b&gt;이제는 Angular의 시그널 기반 반응형 시스템이 개발자 프리뷰를 졸업한 것을 알립니다.&lt;/b&gt; 당분간은&amp;nbsp;보완사항이&amp;nbsp;있는지&amp;nbsp;점검하기&amp;nbsp;위해&amp;nbsp;&lt;code class=&quot;language-typescript&quot;&gt;effect&lt;/code&gt;&amp;nbsp;함수는&amp;nbsp;개발자&amp;nbsp;프리뷰로&amp;nbsp;남겨두지만&amp;nbsp;말입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;앞으로 몇달간은 시그널 기반의 입력, 뷰 쿼리 등의 기능을 추가로 도입할 것입니다. 그리고&amp;nbsp;내년&amp;nbsp;5월에&amp;nbsp;올라갈&amp;nbsp;Angular&amp;nbsp;v18에서는&amp;nbsp;시그널과&amp;nbsp;관련하여&amp;nbsp;개발자&amp;nbsp;경험을&amp;nbsp;개선할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;기능들과&amp;nbsp;함께&amp;nbsp;다시&amp;nbsp;돌아오겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;테스트의 미래&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular팀은 개발자들이 원하는 성능이 나오는지, 자유롭게 변형할 수 있는지, 그리고 직관적인지 확인하기 위해 Jest를 꾸준히 실험해보고 있습니다. 여기에 추가로 Web TestRunner를 실험해보기 시작했고, 초기 구현에 대한 &lt;a href=&quot;https://github.com/angular/angular-cli/pull/25860&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공개 PR&lt;/a&gt;을 진행하고 있습니다. 당분간은&amp;nbsp;Karma에&amp;nbsp;사로잡혀&amp;nbsp;있는&amp;nbsp;프로젝트를&amp;nbsp;먼저&amp;nbsp;구해내기&amp;nbsp;위해&amp;nbsp;Web&amp;nbsp;Test&amp;nbsp;Runner&amp;nbsp;리서치에&amp;nbsp;집중할&amp;nbsp;예정입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;매터리얼 3&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular 팀은 Google의 매터리얼 디자인 팀과 긴밀하게 협업하며 디자인 토큰을 통합하고 Angular Material을 리팩토링해왔습니다. &lt;a href=&quot;https://m3.material.io/foundations/design-tokens/overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;디자인&amp;nbsp;토큰&lt;/a&gt;이란, 구성요소에 훨씬 더 많은 사용자 정의 옵션을 지원하고 Material 3 지원을 활성화하는 체계입니다. 하지만&amp;nbsp;v17&amp;nbsp;버전에는&amp;nbsp;디자인&amp;nbsp;토큰이나&amp;nbsp;&lt;a href=&quot;https://m3.material.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;매터리얼&amp;nbsp;3&lt;/a&gt;를&amp;nbsp;탑재할&amp;nbsp;준비가&amp;nbsp;아직&amp;nbsp;되지&amp;nbsp;않았기&amp;nbsp;때문에,&amp;nbsp;이&amp;nbsp;내용은&amp;nbsp;v17의&amp;nbsp;마이너&amp;nbsp;릴리즈&amp;nbsp;중에&amp;nbsp;탑재될&amp;nbsp;것으로&amp;nbsp;예상됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2022년 4분기에 새로운 MDC 기반의 Angular Material 컴포넌트들을 발표했고, 기능이 동일하지만 DOM 구조나 스타일이 다른 기존 컴포넌트는 지원을 중단하기로 결정했습니다. 이 컴포넌트들은 v15에 지원 중단이 결정되었기 때문에 v17에는 제거되었습니다. 하지만 Angular 버전을 v17로 올리더라도 Angular Material은 v16을 그대로 사용할 수 있습니다. 아직은 Angular 17버전이 Angular Material v16과 호환되기 때문이지만, Angular v18이 나오게 되면 Angular Material 버전도 올려야 할 것입니다. 마이그레이션을&amp;nbsp;수행할&amp;nbsp;수&amp;nbsp;없는&amp;nbsp;경우를&amp;nbsp;대비해서&amp;nbsp;종료되지&amp;nbsp;않는&amp;nbsp;유료&amp;nbsp;지원을&amp;nbsp;제공하는&amp;nbsp;&lt;a href=&quot;https://www.herodevs.com/support&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HeroDevs&lt;/a&gt;의&amp;nbsp;파트너와도&amp;nbsp;협력하고&amp;nbsp;있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;삶의 질을 높여주는 개선사항&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;미래를 지향하는 굵직한 변경사항 정도는 아니지만, 이번에는 삶의 질을 살짝이라도 개선할 수 있는 기능을 소개합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;뷰 전환 효과 - 실험적 지원&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://developer.chrome.com/docs/web-platform/view-transitions/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;뷰 전환 API&lt;/a&gt;를 활용하면 전환 효과를 적용하면서 자연스럽게 DOM을 변경할 수 있습니다. 이제는 &lt;code class=&quot;language-typescript&quot;&gt;withViewTransitions&lt;/code&gt; 를 활용하는 방식으로 Angular 라우터가 뷰 전환 API를 직접 지원하며, 전환 효과는 브라우저의 네이티브 성능으로 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 기능을 사용하려면 애플리케이션 부트스트랩 단계에서 라우터 프로바이더를 선언하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;bootstrapApplication(App, {
  providers: [
    provideRouter(routes, withViewTransitions()),
  ]
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;withViewTransitions&lt;/code&gt;는 &lt;code class=&quot;language-typescript&quot;&gt;onViewTransitionCreated&lt;/code&gt; 프로퍼티가 있는 객체를 옵션으로 받을 수 있습니다. 이&amp;nbsp;객체를&amp;nbsp;활용하면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전환 효과를 적용하지 않는 경우를 지정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;전환 효과가 적용되는 동안에만 추가되었다가 제거되는 스타일 지정용 클래스를 추가할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;자동 연결되는 이미지 디렉티브&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular의 이미지 디렉티브는 이제 이미지 로더에 인자로 지정한 도메인에 대해 사전 링크를 자동으로 생성합니다. 이&amp;nbsp;때&amp;nbsp;오리진을&amp;nbsp;식별하지&amp;nbsp;못하거나&amp;nbsp;사전&amp;nbsp;링크를&amp;nbsp;감지하지&amp;nbsp;못하면&amp;nbsp;개발&amp;nbsp;단계에서&amp;nbsp;경고가&amp;nbsp;표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 &lt;a href=&quot;https://angular.io/guide/image-directive&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이미지 디렉티브 가이드 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;애니메이션 모듈을 지연 로딩하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 기능을 활용하면 처음 로딩되는 빌드 결과물의 크기를 60KB(gzip 압축하면 16KB) 줄일 수 있습니다. 이&amp;nbsp;기능은&amp;nbsp;커뮤니티&amp;nbsp;컨트리뷰터인&amp;nbsp;&lt;a href=&quot;https://github.com/JeanMeche&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Matthieu&amp;nbsp;Riegler&lt;/a&gt;가&amp;nbsp;제안해줬으며,&amp;nbsp;이렇게&amp;nbsp;사용하면&amp;nbsp;됩니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { provideAnimationsAsync } from '@angular/platform-browser/animations-async';

bootstrapApplication(RootCmp, {
  providers: [provideAnimationsAsync()]
});&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;b&gt;입력값 보정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;컴포넌트에 불리언 타입으로 입력 프로퍼티를 받는 경우는 아주 흔한 케이스입니다. 그런데 이런 경우는 보통 컴포넌트에 전달하는 값의 타입을 보정해야 하기도 합니다. 이런&amp;nbsp;컴포넌트가&amp;nbsp;있다고&amp;nbsp;합시다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;@Component({
  standalone: true,
  selector: 'my-expander',
  template: `…`
})
export class Expander {
  @Input() expanded: boolean = false;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 컴포넌트를 이렇게 사용하려고 합니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;my-expander expanded/&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;그러면 문자열 값을 불리언 타입에 할당할 수 없다는 에러를 만나게 될 것입니다. 이런&amp;nbsp;경우에는&amp;nbsp;입력&amp;nbsp;프로퍼티를&amp;nbsp;이렇게&amp;nbsp;작성하면&amp;nbsp;됩니다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;@Component({
  standalone: true,
  selector: 'my-expander',
  template: `…`
})
export class Expander {
  @Input({ transform: booleanAttribute }) expanded: boolean = false;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;HTML 어트리뷰트에 불리언 프로퍼티를 사용할 수 있도록 요청했던 &lt;a href=&quot;https://github.com/angular/angular/issues/14761&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub 리퀘스트&lt;/a&gt;를 직접 확인해 보세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;styles, styleUrls에 문자열 사용하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular 컴포넌트에는 스타일 시트를 여러개 적용할 수 있습니다. 하지만 보통은 스타일 시트 하나만 적용하거나 인라인 스타일을 작성하는 경우가 대다수입니다. 그래서&amp;nbsp;이런&amp;nbsp;경우들이라면:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;@Component({
  styles: [`
    ...
  `]
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;...
@Component({
  styleUrls: ['styles.css']
})
...&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;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;@Component({
  styles: `
    ...
  `
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;...
@Component({
  styleUrl: 'styles.css'
})
...&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;커뮤니티 스키매틱 제작 지원&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개발자 커뮤니티에서 스키매틱을 만들 때 활용할 수 있는 유틸리티 메서드들을 모아서 &lt;code class=&quot;language-typescript&quot;&gt;@schematics/angular/utility&lt;/code&gt;로 제공합니다. 이&amp;nbsp;패키지를&amp;nbsp;활용하면&amp;nbsp;Angular&amp;nbsp;앱의&amp;nbsp;가장&amp;nbsp;깊숙한&amp;nbsp;곳까지&amp;nbsp;직접&amp;nbsp;접근해서&amp;nbsp;원하는&amp;nbsp;기능을&amp;nbsp;가져올&amp;nbsp;수&amp;nbsp;있으며,&amp;nbsp;&lt;code class=&quot;language-typescript&quot;&gt;package.json&lt;/code&gt;에&amp;nbsp;종속성을&amp;nbsp;추가하는&amp;nbsp;기능도&amp;nbsp;구현할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자세한 내용은 &lt;a href=&quot;https://angular.io/guide/schematics-for-libraries&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스키매틱 가이드 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Angular 개발자 학습 코스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular 팀은 대화형 교육 플랫폼을 운영하는 SoloLearn과 협업해서 &lt;a href=&quot;https://www.youtube.com/watch?v=xAT0lHYhHMY&amp;amp;list=PL1w1q3fL4pmj9k1FrJ3Pe91EPub2_h4jF&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&quot;Introduction to Angular&quot;&lt;/a&gt;라는 학습 코스를 런칭했습니다. 최근&amp;nbsp;2개월동안&amp;nbsp;이&amp;nbsp;학습&amp;nbsp;코스를&amp;nbsp;거쳐간&amp;nbsp;개발자들은&amp;nbsp;7만명이&amp;nbsp;넘습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0_FmVa3QI6S7tiKhzC.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOhpF0/btsAFFU2vI2/YdmJxeOQCeTJhkSLSQvGxK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOhpF0/btsAFFU2vI2/YdmJxeOQCeTJhkSLSQvGxK/img.webp&quot; data-alt=&quot;SoloLearn이&amp;amp;nbsp;제공하는&amp;amp;nbsp;Angular&amp;amp;nbsp;학습&amp;amp;nbsp;코스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOhpF0/btsAFFU2vI2/YdmJxeOQCeTJhkSLSQvGxK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOhpF0%2FbtsAFFU2vI2%2FYdmJxeOQCeTJhkSLSQvGxK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;788&quot; data-filename=&quot;0_FmVa3QI6S7tiKhzC.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SoloLearn이&amp;nbsp;제공하는&amp;nbsp;Angular&amp;nbsp;학습&amp;nbsp;코스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자세한 내용은 &lt;a href=&quot;https://blog.angular.io/new-free-interactive-angular-course-for-beginners-on-sololearn-7a4c4f91810a&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;커뮤니티 하이라이트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Angular v17을 더 특별하게 만들어준 346명의 컨트리뷰터들에게 감사를 전합니다! 이&amp;nbsp;중&amp;nbsp;주목할만한&amp;nbsp;소식은&amp;nbsp;이런&amp;nbsp;것들이&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;code class=&quot;language-typescript&quot;&gt;httpClient&lt;/code&gt;가 &lt;a href=&quot;https://github.com/angular/angular/pull/50247&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;백엔드에서 동작하도록 확장&lt;/a&gt;되면서 Angular가 엣지 워커에서 동작할 수 있습니다. &lt;a href=&quot;https://github.com/JeanMeche&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Matthieu Riegler&lt;/a&gt;에게 감사를 전합니다.&lt;/li&gt;
&lt;li&gt;Matthieu는 &lt;a href=&quot;https://github.com/angular/angular/pull/52029&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;POST 요청의 헤더, 필터, 캐시를 커스터마이징&lt;/a&gt;하는 &lt;code class=&quot;language-typescript&quot;&gt;HttpTransferCache&lt;/code&gt; 기능 개발에도 기여했습니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cexbrayat&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;C&amp;eacute;dric Exbrayat&lt;/a&gt;는 새로운 애플리케이션 빌더에 활용되는 &lt;a href=&quot;https://github.com/angular/angular-cli/pull/25913&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;namedChunks&lt;/code&gt;를 도입&lt;/a&gt;해습니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tomalaforge&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Thomas Laforge&lt;/a&gt;가 제공하는&lt;a href=&quot;https://angular-challenges.vercel.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; Angular Challenges&lt;/a&gt;는 Angular 개발자들이 다음 단계로 도약할 수 있도록 돕는 훌륭한 학습 코스입니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://analogjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AnalogJS&lt;/a&gt;는 꾸준히 성장해서 이제 1.0 버전을 준비하고 있습니다. &lt;a href=&quot;https://github.com/brandonroberts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Brandon Roberts&lt;/a&gt;의 이 엄청난 작업을 응원합니다!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/santoshyadavdev&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Santosh Yadav&lt;/a&gt;의 &lt;a href=&quot;https://www.youtube.com/watch?v=3qBXWUpoPHo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Angular 초심자 코스&lt;/a&gt;는 1백만 뷰를 돌파했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Angular로 미래를 만드세요&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;Angular 팀은 지난 6개월동안 더 나은 개발자 경험과 성능을 향상시키는 기능을 지속적으로 발표하며 Angular 르네상스를 이어왔습니다. 그리고&amp;nbsp;오늘은&amp;nbsp;Angular의&amp;nbsp;새로운&amp;nbsp;브랜드와&amp;nbsp;&lt;a href=&quot;http://angular.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;angular.dev&lt;/a&gt;를&amp;nbsp;소개하면서&amp;nbsp;전환점을&amp;nbsp;맞이하게&amp;nbsp;된&amp;nbsp;것&amp;nbsp;같아&amp;nbsp;아주&amp;nbsp;기쁜&amp;nbsp;날입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;Angular로 미래를 만드는 당신의 여정에 함께 할 수 있어서 영광입니다! 감사합니다! &lt;/p&gt;</description>
      <author>한장현</author>
      <guid isPermaLink="true">https://han41858.tistory.com/57</guid>
      <comments>https://han41858.tistory.com/57#entry57comment</comments>
      <pubDate>Wed, 22 Nov 2023 00:13:43 +0900</pubDate>
    </item>
    <item>
      <title>Angular v12.0.0 릴리즈 노트</title>
      <link>https://han41858.tistory.com/56</link>
      <description>&lt;p&gt;안녕하세요. 한장현입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한국시각으로 2021년 5월 13일 오전에 Angular 12.0.0 버전이 릴리즈 되었습니다.&lt;br /&gt;벌써 &lt;a href=&quot;https://angular.io&quot;&gt;Angular 공식 가이드 문서&lt;/a&gt;에도 새 버전이 반영되었습니다 :)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.angular.io/angular-v12-is-now-available-32ed51fbfd49&quot;&gt;이번 릴리즈 노트&lt;/a&gt;에는 어떤 내용이 있는지 살펴봤습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/96FNw/btq4O9SUZEy/b7bux0wfjWXd7lotWvDdKk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/96FNw/btq4O9SUZEy/b7bux0wfjWXd7lotWvDdKk/img.jpg&quot; data-alt=&quot;샌프란시스코의 알라모 스퀘어 공원 - Minko Gechev&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/96FNw/btq4O9SUZEy/b7bux0wfjWXd7lotWvDdKk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F96FNw%2Fbtq4O9SUZEy%2Fb7bux0wfjWXd7lotWvDdKk%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;샌프란시스코의 알라모 스퀘어 공원 - Minko Gechev&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;드디어 돌아왔습니다, 여러분. Angular v12가 새로 릴리즈 되었다는 기쁜 소식을 빠르게 전해드리겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;업데이트된 내용을 살펴보기 전에 Angular가 걸어온 길을 잠시 돌아봅시다. 현재 Angular의 핵심은 Ivy이며 이 렌더링 엔진은 특정 플랫폼에 종속되지 않습니다. 그리고 지난 몇 번의 릴리즈 동안 저희는 Angular 생태계를 기존 렌더링 엔진에서 Ivy로 전환하기 위한 작업을 해왔습니다. 이 과정을 저희는 &quot;Ivy Everywhere&quot;라고 불렀습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 과정을 거치면서 이런 내용들이 변경되었습니다.&lt;/p&gt;
&lt;h2&gt;Ivy Everywhere 달성하기&lt;/h2&gt;
&lt;p&gt;Angular가 다음 단계로 나아가기 위한 중요한 작업이 드디어 끝났습니다. 이제 View Engine 지원이 완전히 중단되었습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;View Engine 지원이 중단되었기 때문에 다음 메이저 릴리즈에는 View Engine이 제거될 것입니다.&lt;/li&gt;
&lt;li&gt;View Engine 기반으로 구현된 라이브러리는 개발자가 별도로 작업하지 않아도 Ivy 앱에서 동작합니다. 하지만 라이브러리 개발자들은 이제 Ivy로 전환할 준비를 시작해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;자세한 내용은 &lt;a href=&quot;https://blog.angular.io/upcoming-improvements-to-angular-library-distribution-76c02f782aa4&quot;&gt;이 블로그 글&lt;/a&gt;에 작성해두었습니다. 라이브러리 개발자분들이나 이 내용에 관심있는 분은 확인해 보세요.&lt;/p&gt;
&lt;h2&gt;오래된 i18n 메시지 ID 형식 버리기&lt;/h2&gt;
&lt;p&gt;지금까지는 i18n 시스템에 오래된 메시지 ID 형식들이 사용되고 있었습니다. 이 형식들은 공백 문자를 사용하거나 템플릿 형태를 조작할 때, ICU 표현식을 사용할 때 제대로 동작하지 않는 경우가 많았습니다. 이 문제를 해결하기 위해 저희는 이 형식들을 버리기로 결정했습니다. 이제는 더 유연하고 직관적인 새 메시지 ID 형식을 사용합니다. 공백 문자가 맞지 않아서 번역이 제대로 적용되지 않는 문제는 이전보다 줄어들 것이고, 번역 작업을 다시 해야 하는 스트레스도 함께 줄어들 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Angular 11 버전으로 프로젝트를 생성하면 이미 새로운 메시지 ID 형식이 자동으로 적용되고 있습니다. 이보다 이전 버전으로 만든 프로젝트에서 번역 파일을 마이그레이션 하려면 &lt;a href=&quot;https://v12.angular.io/guide/migration-legacy-message-id&quot;&gt;이 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;h2&gt;Protractor 대체하기&lt;/h2&gt;
&lt;p&gt;Angular 팀은 커뮤니티와 함께 논의하며 Protractor를 대체하는 작업을 준비하고 있습니다. 그리고 지금은 &lt;a href=&quot;https://github.com/angular/protractor/issues/5502&quot;&gt;RFC&lt;/a&gt;로 접수된 피드백들을 검토하고 있지만, 아직은 대체할 툴을 확정하지 못했습니다. 그래서 새 프로젝트를 만들 때 사용할 툴을 고정한 상태는 아니며, 괜찮은 서드파티 솔루션을 Angular CLI와 함께 사용할 수 있도록 옵션 형태로 제공하고 있습니다. 현재는 Protractor 대신 Cypress, WebdriverIO, TestCafe를 이런 형태로 사용할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;Null 병합(Nullish Coalescing)&lt;/h2&gt;
&lt;p&gt;TypeScript 코드에서 Null 병합 연산자(&lt;code class=&quot;language-typescript&quot;&gt;??&lt;/code&gt;)를 사용하면 코드를 더 깔끔하게 작성할 수 있습니다. Angular v12 버전부터는 이 기능을 템플릿에서도 사용할 수 있습니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;템플릿에 이런 코드가 있다고 합시다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;{{age !== null &amp;amp;&amp;amp; age !== undefined ? age : calculateAge() }}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 이 코드는 이렇게 작성할 수 있습니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;{{ age ?? calculateAge() }}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 기능은 새 버전에서 바로 사용할 수 있습니다!&lt;/p&gt;
&lt;h2&gt;Angular 학습하기&lt;/h2&gt;
&lt;p&gt;Angular 팀은 개발자들이 Angular를 학습하는 과정을 개선하기 위해서도 힘쓰고 있습니다. 그래서 가이드 문서도몇가지변경된 내용이 있습니다. 실제로 활용할 수 있는 내용을 추가해서 &lt;a href=&quot;https://v12.angular.io/guide/content-projection&quot;&gt;컨텐츠 프로젝션(Content Projection)&lt;/a&gt; 가이드 문서를 새로 추가하기도 했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;또 다른 이슈도 있었습니다. Angular 팀은 개발자들이 직접 문서를 수정할 수 있냐는 질문을 수도 없이 받았습니다. 그래서 이제는 직접 가이드 문서를 개선하고 싶은 분들을 위해 &lt;a href=&quot;https://angular.io/guide/contributors-guide-overview&quot;&gt;컨트리뷰터 가이드&lt;/a&gt;를 추가했습니다.&lt;/p&gt;
&lt;p&gt;하나 더 있습니다. 이전 릴리즈에는 에러 메시지와 관련된 &lt;a href=&quot;https://blog.angular.io/angular-debugging-guides-dfe0ef915036&quot;&gt;가이드 문서와 영상 자료&lt;/a&gt;를 추가했습니다. 이 내용은 에러를 디버깅하는 데에 큰 도움이 된다는 피드백을 커뮤니티로부터 받았습니다. 아직 보지 못했다면 &lt;a href=&quot;https://angular.io/errors&quot;&gt;어떤 내용&lt;/a&gt;이 있는지 직접 확인해 보세요!&lt;/p&gt;
&lt;h2&gt;스타일 지정방식 개선&lt;/h2&gt;
&lt;p&gt;Angular v12 버전부터는 Angular 컴포넌트가 인라인 Sass를 지원하기 때문에 &lt;code class=&quot;language-typescript&quot;&gt;@Component&lt;/code&gt; 데코레이터 &lt;code class=&quot;language-typescript&quot;&gt;styles&lt;/code&gt; 안에 Sass 코드를 직접 작성할 수 있습니다. 이전까지 Sass는 Angular 컴파일러를 거쳐야만 사용할 수 있었습니다. 하지만 이제는 &lt;code class=&quot;language-typescript&quot;&gt;angular.json&lt;/code&gt;파일에 &lt;code class=&quot;language-typescript&quot;&gt;&quot;inlineStyleLanguage&quot;: &quot;scss&quot;&lt;/code&gt;를 추가하면 바로 사용할 수 있습니다. 새 프로젝트를 만드는 경우에도 물론 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Angular는 v11.2 버전부터 Tailwind CSS를 지원하고 있습니다. Tailwind CSS를 사용하려면 &lt;a href=&quot;https://www.npmjs.com/package/tailwindcss&quot;&gt;npm 패키지&lt;/a&gt;를 설치하고 프로젝트에 &lt;code class=&quot;language-typescript&quot;&gt;tailwind.config.js&lt;/code&gt; 파일을 생성하면 됩니다. 이제 Angular 안에서 Tailwind를 자유롭게 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Angular CDK와 Angular 매터리얼은 내부적으로&lt;a href=&quot;https://sass-lang.com/blog/the-module-system-is-launched&quot;&gt;Sass의 최신 모듈 시스템&lt;/a&gt;을 사용하고 있습니다. 애플리케이션이 Angular CDK나 Angular Material을 사용하고 있다면 &lt;code class=&quot;language-typescript&quot;&gt;node-sass&lt;/code&gt; 패키지가 아니라 &lt;code class=&quot;language-typescript&quot;&gt;sass&lt;/code&gt; 패키지를 사용하고 있는지 꼭 확인해 보세요. &lt;code class=&quot;language-typescript&quot;&gt;node-sass&lt;/code&gt; 패키지는 현재 관리되고 있지 않으며 새로 추가된 Sass 스펙을 반영하고 있지 않습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 Angular CDK와 Angular 매터리얼은 모두 &lt;code class=&quot;language-typescript&quot;&gt;@use&lt;/code&gt; 문법을 사용할 수 있는 형태로도 API를 제공하고 있습니다. 직접 사용하는 방식과 비교해볼 때 API를 사용하는 방식이 다를뿐이지 동작은 같지만, 좀 더 명확하고 이해하기 쉬운 형태로 사용할 수 있습니다. 그리고 이 내용을 반영하기 위해 &lt;a href=&quot;https://material.angular.io/guides&quot;&gt;material.angular.io&lt;/a&gt; 문서도 완전히 새롭게 개편되었습니다. 이전보다 자세한 설명과 설계 의도, API를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-bash&quot;&gt;ng update&lt;/code&gt; 명령을 실행하면 Angular v12 버전으로 전환하면서 Angular CDK와 Angular 매터리얼 코드도 새로운 Sass API 형태로 자동 전환됩니다. 이 명령을 실행하면 Angular CDK와 Angular 매터리얼이 사용하는 Sass &lt;code class=&quot;language-typescript&quot;&gt;@import&lt;/code&gt; 구문이 &lt;code class=&quot;language-typescript&quot;&gt;@use&lt;/code&gt;로 변경됩니다. 어떻게 변경되는지 &lt;a href=&quot;https://gist.github.com/MarkTechson/6283b6a3b353f9e38964af0740e29280&quot;&gt;예제&lt;/a&gt;를 확인해 보세요.&lt;/p&gt;
&lt;h2&gt;또 다른 기능들&lt;/h2&gt;
&lt;p&gt;이번 릴리즈에 변경된 내용 중에서 놓치면 안되는 것들을 더 살펴봅시다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-typescript&quot;&gt;ng build&lt;/code&gt; 명령이 운영용을 기본으로 빌드하도록 변경되었습니다. 이전보다 빌드 과정이 간단해졌으며 개발용 빌드가 운영 환경에 배포되는 사고를 방지할 수 있습니다!&lt;/li&gt;
&lt;li&gt;엄격한(strict) 모드가 기본값이 되었습니다. 엄격한 모드를 사용하면 앱이 실행될 때 발생할 수 있는 에러를 개발 단계에서 미리 잡아낼 수 있습니다. 엄격한 모드에 대해 자세하게 알아보려면 &lt;a href=&quot;https://angular.io/guide/strict-mode&quot;&gt;이 문서&lt;/a&gt;와 &lt;a href=&quot;https://blog.angular.io/with-best-practices-from-the-start-d64881a16de8&quot;&gt;블로그 글&lt;/a&gt;을 참고하세요.&lt;/li&gt;
&lt;li&gt;언어 지원 서비스(Language Service)가 Ivy 기반으로 전환되었습니다. 언어 지원 서비스를 활용하면 코드 자동완성, 에러 검출, 힌트, 코드 참조/이동 기능을 사용할 수 있기 때문에 생산성을 대폭 높일 수 있습니다. 자세한 내용은 &lt;a href=&quot;https://www.youtube.com/watch?v=doVYC32hjIw&quot;&gt;이 영상&lt;/a&gt;을 확인하세요.&lt;/li&gt;
&lt;li&gt;Angular v11 버전은 Webpack 5를 실험적으로 지원했습니다. 이제는 Webpack 5 정식 버전이 Angular에 사용됩니다.&lt;/li&gt;
&lt;li&gt;TypeScript 버전이 4.2로 상향되었습니다. TypeScript 4.2 버전에 대한 내용은&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-2/&quot;&gt;이 글&lt;/a&gt;을 참고하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;IE11 지원 중단&lt;/h2&gt;
&lt;p&gt;Angular는 살아있는 플랫폼이기 때문에 급격하게 진화하는 웹 생태계에서도 최신 상태를 유지하려고 합니다. 그래서 오래된 브라우저에 대한 지원을 중단하면서 최근에 더 좋은 방식으로 개선된 사용방법을 개발자들에게 제공하고 싶습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 IE11에서 Angular v12 버전으로 개발된 앱을 실행하면 지원이 중단된다는 경고 메시지가 표시됩니다.&lt;br /&gt;Angular v13 버전에서는 완전히 지원이 중단될 예정입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 논의가 어떻게 이루어졌는지 확인하려면 &lt;a href=&quot;https://github.com/angular/angular/issues/41840&quot;&gt;이 RFC&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;h2&gt;커뮤니티의 지원&lt;/h2&gt;
&lt;p&gt;Angular 커뮤니티에서 보내주신 지원은 Angular 프레임워크의 발전에 큰 도움이 되면서 결국 모든 Angular 개발자들의 개발 경험을 개선하고 있습니다. 감사합니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그 중 주목할만한 PR은 이런 것들이 있었습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;네비게이션동작 중 불필요한 &lt;code class=&quot;language-typescript&quot;&gt;ngZone&lt;/code&gt; 경고 메시지 제거(&lt;a href=&quot;https://github.com/angular/angular/pull/25839&quot;&gt;#25839&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-typescript&quot;&gt;HttpClient&lt;/code&gt;에 메타데이터 지정 기능 추가(&lt;a href=&quot;https://github.com/angular/angular/pull/25751&quot;&gt;#25751&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;폼 유효성 검사기에 &lt;code class=&quot;language-typescript&quot;&gt;min&lt;/code&gt;, &lt;code class=&quot;language-typescript&quot;&gt;max&lt;/code&gt; 추가(&lt;a href=&quot;https://github.com/angular/angular/pull/39063&quot;&gt;#39063&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;옵저버블을사용할 때 &lt;code class=&quot;language-typescript&quot;&gt;APP_INITIALIZER&lt;/code&gt; 지원(&lt;a href=&quot;https://github.com/angular/angular/pull/33222&quot;&gt;#33222&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;Angular 팀은 다양한 분야에 걸쳐 커뮤니티와 협업하고 있습니다. 새로운 내용을 빠르게 확인하려면 저희 &lt;a href=&quot;https://twitter.com/angular&quot;&gt;트위터&lt;/a&gt;를 팔로우하거나 &lt;a href=&quot;https://www.youtube.com/channel/UCbn1OgGei-DV7aSRo_HaAiw&quot;&gt;유튜브 채널&lt;/a&gt;을 확인해 보세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Angular v12에 변경된 내용 중에서 가장 흥미있는 내용은 무엇인가요? &lt;a href=&quot;https://blog.angular.io/angular-v12-is-now-available-32ed51fbfd49&quot;&gt;이 블로그 글&lt;/a&gt;에 댓글을 달아주세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;훌륭한 Angular 커뮤니티의 일원으로 활동해주셔서 감사합니다.&lt;br /&gt;다음 소식을 전해드릴때가지계속 훌륭한 앱을 만들어 주세요!&lt;/p&gt;</description>
      <category>Angular</category>
      <author>한장현</author>
      <guid isPermaLink="true">https://han41858.tistory.com/56</guid>
      <comments>https://han41858.tistory.com/56#entry56comment</comments>
      <pubDate>Thu, 13 May 2021 13:51:39 +0900</pubDate>
    </item>
    <item>
      <title>Deno의 모든 것 : Node.js와 간단하게 비교해보기</title>
      <link>https://han41858.tistory.com/55</link>
      <description>&lt;p&gt;안녕하세요. 한장현입니다.&lt;/p&gt;
&lt;p&gt;Deno와 Node.js를 간단하게 비교해 본 글이 있어서 번역해 봤습니다.&lt;/p&gt;
&lt;p&gt;원문: &lt;a href=&quot;https://www.hiddenbrains.com/blog/deno-vs-nodejs-development.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.hiddenbrains.com/blog/deno-vs-nodejs-development.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;nodejs-vs-deno.webp&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;441&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHrW79/btqWKkPj5jy/swnKN8eqDLoW7mWnEFjyG1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHrW79/btqWKkPj5jy/swnKN8eqDLoW7mWnEFjyG1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHrW79/btqWKkPj5jy/swnKN8eqDLoW7mWnEFjyG1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHrW79%2FbtqWKkPj5jy%2FswnKN8eqDLoW7mWnEFjyG1%2Fimg.webp&quot; data-filename=&quot;nodejs-vs-deno.webp&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;441&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;최근 Deno v1이 릴리즈 되면서 큰 이슈가 되었습니다.&lt;br /&gt;&amp;nbsp;어떤 사람들은 이제 Node.js의 시대가 끝났다고 이야기하기도 했습니다. 하지만 이런 의견은 너무 과장되었으며 지나치게 낙관적인 시각입니다.&lt;br /&gt;&amp;nbsp;Deno는 Node.js와 비교될 수밖에 없습니다. 무엇보다도 Deno를 개발하고 있는 사람이 Node.js를 개발한 Ryan Dahl이기 때문입니다.&lt;/p&gt;
&lt;h2&gt;Deno&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;Deno는 V8 JavaScript 엔진과 Rust 언어로 개발된 JavaScript, TypeScript 실행환경입니다.&lt;/p&gt;
&lt;h2&gt;Deno의 강점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;잘 설계된 모듈 시스템을 갖추고 있습니다.&lt;/li&gt;
&lt;li&gt;보안이 핵심입니다.&lt;/li&gt;
&lt;li&gt;TypeScript를 바로 실행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;의존성 없이 실행할 수 있는 파일 하나로 배포됩니다.&lt;/li&gt;
&lt;li&gt;의존성 검사기(dependency inspector)와 코드 포매터(code formatter)를 내장하고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;보안&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;Node.js는 파일 시스템, 외부로 나가는 요청, 환경변수 등에 제한 없이 접근할 수 있습니다. 물론 이렇게 접근하는 방식이 편하지만 안좋은 점도 있습니다. 개발 환경이나 운영중인 환경에서 심각한 보안 문제를 초래할 수도 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deno에서는 &lt;code&gt;--alow-net&lt;/code&gt; 옵션을 사용해야 네트워크를 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;권한을 허용/제한할 때 커맨드라인 옵션을 사용합니다.&lt;/li&gt;
&lt;li&gt;예외를 적용하면 폴더에서 코드를 읽을 수 있습니다.&lt;/li&gt;
&lt;li&gt;커맨드라인에서 스크립트 파일을 실행할 때 권한을 허용하려면 플래그를 사용해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;Deno에서는 사용자가 명확하게 허용해야 권한이 부여되기 때문에, 프로그램 마음대로 파일을 삭제할 수 없습니다. Deno에서는 보안을 특히 신경 썼습니다. 명시적으로 권한을 허용하지 않으면 하드 드라이브에 접근할 수 없으며, 악성 코드도 마음대로 실행할 수 없습니다.&lt;/p&gt;
&lt;h3&gt;패키지 관리&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;벌써부터 머리가 복잡해지나요?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Node.js는 npm으로 패키지를 관리했습니다. 하지만 Deno는 패키지를 설치하거나 외부 링크로 패키지를 로드할 때 모두 URL을 사용합니다. 그래서 이제는 패키지를 관리할 &lt;code&gt;package.json&lt;/code&gt; 파일이 없으며 &lt;code&gt;node_modules&lt;/code&gt; 폴더도 없습니다. Deno의 장점 중 가장 대단한 것이 이것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그러면 Deno는 어떻게 패키지를 관리할까요? Deno에서는 아무 곳에서나 모듈을 로드해서 사용할 수 있습니다. 이제는 패키지를 중앙에서 관리할 필요가 없습니다. 다만, 취약점이 확인되지 않은 서드 파티 모듈을 사용할 때에도 제한이 없기 때문에 가장 논란이 많은 부분이기도 합니다.&lt;/p&gt;
&lt;h3&gt;표준 라이브러리&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;Node.js 첫 버전이 릴리즈될 때부터 JavaScript는 계속 변화하고 있었고 라이브러리도 개선되고 있었습니다. Deno는 개발자가 표준화된 방법으로 라이브러리와 공식 툴을 사용하려면 표준 라이브러리를 어떻게 제공해야 하는지 고민하고 있습니다.&lt;/p&gt;
&lt;h3&gt;모듈&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;Node.js가 개발되는 시점에 JavaScript 모듈은 CommonJS가 표준이었으며, npm도 이 방식을 지원했습니다. 이제는 ES Module이 새로운 표준이 되었으며, &lt;a href=&quot;https://jspm.org/&quot;&gt;jspm&lt;/a&gt;이 이 형식을 지원합니다.&lt;/p&gt;
&lt;h3&gt;Deno는 ES 모듈을 지원합니다.&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;Node.js에서는 &lt;code&gt;require&lt;/code&gt; 키워드로 CommonJS 모듈을 로드할 수 있으며, 표준 모듈, 서드파티 모듈, &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;npmjs.com&lt;/a&gt;에서 제공되는 모듈은 모두 이 형식입니다.&lt;br /&gt;&amp;nbsp;Deno에서는 &lt;code&gt;import&lt;/code&gt; 키워드에 URL을 지정하는 방식으로 모듈을 로드합니다. Deno 모듈은 어느 곳에서 호스팅되더라도 관계없으며, 한 곳에서 관리되지 않지만 로컬에 캐싱되고 컴파일되기도 합니다. 따라서 개발자가 의도하지 않는 이상 자동으로 업데이트되지도 않습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno 프로그램에 필요한 라이브러리를 처음 캐싱하고 나면 언제든 바로 실행할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;Node.js의 설계 한계&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;Node.js를 만들었고 지금은 Deno를 만들고 있는 &lt;a href=&quot;https://www.youtube.com/watch?v=1gIiZfSbEAE&quot;&gt;Ryan Dahl에 따르면&lt;/a&gt;, Node.js에는 설계 이슈가 몇가지 있습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;모듈 시스템 설계에 문제가 있었음. 패키지를 중앙에서 관리함&lt;/li&gt;
&lt;li&gt;오래된 API를 지원해야 함&lt;/li&gt;
&lt;li&gt;보안 관리 기능이 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;Deno는 이런 문제들을 해결했습니다.&lt;/p&gt;
&lt;h3&gt;Node.js와 다른 점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Deno는 안정성을 강조하는 Rust와 Tokio를 기반으로 작성되었습니다. Node.js는 C++로 개발되었습니다.&lt;/li&gt;
&lt;li&gt;코드가 실행되기 전에 발생할 수 있는 에러를 미리 검출하게 위해 TypeScript를 활용합니다.&lt;/li&gt;
&lt;li&gt;TypeScript는 대규모 어플리케이션에 특히 유리합니다.&lt;/li&gt;
&lt;li&gt;의존성 패키지를 로드할 때 ES6 &lt;code&gt;import&lt;/code&gt; 구문을 사용합니다.&lt;/li&gt;
&lt;li&gt;npm 같은 패키지 매니저를 사용하지 않습니다. package.json 파일도 없습니다.&lt;/li&gt;
&lt;li&gt;Deno에서 예외가 발생하면 프로그램이 종료됩니다. NodeJS에서는 아니었습니다.&lt;/li&gt;
&lt;li&gt;Deno에서 파일 시스템, 네트워크, 환경변수에 접근하려면 권한을 명시적으로 허용해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;deno-comparison-with-node.webp&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;995&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blX6Ze/btqWOgrzzJS/6mKeYL4z2XVPTb7mRzudek/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blX6Ze/btqWOgrzzJS/6mKeYL4z2XVPTb7mRzudek/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blX6Ze/btqWOgrzzJS/6mKeYL4z2XVPTb7mRzudek/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblX6Ze%2FbtqWOgrzzJS%2F6mKeYL4z2XVPTb7mRzudek%2Fimg.webp&quot; data-filename=&quot;deno-comparison-with-node.webp&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;995&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;Deno가 Node.js를 대체할까요?&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;아직은 모릅니다.&lt;br /&gt;&amp;nbsp;Node.js는 이미 거대한 생태계를 형성했습니다. Deno는 아직 초기 단계이며 Node.js를 따라잡기에는 아직 갈 길이 멉니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;지금은 TypeScript로 작은 프로젝트를 만드는 정도로는 아주 멋진 환경입니다.&lt;br /&gt;&amp;nbsp;Ryan Dahl에 따르면 Deno는 Node.js의 인기에 도전하는 프로젝트가 아니라고 하지만, Deno가 충분히 성숙하고 나면 대규모 프로젝트에 도입할 수 있는 선택지가 될 수는 있을 것입니다.&lt;/p&gt;
&lt;h2&gt;결론&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;Deno가 개발자들에게 인기를 얻을지는 시간이 지나 봐야 알 수 있습니다. 지금도 그렇지만 나중에도 Node.js는 제 갈 길을 갈 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&quot;Node는 갑자기 없어지거나 하지 않습니다.&quot; - Ryan Dahl&lt;/p&gt;</description>
      <author>한장현</author>
      <guid isPermaLink="true">https://han41858.tistory.com/55</guid>
      <comments>https://han41858.tistory.com/55#entry55comment</comments>
      <pubDate>Tue, 9 Feb 2021 21:52:51 +0900</pubDate>
    </item>
    <item>
      <title>웹 스토리지 : 무엇을 써야할까요?</title>
      <link>https://han41858.tistory.com/54</link>
      <description>&lt;p&gt;안녕하세요. 한장현입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;최근에 로컬 스토리지를 사용하려고 찾아봤더니 &lt;a href=&quot;https://web.dev/storage-for-the-web/&quot;&gt;이제는 로컬 스토리지를 사용하지 않는것이 좋다는 Google Chrome 팀 개발자의 글&lt;/a&gt;을 발견했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요즘 트렌드는 이렇구나 다시 한 번 느끼면서 글을 번역해 봤습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h1&gt;웹 스토리지&lt;/h1&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;브라우저에 데이터를 저장하는 방법은 많습니다. 이중에 무엇을 사용해야 할까요? 인터넷 연결은 불안정하며 언제든 끊어질 수 있습니다. 그래서 오프라인에서도 동작하며 불안정한 상황을 안정되게 유지하기 위해 &lt;a href=&quot;https://web.dev/progressive-web-apps/&quot;&gt;PWA(Progressive Web App)&lt;/a&gt;를 사용하기도 합니다. 연결이 완벽한 무선 환경에서도 캐시나 스토리지 기술을 적절하게 활용하면 사용자가 느끼는 앱 성능을 향상시킬 수 있습니다. 애플리케이션 정적 리소스(HTML, JavaScript, CSS, 이미지 등)나 데이터(사용자 데이터, 기사 글 등)를 캐싱하는 방법은 다양합니다. 이 중에서 어떤 것이 제일 좋을까요? 얼마나 저장할 수 있을까요? 데이터가 유실되는 것을 방지하려면 어떻게 해야 할까요?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;무엇을 사용해야 하나요?&lt;/h2&gt;
&lt;p&gt;리소스를 저장할 때는 이런 방법을 추천합니다:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;네트워크로 불러온 리소스나 파일을 캐싱해야 한다면 &lt;a href=&quot;https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api&quot;&gt;캐시 스토리지 API(Cache Storage API)&lt;/a&gt;를 활용하는 것이 좋습니다. 캐시 스토리지 API는 &lt;a href=&quot;https://developers.google.com/web/fundamentals/primers/service-workers&quot;&gt;서비스 워커(Service Worker)&lt;/a&gt;가 제공하는 기능 중 하나입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이 경우가 아니라면 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API&quot;&gt;IndexedDB&lt;/a&gt;를 활용하는 것이 좋습니다. IndexedDB를 &lt;a href=&quot;https://www.npmjs.com/package/idb&quot;&gt;프라미스(Promise) 형태로 랩핑한 라이브러리&lt;/a&gt;도 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;최근 브라우저들은 IndexedDB와 캐시 스토리지 API를 모두 지원합니다. 이 스토리지들은 모두 비동기(asynchronous)로 동작하기 때문에 메인 스레드 연산에 영향을 주지 않습니다. 그리고 &lt;code class=&quot;language-javascript&quot;&gt;window&lt;/code&gt; 객체로 접근할 수 있으며 웹 워커나 서비스 워커에서도 접근할 수 있기 때문에 자유롭게 활용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;다른 스토리지는 어떤가요?&lt;/h2&gt;
&lt;p&gt;브라우저에서 사용할 수 있는 스토리지는 두 가지 외에도 더 있습니다. 하지만 저장용량에 제한이 있거나 심각한 성능 이슈가 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/API/Window/sessionStorage&quot;&gt;세션 스토리지(SessionStorage)&lt;/a&gt;는 탭 안에서만 유효하며 탭이 닫히면 스토리지도 종료됩니다. 세션 스토리지는 현재 탭에서만 사용하는 IndexedDB 키를 잠시 저장하는 것과 같이 작은 용량 데이터를 저장할 때는 좋습니다. 하지만 이 스토리지는 동기(synchronous) 방식으로 동작하기 때문에 메인 스레드 연산을 중단시키기 때문에 조심해야 합니다. 용량 제한은 5MB이며 문자열만 저장할 수 있습니다. 그리고 탭 안에서만 유효하기 때문에 웹 워커나 서비스 워커가 접근할 수 없습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/API/Window/localStorage&quot;&gt;로컬 스토리지(LocalStorage)&lt;/a&gt;도 동기 방식으로 동작하며 메인 스레드 연산을 중단시키기 때문에 사용하지 않는 것이 좋습니다. 용량 제한은 5MB이며 문자열만 저장할 수 있습니다. 그리고 웹 워커나 서비스 워커에서 접근할 수 없습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies&quot;&gt;쿠키(Cookies)&lt;/a&gt;는 본연의 역할이 있기 때문에 스토리지로 사용하지 않는 것이 좋습니다. 그리고 쿠키는 HTTP 요청이 있을 때마다 외부로 함께 전달되기 때문에 데이터를 조금만 저장해도 소모하는 전체 데이터 양이 급격하게 늘어날 수 있습니다. 쿠키는 비동기로 동작하지만 웹 워커에서는 접근할 수 없으며, 로컬 스토리지나 세션 스토리지와 마찬가지로 문자열만 저장할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/File_and_Directory_Entries_API/Introduction&quot;&gt;파일 시스템 API&lt;/a&gt;나 FileWriter API를 활용하면 파일 시스템에 샌드박스 형태로 파일을 읽고 쓸 수 있습니다. 이 스토리지는 비동기로 동작하지만 &lt;a href=&quot;https://caniuse.com/#feat=filesystem&quot;&gt;Chromium 기반 브라우저에서만 동작&lt;/a&gt;하기 때문에 권장하지 않습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/file-system-access/&quot;&gt;파일 시스템 접근 API&lt;/a&gt;는 로컬 파일 시스템에 있는 파일을 쉽게 읽고 쓰기 위해 설계된 API입니다. 하지만 로컬 파일을 읽거나 쓸 때 반드시 권한을 받아야 하며 한 세션 안에서만 유효합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;WebSQL은 사용하면 안됩니다. 이미 사용하고 있다면 IndexedDB로 전환하는 것이 좋습니다. 왜냐하면 WebSQL은 최근 브라우저들에서 &lt;a href=&quot;https://caniuse.com/#feat=sql-storage&quot;&gt;지원이 중단&lt;/a&gt;되었으며, W3C는 2010년에 &lt;a href=&quot;https://www.w3.org/TR/webdatabase/&quot;&gt;WebSQL 스펙 관리를 중단&lt;/a&gt;했습니다. 더 이상 업데이트될 계획도 없습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;애플리케이션 캐시는 사용하면 안 됩니다. 이미 사용하고 있다면 서비스 워커나 캐시 API로 전환하는 것이 좋습니다. 이 기능은 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/applicationCache&quot;&gt;지원이 중단&lt;/a&gt;되었으며 앞으로는 브라우저에서 제거될 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;얼마나 저장할 수 있나요?&lt;/h2&gt;
&lt;p&gt;간단하게 말해서 &lt;b&gt;많이&lt;/b&gt; 저장할 수 있습니다. MB 단위부터 GB 단위 이상도 가능합니다. 브라우저마다 다를 수 있지만, 일반적으로 스토리지에 저장할 수 있는 양은 디바이스 스토리지 크기에 따라 달라집니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Chrome 기반 브라우저는 원래 디스크 전체 용량의 80%까지 저장할 수 있지만, 공식 Chrome은 디스크 전체 용량의 60%까지 저장할 수 있습니다.&lt;br /&gt;&lt;a href=&quot;https://web.dev/storage-for-the-web/#check&quot;&gt;StorageManage API&lt;/a&gt;를 활용하면 저장할 수 있는 최대 용량이 얼마나 되는지 확인할 수 있습니다.&lt;br /&gt;크로미움 기반의 다른 브라우저들은 사용할 수 있는 용량이 조금 더 많습니다.&lt;br /&gt;Chrome은 왜 다른지 확인하려면 &lt;a href=&quot;https://github.com/GoogleChrome/web.dev/pull/3896&quot;&gt;이 이슈&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Internet Explorer는 10 버전부터 250MB까지 저장할 수 있으며, 저장된 용량이 10MB를 넘어가면 정말 저장할 것인지 물어보는 팝업이 뜹니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;FireFox는 디스크 빈 용량의 50%까지 저장할 수 있습니다.&lt;br /&gt;그리고 &lt;code class=&quot;language-bash&quot;&gt;example.com&lt;/code&gt;이나 &lt;code class=&quot;language-bash&quot;&gt;www.example.com&lt;/code&gt;, &lt;code class=&quot;language-bash&quot;&gt;foo.bar.example.com&lt;/code&gt;과 같은 &lt;a href=&quot;https://godoc.org/golang.org/x/net/publicsuffix&quot;&gt;eTLD+1 그룹&lt;/a&gt;은 2GB까지 저장할 수 있습니다.&lt;br /&gt;얼마나 저장할 수 있는지 확인하려면 &lt;a href=&quot;https://web.dev/storage-for-the-web/#check-available&quot;&gt;StorageManager API&lt;/a&gt;를 활용하면 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Safari(데스크탑, 모바일)는 1GB까지 저장할 수 있습니다.&lt;br /&gt;이 용량을 다 사용하면 용량 제한을 200MB 더 늘릴 것인지 물어보는 팝업이 뜹니다.&lt;br /&gt;관련 문서는 찾지 못했습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이전에는 저장할 수 있는 용량 제한을 넘어가면 더 많은 데이터를 저장할 것인지 물어보는 팝업이 떴습니다. 50MB를 저장하고 나면 100MB까지 늘릴 것인지 물어보는 식인데, 이 팝업은 저장 용량이 50MB를 넘어갈 때마다 반복해서 뜹니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 요즘 브라우저들은 사용할 수 있는 용량을 다 채울 때까지 사용자에게 물어보지 않습니다. Safari만 다릅니다. Safari는 750MB를 저장하고 나면 1.1GB까지 저장 공간을 사용할 것인지 물어보는 팝업이 뜹니다. 용량 제한을 넘어간 상태에서 새로운 데이터를 쓰려고 하면 오류가 발생합니다.&lt;/p&gt;
&lt;h2&gt;스토리지를 얼마나 사용할 수 있는지 확인하는 방법&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://caniuse.com/#feat=mdn-api_storagemanager&quot;&gt;요즘 브라우저&lt;/a&gt;들은 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/estimate&quot;&gt;StorageManager API&lt;/a&gt;를 활용해서 현재 저장 공간을 얼마나 사용하고 있는지, 총 얼마나 저장할 수 있는지 확인할 수 있습니다. 이 API를 활용하면 IndexedDB와 캐시 API가 사용하고 있는 용량을 바이트(byte) 단위로 확인할 수 있으며, 앞으로 얼마나 더 사용할 수 있는지도 확인할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;if (navigator.storage &amp;amp;&amp;amp; navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -&amp;gt; 사용 중인 용량(byte)
  // quota.quota -&amp;gt; 사용할 수 있는 전체 용량(byte)
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`사용할 수 있는 용량의 ${percentageUsed}%를 사용하고 있습니다.`);
  const remaining = quota.quota - quota.usage;
  console.log(`앞으로 ${remaining} 바이트를 더 사용할 수 있습니다.`);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다만, 모든 브라우저가 &lt;a href=&quot;https://caniuse.com/#feat=mdn-api_storagemanager&quot;&gt;StorageManager API&lt;/a&gt;를 지원하는 것은 아닙니다. 그래서 StorageManager API를 활용하는 코드를 작성하려면 대상 브라우저가 StorageManager API를 지원하는지 확인해야 합니다. 그리고 브라우저가 StorageManager API를 지원하더라도 사용할 수 있다고 하는 용량이 실제로 사용할 수 있는 용량을 넘어가는 경우가 있으니 주의해야 합니다. 이 내용은 아래 &quot;사용량을 넘어가는 에러(over-quota errors)&quot; 섹션에서 살펴봅시다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;크로미움 기반 브라우저에서 사용할 수 있는 용량을 확인하면 현재 디스크 용량의 비율로 용량을 표시합니다.&lt;br /&gt;하지만 공식 Chrome은 언제나 실제 디스크 전체 용량의 60%라고 표시합니다.&lt;br /&gt;저장된 리소스의 크기를 확실히 알 수 있다는 점에서는 좋습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;디버깅하기&lt;/h3&gt;
&lt;p&gt;개발 단계에서 브라우저 개발자 도구를 열면 스토리지가 여러 종류로 제공되는 것을 확인할 수 있습니다. 저장된 데이터도 간단하게 지울 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;storage.png&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;591&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YmclC/btqPibk6rkG/7iKMY3vu8CiAfaT5xAS3P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YmclC/btqPibk6rkG/7iKMY3vu8CiAfaT5xAS3P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YmclC/btqPibk6rkG/7iKMY3vu8CiAfaT5xAS3P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYmclC%2FbtqPibk6rkG%2F7iKMY3vu8CiAfaT5xAS3P0%2Fimg.png&quot; data-filename=&quot;storage.png&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;591&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;tool.png&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;1808&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZgeaO/btqPlcxfOnD/KJsQBnWzcTDmJdW0s2Kf81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZgeaO/btqPlcxfOnD/KJsQBnWzcTDmJdW0s2Kf81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZgeaO/btqPlcxfOnD/KJsQBnWzcTDmJdW0s2Kf81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZgeaO%2FbtqPlcxfOnD%2FKJsQBnWzcTDmJdW0s2Kf81%2Fimg.png&quot; data-filename=&quot;tool.png&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;1808&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이 글을 작성하면서 스토리지를 테스트해볼 수 있는 &lt;a href=&quot;https://storage-quota.glitch.me/&quot;&gt;툴&lt;/a&gt;을 만들어 봤습니다. 스토리지들이 어떻게 동작하는지 간단하게 확인할 수 있으며, 용량 제한을 넘어가면 어떤 일이 발생하는지도 확인할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;용량 제한 에러(over-quota errors)를 처리하는 방법&lt;/h2&gt;
&lt;p&gt;저장할 수 있는 용량을 모두 사용하면 어떻게 될까요? 이런 경우에는 &lt;code class=&quot;language-javascript&quot;&gt;QuotaExceededError&lt;/code&gt;와 같은 에러가 발생하기 때문에 이 에러를 계속 처리해야 합니다. 이 문제를 해결하는 방법은 앱을 어떻게 설계했느냐에 따릅니다. 오랫동안 사용하지 않는 데이터를 지우거나, 저장된 용량을 확인하면서 데이터를 조금씩 지울 수도 있고, 사용자가 지정한 데이터를 지우는 방법도 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;용량 제한을 넘어가면 IndexedDB와 캐시 API는 모두 &lt;code class=&quot;language-javascript&quot;&gt;QuotaExceededError&lt;/code&gt;라는 &lt;code class=&quot;language-javascript&quot;&gt;DOMError&lt;/code&gt;를 발생시킵니다.&lt;/p&gt;
&lt;h3&gt;IndexedDB&lt;/h3&gt;
&lt;p&gt;공식 Chrome에서 용량 제한을 넘어가면 IndexedDB 쓰기 동작이 실패합니다. 이때 &lt;code&gt;onabort()&lt;/code&gt; 핸들러가 실행되면서 에러 이벤트가 인자로 전달되는데, 이 에러 이벤트의 이름은 &lt;code&gt;QuotaExceededError&lt;/code&gt;이고 에러 이벤트 객체에서 &lt;code&gt;DomException&lt;/code&gt;을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const transaction = idb.transaction(['entries'], 'readwrite');

transaction.onabort = function(event) {
  const error = event.target.error; // DOMException
  if (error.name == 'QuotaExceededError') {
    // 실패했을 때 실행되는 코드
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;캐시 API&lt;/h3&gt;
&lt;p&gt;공식 Chrome에서 용량 제한을 넘어가면 캐시 API가 프라미스 형태로 &lt;code class=&quot;language-javascript&quot;&gt;QuotaExceededError&lt;/code&gt;를 반환합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;try {
  const cache = await caches.open('my-cache');
  await cache.add(new Request('/sample1.jpg'));
} catch (err) {
  if (error.name === 'QuotaExceededError') {
    // 실패했을 때 실행되는 코드
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;데이터는 어떻게 지워지나요?&lt;/h3&gt;
&lt;p&gt;웹 스토리지는 &quot;최적화(Best Effort) 스토리지&quot;와 &quot;데이터 보존(Persistent) 스토리지&quot;로 나눠볼 수 있습니다. 최적화 스토리지는 사용자의 개입 없이 브라우저가 자동으로 데이터를 비우기 때문에 오래 보관할 데이터나 중요한 데이터가 지워질 우려가 있습니다. 데이터 보존 스토리지는 저장 공간을 많이 사용하더라도 데이터가 자동으로 지워지지 않습니다. 브라우저 설정으로 사용자가 직접 데이터를 비워야 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;IndexedDB나 캐시 API, 기타 스토리지들도 기본적으로는 최적화 스토리지 방식으로 동작합니다. 따로 &lt;a href=&quot;https://developers.google.com/web/updates/2016/06/persistent-storage&quot;&gt;데이터를 보존하도록 지정&lt;/a&gt;하지 않는 이상 디바이스 용량이 꽉 차면 브라우저가 데이터를 자동으로 지웁니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;최적화 스토리지가 동작하는 방식은 이렇습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;크로미움 기반 브라우저들은 디바이스 용량이 부족할 때 가장 오래된 데이터부터 용량 제한을 해결할 때까지 데이터를 지웁니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Internet Explorer 10 버전부터는 데이터를 자동으로 지우지 않지만, 쓰기 작업을 더 이상 실행할 수 없습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Firefox는 디스크 용량이 꽉 찼을 때 가장 오래된 데이터부터 용량 제한을 해결할 때까지 데이터를 지웁니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Safari 이전 버전은 데이터를 자동으로 지우지 않았지만 요즘에는 최근 7일 데이터를 남겨두고 모든 스토리지를 비웁니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;iOS와 iPadOS 13.4, macOS용 Safari 13.1 버전부터 스토리지를 7일 분량만 남기는 정책이 적용되었습니다. 이때 스토리지는 IndexedDB, 서비스 워커 저장소, 캐시 API를 모두 포함합니다. 그래서 7일이 지난 데이터는 모두 삭제되기 때문에 더 이상 활용할 수 없습니다. 다만 이 정책은 홈 화면에 추가되어 PWA 형태로 설치된 웹 사이트에는 적용되지 않습니다. 자세한 내용은 WebKit 블로그 &lt;a href=&quot;https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/&quot;&gt;서드 파티 쿠키 모두 막기 외&lt;/a&gt; 문서를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;중요한 사용자 데이터나 애플리케이션 데이터를 유지하려면 &lt;a href=&quot;https://web.dev/persistent-storage/&quot;&gt;데이터 보존 스토리지&lt;/a&gt;를 사용하세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;보너스: 왜 IndexedDB 래퍼(wrapper)를 사용하나요?&lt;/h3&gt;
&lt;p&gt;IndexedDB는 낮은 계층에서 동작하는(low level) API이기 때문에 아주 간단한 데이터만 저장하려고 해도 복잡한 환경설정을 해야 합니다. 그리고 요즘 유행하는 프라미스 기반 API가 아니라 이벤트 기반으로 제공되는 API입니다. 그래서 &lt;a href=&quot;https://www.npmjs.com/package/idb&quot;&gt;idb&lt;/a&gt;와 같은 IndexedDB 래퍼를 활용하면 복잡한 환경설정 없이도 IndexedDB 기본 기능은 물론이고, 트랜잭션이나 스키마 버전 관리와 같이 복잡한 IndexedDB 기능을 쉽게 활용할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;결론&lt;/h2&gt;
&lt;p&gt;스토리지 용량 제한이 있고 이 용량 제한을 풀기 위해 사용자에게 확인을 받아야 했던 시대는 지났습니다. 이제 웹 사이트는 다양하고 효율적인 방법으로 리소스와 데이터를 저장할 수 있습니다. &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/estimate&quot;&gt;StorageManager API&lt;/a&gt;를 활용하면 지금 데이터를 얼마나 저장하고 있는지 확인할 수 있으며, 앞으로 얼마나 더 저장할 수 있는지도 확인할 수 있습니다. 그리고 &lt;a href=&quot;https://developers.google.com/web/updates/2016/06/persistent-storage&quot;&gt;데이터 보존 스토리지&lt;/a&gt;를 활용하면 사용자가 데이터를 직접 지우지 않는 이상 데이터를 안전하게 보관할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;참고&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/indexeddb-best-practices&quot;&gt;IndexedDB 활용 사례&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.google.com/document/d/19QemRTdIxYaJ4gkHYf2WWBNPbpuZQDNMpUVf8dQxj4U/preview&quot;&gt;Chrome 웹 스토리지와 용량 제한 개념&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;p&gt;생략&lt;/p&gt;</description>
      <author>한장현</author>
      <guid isPermaLink="true">https://han41858.tistory.com/54</guid>
      <comments>https://han41858.tistory.com/54#entry54comment</comments>
      <pubDate>Mon, 7 Dec 2020 01:00:23 +0900</pubDate>
    </item>
    <item>
      <title>Angular 11.0.0 릴리즈 노트</title>
      <link>https://han41858.tistory.com/53</link>
      <description>&lt;p&gt;안녕하세요. 한장현입니다.&lt;/p&gt;
&lt;p&gt;한국시각으로 2020년 11월 12일 오전에 Angular 11.0.0이 릴리즈되었습니다.&lt;br /&gt;&lt;a href=&quot;https://angular.io/&quot;&gt;Angular 공식 가이드 문서&lt;/a&gt;에 v11.0.0 버전이 빠르게 반영되었고, 공식 블로그에 &lt;a href=&quot;https://blog.angular.io/version-11-of-angular-now-available-74721b7952f7&quot;&gt;릴리즈 노트&lt;/a&gt;도 게재되었습니다.&lt;br /&gt;릴리즈 노트에 어떤 내용이 있는지 살펴봤습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img title=&quot;by Jules Kremer&quot; src=&quot;https://miro.medium.com/max/1184/0*55kr1t601sp22pkE&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Angular 11 버전이 릴리즈되었습니다.&lt;/h2&gt;
&lt;p&gt;전 세계 Angular 개발자들을 위해 야심 차게 준비한 Angular 11 버전이 드디어 나왔습니다. 이번 릴리즈는 프레임워크가 동작하는 플랫폼부터 CLI, 컴포넌트 등 Angular 전반에 걸쳐 변경된 내용이 있습니다. 지금부터 알아봅시다!&lt;/p&gt;
&lt;h1&gt;변경된 내용&lt;/h1&gt;
&lt;h2&gt;Operation Byelog&lt;/h2&gt;
&lt;p&gt;이전에 &lt;a href=&quot;https://angular.io/guide/roadmap&quot;&gt;Angular 로드맵에서 공유한 것&lt;/a&gt; 중에 Operation Byelog와 관련된 내용이 있었습니다. Angular 팀은 커뮤니티 구성원들이 원하는 것이 무엇인지 정확하게 알기 위해 이슈와 PR을 꼼꼼하게 분류하고 점검했습니다. 이제 그 결과가 나왔음을 알려드립니다! Operation Byelog 3개 저장소에서 관리되던 이슈들은 모두 처리되었으며, 앞으로 발견되는 이슈 처리에도 힘쓰겠습니다.&lt;/p&gt;
&lt;p&gt;앞으로의 목표는 이렇습니다: 새로 등록되는 이슈는 2주 안에 분류를 마치겠습니다.&lt;/p&gt;
&lt;p&gt;이 과정을 진행하면서 오래된 라우터 이슈 &lt;a href=&quot;https://github.com/angular/angular/issues/18469&quot;&gt;두&lt;/a&gt; &lt;a href=&quot;https://github.com/angular/angular/issues/13011&quot;&gt;개&lt;/a&gt;와 폼 &lt;a href=&quot;https://github.com/angular/angular/issues/14542&quot;&gt;이슈&lt;/a&gt;도 드디어 해결되었다는 것을 알려드립니다.&lt;/p&gt;
&lt;p&gt;국제화 &lt;a href=&quot;https://github.com/angular/angular/issues/11405&quot;&gt;이슈&lt;/a&gt;도 함께 해결되었습니다!&lt;/p&gt;
&lt;p&gt;이제는 Angular 커뮤니티를 어떻게 더 지원할 수 있을지 구체적인 계획을 마련하고 있습니다.&lt;br /&gt;앞으로도 계속해서 새로운 이슈를 분류하고 수정해 나갈 것이며 커뮤니티 구성원들의 기여를 받는 과정도 개선하겠습니다.&lt;/p&gt;
&lt;h2&gt;폰트 자동 내장&lt;/h2&gt;
&lt;p&gt;애플리케이션이 &lt;a href=&quot;https://web.dev/first-contentful-paint/&quot;&gt;처음 렌더링 되는 시점&lt;/a&gt;을 더 빠르게 당기기 위해 폰트 자동 내장(automatic font inlining) 기능을 도입했습니다. 앱에 사용되거나 링크된 폰트 파일은 이제 Angular CLI로 앱을 컴파일하는 시점에 다운받아져 애플리케이션 내부에 포함됩니다. 이 기능은 Angular 11부터 기본으로 적용됩니다. 아무것도 하지 않아도 앱 초기 실행이 얼마나 빨라지는지 확인해 보세요!&lt;/p&gt;
&lt;h2&gt;컴포넌트 테스트 하네스&lt;/h2&gt;
&lt;p&gt;컴포넌트 테스트 하네스(harnesses)는 Angular 9 버전에 도입된 기능이었습니다. 이 기능은 원래 Angular Material 컴포넌트를 테스트하기 위해 간단한 API 형태를 제공하는 기능이었습니다.&lt;/p&gt;
&lt;p&gt;Angular 11 버전부터는 모든 컴포넌트에 이 기능을 제공합니다! 이제 개발자가 테스트 스펙을 좀 더 다양하게 작성할 수 있습니다.&lt;/p&gt;
&lt;p&gt;기존에 있던 기능을 확장하며 성능을 개선했으며 새로운 API를 추가하기도 했습니다. 그리고 컴포넌트를 병렬로 테스트하더라도 모든 병렬 로직이 비동기냐에 관계없이 제대로 동작하기 때문에 테스트 스펙을 작성하기도 편해졌습니다. &lt;code class=&quot;language-typescript&quot;&gt;manualChangeDetection&lt;/code&gt; 함수를 활용하면 기본 변화 감지 로직을 비활성화시키고 변화 감지 로직을 수동으로 조작할 수 있습니다.&lt;/p&gt;
&lt;p&gt;예제와 함께 자세한 내용을 확인하려면 &lt;a href=&quot;http://material.angular.io/cdk/test-harnesses/overview&quot;&gt;Angular Material 테스트 하네스 문서&lt;/a&gt;를 참고하세요!&lt;/p&gt;
&lt;h2&gt;진행상황 표시방식 개선&lt;/h2&gt;
&lt;p&gt;애플리케이션을 빌드할 때 표시되는 화면을 개선했습니다. 이제 Angular CLI 실행결과가 좀 더 보기 좋게 표시됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img title=&quot;Angular CLI 출력 화면 개선&quot; src=&quot;https://miro.medium.com/max/607/0*-dCa80651cnfbjpX&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;언어 지원 서비스 개선&lt;/h2&gt;
&lt;p&gt;Angular 언어 지원 서비스는 개발 효율성을 향상시키는 것 외에 개발을 좀 더 재미있게 만드는 점에서도 유용합니다. 지금까지는 이전 버전인 View Engine으로 동작하고 있었지만 이제부터 이 기능이 Ivy 기반으로 동작합니다. 이제 이 기능은 좀 더 다양하고 정확하게 동작합니다.&lt;/p&gt;
&lt;p&gt;언어 지원 서비스는 TypeScript 컴파일러와 비슷하게 템플릿에 사용된 객체의 제네릭 타입을 추론하는 용도로 사용됩니다. 아래 화면을 예로 들면 템플릿에서 반복되는 객체가 문자열이라는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img title=&quot;Angular 언어 지원 서비스가 템플릿에서 타입을 추론하는 화면&quot; src=&quot;https://miro.medium.com/max/700/0*L1Tg13gdu3PCqUNN&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;언어 지원 서비스의 효율성을 더 높일 수 있는 기능은 아직 개발 중입니다. 다음 버전에는 이 기능을 반영할 수 있길 바라면서 진행 소식을 먼저 공유해드립니다.&lt;/p&gt;
&lt;h2&gt;핫 모듈 갱신 기능 지원&lt;/h2&gt;
&lt;p&gt;Angular는 핫 모듈 갱신(Hot Module Replacement, HMR) 기능을 제공하고 있긴 했지만 이 기능을 활성화하려면 환경설정과 프로젝트 코드를 일부 수정해야 했기 때문에 Angular 프로젝트에 간단하게 도입하기 어려웠습니다. Angular 11 버전부터는 Angular CLI로 &lt;code&gt;ng serve&lt;/code&gt; 명령을 실행할 때 간단하게 이 기능을 활성화할 수 있습니다. 이렇게 사용하면 됩니다:&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ng serve --hmr&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 명령을 실행하고 로컬 서버가 시작되면 HMR 기능이 활성화되었다는 메시지가 로그에 출력됩니다:&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;NOTICE: Hot Module Replacement (HMR) is enabled for the dev server.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;webpack을 사용하는 환경에서 HMR을 활성화하려면 &lt;a href=&quot;https://webpack.js.org/guides/hot-module-replacement&quot;&gt;webpack 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;이제 개발하는 동안 컴포넌트, 템플릿, 스타일 코드를 변경하면 이 내용이 실행 중인 애플리케이션에 즉시 반영됩니다. 화면 새로고침도 이제 일어나지 않습니다. 폼에 입력했던 데이터는 그대로 유지되며, 스크롤했던 위치도 그대로 유지됩니다. 개발 생산성이 더욱 증가하길 바랍니다.&lt;/p&gt;
&lt;h2&gt;더 빨라진 빌드&lt;/h2&gt;
&lt;p&gt;일부 코드를 변경하면서 빌드 싸이클을 빠르게 단축했습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;의존성 패키지를 설치하는 동안 실행되는 &lt;code&gt;ngcc update&lt;/code&gt; 성능이 2~4배 빨라졌습니다.&lt;/li&gt;
&lt;li&gt;TypeScript v4.0을 활용하면서 컴파일 속도가 빨라졌습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;webpack 5 일부 지원&lt;/h1&gt;
&lt;p&gt;현재 Angular 팀은 webpack v5를 테스트해보고 있습니다. 아직까지는 &lt;a href=&quot;https://webpack.js.org/concepts/module-federation/&quot;&gt;모듈 병합(module federation)&lt;/a&gt;과 같은 webpack v5 일부 기능만 사용해볼 수 있지만, 이후에는 webpack v5를 활용해서 이런 이점을 얻을 수 있을 것입니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;삭제되지 않는 디스크 캐시를 활용해서 빌드 시간을 줄일 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webpack.js.org/guides/tree-shaking/&quot;&gt;cjs 트리 셰이킹&lt;/a&gt;을 활용해서 빌드 결과물의 용량을 줄일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;하지만 아직 이 기능들은 테스트 중이기 때문에 당장 운영용 애플리케이션에 도입하는 것은 권장하지 않습니다.&lt;/p&gt;
&lt;p&gt;그래도 Angular 프로젝트에 webpack 5 버전을 적용하려면 package.json 파일에 아래 코드를 추가하면 됩니다:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;resolutions&quot;: {
     &quot;webpack&quot;: &quot;5.4.0&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다만, &lt;code class=&quot;language-json&quot;&gt;resolutions&lt;/code&gt; 프로퍼티는 npm이 지원하지 않기 때문에 yarn을 사용해야 합니다.&lt;/p&gt;
&lt;h2&gt;린트(linting)&lt;/h2&gt;
&lt;p&gt;이전까지는 Angular 프로젝트를 생성했을 때 TSLint를 기본 린트 툴로 함께 설치했습니다. 하지만 얼마 전부터 TSLint 운영자가 지원을 중단하며 ESLint로 대체할 것을 권장하고 있습니다. 그래서 Angular 팀은 &lt;a href=&quot;https://github.com/typescript-eslint/typescript-eslint&quot;&gt;typescript-eslint&lt;/a&gt;, &lt;a href=&quot;https://github.com/angular-eslint/angular-eslint&quot;&gt;angular-eslint&lt;/a&gt;, &lt;a href=&quot;https://github.com/typescript-eslint/tslint-to-eslint-config&quot;&gt;tslint-to-eslint-config&lt;/a&gt;를 개발한 &lt;a href=&quot;https://twitter.com/mrjameshenry&quot;&gt;James Henry&lt;/a&gt;와 함께 이 이슈를 정리했습니다! 이제 기존 TSLint 스택을 ESLint 스택으로 원활하게 전환할 수 있습니다.&lt;/p&gt;
&lt;p&gt;Angular 11 버전부터는 TSLint와 Codelyzer를 사용하지 않습니다. 이 말은, 이제 Angular 프로젝트에 기본으로 적용되는 린트 툴이 없어진다는 이야기입니다.&lt;/p&gt;
&lt;p&gt;Angular 프로젝트에 적용된 TSLint를 ESLint로 전환하는 방법은 &lt;a href=&quot;https://github.com/angular-eslint/angular-eslint#migrating-from-codelyzer-and-tslint&quot;&gt;angular-eslint 공식 가이드 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;h2&gt;묵은 때 제거&lt;/h2&gt;
&lt;p&gt;이번 버전부터 IE9, IE10, IE 모바일 지원이 중단되고 IE 중에서는 &lt;a href=&quot;https://angular.io/guide/browser-support&quot;&gt;IE11만 유일하게 지원하는 버전&lt;/a&gt;이 될 것입니다. &lt;a href=&quot;https://angular.io/guide/deprecations&quot;&gt;지원이 중단되는 것으로 계획되었던 API&lt;/a&gt;가 일부 반영되었으며, 새로운 지원 중단 목록이 추가되기도 했습니다. 지원이 중단되는 기능을 사용하고 있지 않은지 꼭 확인해 보세요.&lt;/p&gt;
&lt;h2&gt;로드맵&lt;/h2&gt;
&lt;p&gt;또 현재 우선순위에 대한 소식을 계속 알려드리기 위해 Angular &lt;a href=&quot;https://angular.io/guide/roadmap&quot;&gt;로드맵&lt;/a&gt;도 최신화했습니다. 이 글에 언급한 항목 중 아직 개발 중인 기능은 로드맵에도 추가되어 있습니다. Angular 팀은 큰 공수가 들어가는 작업을 점진적으로 진행하려고 노력하고 있으며, 개발자들의 피드백도 정식 버전 출시 전에 빠르게 받아보려고 합니다.&lt;/p&gt;
&lt;p&gt;또한 Angular 커뮤니티의 &lt;a href=&quot;https://twitter.com/simpulton&quot;&gt;Lukas Ruebbelke&lt;/a&gt;와 지속적으로 협력하면서 개발자들에게 좀 더 나은 정보를 제공하기 위해 프로젝트 내용 일부를 수정하기도 했습니다.&lt;/p&gt;
&lt;h2&gt;Angular 11로 업데이트하는 방법&lt;/h2&gt;
&lt;p&gt;이전과 마찬가지로 Angular 애플리케이션을 새 버전으로 바꾸려면 Angular CLI 명령을 실행하면 됩니다:&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ng update @angular/cli @angular/core&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;새 버전에 변경된 내용을 확인하려면 &lt;a href=&quot;https://update.angular.io/&quot;&gt;update.angular.io&lt;/a&gt; 문서를 참고하세요. 언제나 그렇듯 메이저 버전은 하나씩 올려야 버전 업그레이드 과정을 원활하게 진행할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이번에 도입된 기능들이 여러분들에게 도움이 되기를 바랍니다. 그리고 이 버전을 어떻게 생각하는지 &lt;a href=&quot;https://twitter.com/angular&quot;&gt;Twitter&lt;/a&gt;에 남겨주세요!&lt;/p&gt;</description>
      <category>Angular</category>
      <category>Angular</category>
      <author>한장현</author>
      <guid isPermaLink="true">https://han41858.tistory.com/53</guid>
      <comments>https://han41858.tistory.com/53#entry53comment</comments>
      <pubDate>Sun, 15 Nov 2020 22:55:28 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript 4.0 릴리즈 노트</title>
      <link>https://han41858.tistory.com/52</link>
      <description>&lt;p&gt;&amp;nbsp;안녕하세요. 한장현입니다. &lt;br /&gt;&amp;nbsp;그동안 TypeScript는 꾸준히 새 버전이 나오고 있었습니다. &lt;br /&gt;&amp;nbsp;이번에는 메이저 버전이 변경되는 업데이트라 좀 더 재미있는 내용이 있을까 해서 릴리즈 노트를 찾아봤습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;원문&lt;/a&gt;은 2020년 8월 20일에 작성되었습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;오늘 저희는 TypeScript 4.0을 발표하게 되었습니다! 이 버전은 프로그래밍 언어의 표현력, 생산성, 확장성에 대해 깊이 고민한 결과물이며, TypeScript의 시대를 새롭게 여는 버전이 될 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript에 익숙하지 않은 독자를 위해 간단하게 설명하자면, TypeScript는 JavaScript를 기반으로 정적 타입 문법을 추가한 것입니다. 변수를 선언할 때 타입을 지정하고 이후에 변수를 사용할 때 타입을 다시 지정하면, TypeScript의 타입 검사 로직이 둘 사이의 관계가 적절한지 검사하기 때문에 잘못 작성된 코드를 실행하기 전에 검출할 수 있습니다. 이 기능은 파일을 저장하지 않은 상태에서도 동작합니다. 그리고 코드 작성을 끝낸 후에는 타입과 관련된 코드를 모두 제거해서 간결하며 가독성 높은 JavaScript로 변환할 수 있기 때문에 JavaScript가 실행되는 환경이라면 어디서든 자유롭게 실행할 수 있습니다. 코드 에러 검출 외에도, 정적 타입을 지정하면 에디터에서 코드 자동완성, 코드 이동, 리팩토링 등과 같은 기능을 다양하게 활용할 수 있습니다. 어렵게 생각할 필요는 없습니다. Visual Studio Code나 Visual Studio에서 JavaScript를 다뤄본 경험이 있다면 TypeScript을 사용해본 경험도 있다고 할 수 있습니다. TypeScript에 대해 학습하려면 &lt;a href=&quot;https://www.typescriptlang.org&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 가이드 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 4.0에 메이저급 큰 변화는 없습니다. 그래서 아직 TypeScript를 접해보지 않았다면 이 언어를 공부하기에 지금이 가장 적절한 시기입니다. 꾸준히 운영되고 있는 커뮤니티가 있으며, 실행해볼 수 있는 예제 코드나 관련글들도 방대하게 존재합니다. TypeScript를 공부하는 동안 한 가지만 기억하세요. TypeScript 4.0에서 활용할 수 있는 기능은 아주 많지만 무엇보다도 TypeScript의 기본을 탄탄하게 알아두는 것이 중요합니다! &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript로 구현한 프로젝트가 있다면 다음 명령을 실행해서 TypeScript 버전을 업그레이드할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install -D typescript&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript를 활용할 수 있는 에디터도 확인해 보세요.&lt;/p&gt;
&lt;p&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;a href=&quot;https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Visual Studio 2019/2017 다운로드&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.visualstudio.com/insiders&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Visual Studio Code 인사이더 버전 설치&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-newer-typescript-versions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript 최신버전 설치 가이드 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.0 버전이 나오기까지&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript는 요즘 많은 사람들의 JavaScript 스택 대용으로 사용되고 있습니다. npm만 봐도 TypeScript 패키지는 올해 7월 처음으로 5천만 다운로드를 기록했습니다! 그리고 아직 개선의 여지가 분명히 있음에도 불구하고 TypeScript를 사용하는 개발자 대부분이 TypeScript를 즐겁게 사용하고 있다는 소식도 듣게 되었습니다. StackOverflow에서 진행한 설문에서는 &lt;a href=&quot;https://insights.stackoverflow.com/survey/2020#technology-most-loved-dreaded-and-wanted-languages&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript가 개발자들이 사랑하는 언어 2위&lt;/a&gt;에 올랐습니다. 최근 State of JavaScript 설문에서는 &lt;a href=&quot;https://2019.stateofjs.com/javascript-flavors/typescript/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript를 사용해본 개발자 중 89%가 TypeScript를 계속 사용하고 싶다&lt;/a&gt;고 응답했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 시점에서 TypeScript가 어떻게 개발되어 왔는지 한 번 돌아볼 필요가 있습니다. 1, 2 버전은 생략합시다. 하지만 1, 2 버전에 도입된 기능 중에서도 지금까지 훌륭하게 활용되는 기능이 많으며, 이런 기능들은 TypeScript 4에서도 유지하려고 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;3.0 버전부터 봅시다. 이 버전에 변경된 사항은 셀 수 없이 많지만 그중에서도 튜플 타입과 리스트 타입을 통합한 것이 가장 큰 변경사항이었습니다. 또 3.0 버전은 프로젝트를 참조할 수 있는 기능을 제공했기 때문에 프로젝트를 확장하거나 관리하기에도 편해졌습니다. &lt;code&gt;any&lt;/code&gt; 타입을 좀 더 안전하게 처리하기 위해 &lt;code&gt;unknown&lt;/code&gt; 타입을 도입한 것도 큰 반응이 있었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript 3.1 버전&lt;/a&gt;은 &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;맵핑 타입(mapped types)&lt;/a&gt;을 튜플과 배열 타입에도 확장해서 실행시점에 TypeScript 전용 코드 없이도 간단하게 함수에 프로퍼티를 추가할 수 있게 개선되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-2/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript 3.2 버전&lt;/a&gt;부터는 객체를 분해할 때 제네릭 타입을 사용할 수 있게 되었으며 &lt;code&gt;bind&lt;/code&gt;, &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt; 함수의 타입을 엄격하게 지정하도록 개선하면서 좀 더 나은 메타 프로그래밍이 가능한 모델을 구축했습니다. 그리고 &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-3/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript 3.3 버전&lt;/a&gt;은 3.2 버전의 안정성을 개선하는 데에 집중했지만 그 와중에 유니언 타입 함수나 &lt;code&gt;--build&lt;/code&gt; 옵션에 파일 증분 빌드 기능을 도입한 것과 같이 다양한 기능이 함께 추가되었습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript 3.4 버전&lt;/a&gt;은 이뮤터블 데이터 구조에 대한 지원이나 고차 제네릭 함수(higher-order generic functinos)에 대한 지원에 집중하면서 함수형 패턴을 지원하는 데에 힘썼습니다. 그리고 이 버전에 도입된 &lt;code&gt;--incremental&lt;/code&gt; 옵션을 사용하면 TypeScript 코드를 빌드할 때 증분 빌드를 수행하기 때문에 컴파일 시간을 크게 줄일 수 있었습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript 3.5 버전&lt;/a&gt;과 &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;3.6 버전&lt;/a&gt;은 타입 검사 규칙을 점검하면서 TypeScript의 타입 시스템 전체를 손봤습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript 3.7 버전&lt;/a&gt;은 ECMAScript에 도입된 새 타입 시스템이 추가되었기 때문에 아주 중요한 버전이라고 할 수 있습니다. 이 버전에서 타입과 관련해서는 타입 별칭(alias) 참조 코드를 재귀적으로 참조하도록 구성했고 assertion 스타일의 함수 지원 기능을 추가했습니다. 그리고 JavaScript와 관련해서는 옵셔널 체이닝(optional chaining)이나 새로운 병합 연산자(coalescing)가 추가되었습니다. 두 문법 모두 TypeScript 개발자나 JavaScript 개발자의 도입 요구가 많았던 기능입니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;최근에 나온 &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;3.8 버전&lt;/a&gt;과 &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;3.9 버전&lt;/a&gt;에는 타입만 로드(&lt;code&gt;import&lt;/code&gt;)하거나 외부로 공개(&lt;code&gt;export&lt;/code&gt;)하는 문법이 추가되었고 ECMAScript에 추가된 private 필드 지정 문법이 추가되었으며, 모듈 계층에서 동작하는 최상위 &lt;code&gt;await&lt;/code&gt; 문법, &lt;code&gt;export *&lt;/code&gt; 문법이 개선되었습니다. 또 이 버전들은 동작 성능과 확장성 최적화가 이루어진 버전이기도 합니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;언어 지원 서비스나 인프라구조, 웹사이트, TypeScript 코어 프로젝트에 대해서는 다루지 않았습니다. TypeScript 코어 프로젝트는 수많은 커뮤니티 컨트리뷰터의 도움으로 발전하고 있으며 DefinitelyTyped 프로젝트나 TypeScript 프로젝트도 마찬가지입니다. DefinitelyTyped 프로젝트가 처음 시작되었던 2012년에 풀 리퀘스트는 80건밖에 되지 않았지만, 그 해 말부터 크게 증가해서 2019년에는 8,300개라는 놀라운 숫자의 풀 리퀘스트가 생성되었습니다. 컨트리뷰터들의 수많은 기여들은 TypeScript의 기초를 탄탄하게 하는 데에 큰 도움이 되었으며, TypeScript 생태계를 계속해서 개선해나가는 데에도 큰 도움이 됩니다. 열정적인 커뮤니티 활동에 감사드립니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;새로 추가된 기능&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;4.0 버전은 이런 과정을 거쳤기 때문에 나올 수 있었습니다. 이제 4.0 버전에 새로 추가된 기능에 대해 알아봅시다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가변 튜플 타입&lt;/li&gt;
&lt;li&gt;튜플 엘리먼트에 이름 지정하기&lt;/li&gt;
&lt;li&gt;생성자에서 클래스 프로퍼티 추론하기&lt;/li&gt;
&lt;li&gt;간략 할당 연산자&lt;/li&gt;
&lt;li&gt;&lt;code&gt;catch&lt;/code&gt; 절에 바인딩되는 에러 타입은 &lt;code&gt;unknown&lt;/code&gt;입니다.&lt;/li&gt;
&lt;li&gt;커스텀 JSX 팩토리&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--noEmitOnError&lt;/code&gt; 옵션을 사용할 때 빌드 속도 개선&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--incremental&lt;/code&gt;과 &lt;code&gt;--noEmit&lt;/code&gt; 함께 사용하기&lt;/li&gt;
&lt;li&gt;에디터 지원 개선
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;옵셔널 체이닝 지원&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/** @deprecated */&lt;/code&gt; 지원&lt;/li&gt;
&lt;li&gt;에디터 시작 시점에 활용할 수 있는 부분 지원 모드&lt;/li&gt;
&lt;li&gt;더 똑똑해진 심볼 자동 로드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;웹사이트 개편!&lt;/li&gt;
&lt;li&gt;Breaking Changes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가변 튜플 타입&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;배열이나 튜플 인자 두 개를 받아서 새로운 배열로 결합하는 &lt;code&gt;concat&lt;/code&gt; JavaScript 함수가 있다고 합시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
function concat(arr1, arr2) {
    return [...arr1, ...arr2];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그리고 배열이나 튜플을 하나를 인자로 받아서 첫 번째 항목을 제외하고 나머지를 반환하는 &lt;code&gt;tail&lt;/code&gt; 함수도 있다고 합시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
function tail(arg) {
    const [_, ...result] = arg;
    return result
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 함수를 대상으로 TypeScript 타입을 지정하려면 어떻게 해야 할까요? &lt;br /&gt;&lt;code&gt;concat&lt;/code&gt;의 경우에 이전 버전까지 가능했던 방법은 모든 경우를 고려해서 오버로딩하는 것뿐이었습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
function concat(arr1: [], arr2: []): [];
function concat&amp;lt;A&amp;gt;(arr1: [A], arr2: []): [A];
function concat&amp;lt;A, B&amp;gt;(arr1: [A, B], arr2: []): [A, B];
function concat&amp;lt;A, B, C&amp;gt;(arr1: [A, B, C], arr2: []): [A, B, C];
function concat&amp;lt;A, B, C, D&amp;gt;(arr1: [A, B, C, D], arr2: []): [A, B, C, D];
function concat&amp;lt;A, B, C, D, E&amp;gt;(arr1: [A, B, C, D, E], arr2: []): [A, B, C, D, E];
function concat&amp;lt;A, B, C, D, E, F&amp;gt;(arr1: [A, B, C, D, E, F], arr2: []): [A, B, C, D, E, F];)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;음... 그래요. 그런데 오버로딩 함수 7개의 두번째 배열은 모두 빈 배열입니다. &lt;code&gt;arr2&lt;/code&gt;에 해당하는 오버로드 함수도 추가해 봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
function concat&amp;lt;A2&amp;gt;(arr1: [], arr2: [A2]): [A2];
function concat&amp;lt;A1, A2&amp;gt;(arr1: [A1], arr2: [A2]): [A1, A2];
function concat&amp;lt;A1, B1, A2&amp;gt;(arr1: [A1, B1], arr2: [A2]): [A1, B1, A2];
function concat&amp;lt;A1, B1, C1, A2&amp;gt;(arr1: [A1, B1, C1], arr2: [A2]): [A1, B1, C1, A2];
function concat&amp;lt;A1, B1, C1, D1, A2&amp;gt;(arr1: [A1, B1, C1, D1], arr2: [A2]): [A1, B1, C1, D1, A2];
function concat&amp;lt;A1, B1, C1, D1, E1, A2&amp;gt;(arr1: [A1, B1, C1, D1, E1], arr2: [A2]): [A1, B1, C1, D1, E1, A2];
function concat&amp;lt;A1, B1, C1, D1, E1, F1, A2&amp;gt;(arr1: [A1, B1, C1, D1, E1, F1], arr2: [A2]): [A1, B1, C1, D1, E1, F1, A2];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;코드를 굳이 이렇게 작성해야 할 필요가 있을까요. &lt;code&gt;tail&lt;/code&gt;의 경우에도 마찬가지입니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이런 식으로 가면 원하는 바를 달성하기 위해 오버로딩 함수를 천 개쯤 구현해야 할 수도 있기 때문에 문제를 근본적으로 해결하는 답이라고 볼 수 없습니다. 그래서 모든 경우를 한 번에 처리하려면 다음과 같이 구성해야 합니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
function concat&amp;lt;T, U&amp;gt;(arr1: T[], arr2: U[]): Array&amp;lt;T | U&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;하지만 이렇게 정의해도 입력값의 길이나 엘리먼트의 차수(order)를 처리할 수 없었으며 튜플인 경우에도 마찬가지입니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그래서 TypeScript 4.0는 타입 추론 개선을 포함한 두 가지 개선사항을 도입하면서 새로운 타입 정의 모델을 마련했습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;첫 번째 개선사항은 제네릭에 사용하는 튜플에 전개 연산자(spread operator, &lt;code&gt;...&lt;/code&gt;)를 사용할 수 있게 된 것입니다. 이 기능이 도입되면서 이제는 현재 다루고 있는 데이터의 실제 타입이 무엇인지 알지 못하더라도 튜플이나 배열을 처리할 수 있게 되었습니다. 제네릭에 사용된 전개 연산자에 해당하는 변수의 인스턴스가 생성되면 이 튜플 타입을 기반으로 새로운 튜플이나 배열을 생성할 수 있습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;새로운 방식으로 &lt;code&gt;tail&lt;/code&gt; 함수를 구현해보면 이렇습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
function tail&amp;lt;T extends any[]&amp;gt;(arr: readonly [any, ...T]) {
    const [_ignored, ...rest] = arr;
    return rest;
}

const myTuple = [1, 2, 3, 4] as const;
const myArray = [&quot;hello&quot;, &quot;world&quot;];

// type [2, 3, 4]
const r1 = tail(myTuple);

// type [2, 3, 4, ...string[]]
const r2 = tail([...myTuple, ...myArray] as const);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;두 번째 개선사항은 튜플 안에 사용하는 전개 연산자의 위치가 꼭 마지막이 아니어도 된다는 것입니다!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
type Strings = [string, string];
type Numbers = [number, number];

// [string, string, number, number, boolean]
type StrStrNumNumBool = [...Strings, ...Numbers, boolean];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 4.0 이전 버전에서 위와 같은 코드를 작성하면 이런 에러가 발생했습니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;
A rest element must be last in a tuple type.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;하지만 TypeScript 4.0부터는 아닙니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;길이가 고정되지 않은 상태에서 전개 연산자를 사용하면 이제는 해당 타입이 마지막 엘리먼트 전까지 자동으로 지정됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
type Strings = [string, string];
type Numbers = number[]

// [string, string, ...Array&amp;lt;number | boolean&amp;gt;]
type Unbounded = [...Strings, ...Numbers, boolean];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 두 가지 개선사항을 함께 활용하면 &lt;code&gt;concat&lt;/code&gt; 함수를 다음과 같이 아름답게 정의할 수 있습니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
type Arr = readonly any[];

function concat&amp;lt;T extends Arr, U extends Arr&amp;gt;(arr1: T, arr2: U): [...T, ...U] {
    return [...arr1, ...arr2];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 코드에서 중복된 부분이 불필요하다고 생각할 수 있지만, 이렇게 작성해야 어떠한 배열이나 튜플에 대해서도 타입 추론 기능이 제대로 동작합니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이런 방식은 그 자체로도 훌륭하지만 시나리오가 복잡한 경우에 더 빛을 발합니다. 함수를 인자로 받아서 실행하는 &lt;a href=&quot;https://en.wikipedia.org/wiki/Partial_application&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;partialCall.partialCall&lt;/code&gt; 패턴&lt;/a&gt;을 생각해 봅시다. 이런 패턴에서는 함수(&lt;code&gt;partialCall&lt;/code&gt;)가 다른 함수(&lt;code&gt;f&lt;/code&gt;)를 인자로 받아서 &lt;code&gt;f&lt;/code&gt; 함수가 처리하는 결과를 또 다른 함수로 반환합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
function partialCall(f, ...headArgs) {
    return (...tailArgs) =&amp;gt; f(...headArgs, ...tailArgs)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 4.0에서 개선된 사항을 활용하면 이 함수는 다음과 같이 정의할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
type Arr = readonly unknown[];

function partialCall&amp;lt;T extends Arr, U extends Arr, R&amp;gt;(
    f: (...args: [...T, ...U]) =&amp;gt; R, ...headArgs: T
) {
    return (...tailArgs: U) =&amp;gt; f(...headArgs, ...tailArgs)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이렇게 작성하면 &lt;code&gt;partialCall&lt;/code&gt; 함수가 받는 인자가 어떤 타입이어야 하는지, 반환하는 타입이 어떤 타입이어야 하는지 더 편하게 파악할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
const foo = (x: string, y: number, z: boolean) =&amp;gt; {}

// x 형식이 맞지 않기 때문에 동작하지 않습니다.
const f1 = partialCall(foo, 100);
//                          ~~~
// error! Argument of type 'number' is not assignable to parameter of type 'string'.


// 전달하는 인자의 개수가 맞지 않기 때문에 동작하지 않습니다.
const f2 = partialCall(foo, &quot;hello&quot;, 100, true, &quot;oops&quot;)
//                                              ~~~~~~
// error! Expected 4 arguments, but got 5.


// 이 코드는 동작합니다! f3는 '(y: number, z: boolean) =&amp;gt; void' 타입으로 추론됩니다.
const f3 = partialCall(foo, &quot;hello&quot;);

// f3는 이제 어떻게 활용할 수 있을까요?

f3(123, true); // 동작합니다!

f3();
// error! Expected 2 arguments, but got 0.

f3(123, &quot;hello&quot;);
//      ~~~~~~~
// error! Argument of type 'string' is not assignable to parameter of type 'boolean'.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;가변 튜플 타입을 활용하면 지금까지 없던 패턴을 새롭게 개발할 수 있습니다. 그리고 이 기능이 많이 활용되면서 JavaScript &lt;code&gt;bind&lt;/code&gt;를 활용하는 로직이 더 다양하게 확장될 수 있으리라 기대합니다. 타입 추론 기능이 개선되면서 이밖에도 다양한 패턴이 나올 수 있을 것입니다. 자세한 내용은 가변 튜플에 대한 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/39094&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;튜플 엘리먼트에 이름 지정하기&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;튜플 타입과 인자 목록에 대한 개선사항은 기존 JavaScript 코드에 빈번하게 사용되던 배열 조작 과정에 더 강력한 타입 유효성 검사를 적용할 수 있다는 점에서도 중요합니다. 나머지 매개변수에 튜플 유형을 사용할 수 있다는 점이 특히 중요합니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;전개 연산자에 튜플 타입을 사용하는 함수를 예로 들어 봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
function foo(...args: [string, number]): void {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 함수는 아래 함수와 거의 비슷하다고 볼 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
function foo(arg0: string, arg1: number): void {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;사용하는 방식도 그렇습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
foo(&quot;hello&quot;, 42); // 동작

foo(&quot;hello&quot;, 42, true); // 에러
foo(&quot;hello&quot;); // 에러
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;하지만 두 정의 방식은 가독성 측면에서 좀 다릅니다. 첫 번째 코드처럼 정의하면 함수에 전달된 인자의 이름이 없습니다. 물론 이렇게 작성해도 타입 검사는 통과하겠지만 사용하기에는 조금 불편합니다. 이 함수가 처음 정의된 의도대로 앞으로도 사용될 것인지도 알 수 없습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;TypeScript 4.0부터는 아래처럼 튜플에 이름을 지정할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
type Range = [start: number, end: number];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그리고 함수로 전달되는 인자 배열과 튜플 타입의 연결을 강화하기 위해 전개 연산자와 옵션 엘리먼트를 이렇게 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
type Foo = [first: number, second?: string, ...rest: any[]];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;튜플 엘리먼트에 이름을 지정할 때 지켜야 할 규칙이 몇 가지 있습니다. 첫 번째, 튜플 엘리먼트에 이름을 하나라도 지정하면 모든 엘리먼트에 이름을 지정해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
type Bar = [first: string, number];
//                         ~~~~~~
// error! Tuple members must all have names or all not have names.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;다만, 이때 이름을 지정했다고 해서 배열을 분해할 때도 그 이름을 똑같이 사용해야 한다는 것은 아닙니다. 이 기능은 개발자에게 좀 더 정확한 정보를 제공하는 것이 목적입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
function foo(x: [first: string, second: number]) {
    // ...

    // 참고: `first`, `second`라는 이름을 사용하지 않아도 됩니다.
    let [a, b] = x;

    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;정리하자면, 튜플 엘리먼트에 이름을 지정하면 함수에 전달되는 인자의 정보를 더 명확하게 지정할 수 있기 때문에 오버로딩 함수를 구현할 때도 타입이 틀어질 일이 줄어듭니다. TypeScript 에디터에서도 오버로딩 함수들을 확인하고 적절한 제안을 해줄 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;signatureHelpLabeledTuples.gif&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;362&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UkRJs/btqHemaOQU2/hxFA9bsGg4YgS8Xp1DDs40/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UkRJs/btqHemaOQU2/hxFA9bsGg4YgS8Xp1DDs40/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UkRJs/btqHemaOQU2/hxFA9bsGg4YgS8Xp1DDs40/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/UkRJs/btqHemaOQU2/hxFA9bsGg4YgS8Xp1DDs40/img.gif&quot; data-filename=&quot;signatureHelpLabeledTuples.gif&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;362&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;더 자세한 내용은 튜플 엘리먼트에 이름 지정하기 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/38234&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;생성자에서 클래스 프로퍼티 추론하기&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 4.0부터는 클래스 프로퍼티의 타입을 결정할 때 좀 더 개선된 추론 방식을 활용합니다. 이 기능은 &lt;code&gt;noImplicitAny&lt;/code&gt;가 활성화된 상태에서 확인할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
class Square {
    // 이전 버전: any로 추론되기 때문에 에러가 발생합니다!
    // 4.0: `number`로 추론합니다!
    area;
    sideLength;

    constructor(sideLength: number) {
        this.sideLength = sideLength;
        this.area = sideLength ** 2;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그리고 생성자에 있는 정보만으로 추론할 수 없는 경우라면 &lt;code&gt;undefined&lt;/code&gt;로 간주합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
class Square {
    sideLength;

    constructor(sideLength: number) {
        if (Math.random()) {
            this.sideLength = sideLength;
        }
    }

    get area() {
        return this.sideLength ** 2;
        //     ~~~~~~~~~~~~~~~
        // error! Object is possibly 'undefined'.
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이런 경우가 있다면 생성자보다 &lt;code&gt;initialize&lt;/code&gt;와 같은 초기화 메소드를 따로 구현하는 것이 좋습니다. 그리고 &lt;code&gt;strictPropertyInitialization&lt;/code&gt; 옵션을 사용한다면 명확하게 &lt;code&gt;!&lt;/code&gt;를 사용해야 할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
class Square {
    // 타입을 정확하게 지정
    //        v
    sideLength!: number;
    //         ^^^^^^^^
    // type annotation

    constructor(sideLength: number) {
        this.initialize(sideLength)
    }

    initialize(sideLength: number) {
        this.sideLength = sideLength;
    }

    get area() {
        return this.sideLength ** 2;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자세한 내용은 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/379200&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;간략(short-circuiting) 할당 연산자&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;JavaScript 뿐만 아니라 다른 언어에도 복합 할당 연산자(compound assignment operator)라는 개념이 존재합니다. 복합 할당 연산자는 연산자가 실행된 결과를 왼쪽 변수에 다시 할당하기 위해 연산자 두 개를 결합한 것을 의미합니다. 이런 코드가 그렇습니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
// 더하기
// a = a + b
a += b;

// 빼기
// a = a - b
a -= b;

// 곱하기
// a = a * b
a *= b;

// 나누기
// a = a / b
a /= b;

// 지수 연산
// a = a ** b
a **= b;

// 왼쪽으로 비트 이동
// a = a &amp;lt;&amp;lt; b
a &amp;lt;&amp;lt;= b;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;연산자 대부분은 할당 연산자와 결합할 수 있습니다. 그런데 최근까지도 &lt;code&gt;and(&amp;amp;&amp;amp;)&lt;/code&gt;, &lt;code&gt;or(||)&lt;/code&gt;, null 병합 연산자(&lt;code&gt;??&lt;/code&gt;)들은 할당 연산자와 결합할 수 없었습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그래서 TypeScript 4.0은 새로운 ECMAScript 스펙에 맞게 이 연산자들에 대해서도 &lt;code&gt;&amp;amp;&amp;amp;=&lt;/code&gt;, &lt;code&gt;||=&lt;/code&gt;, &lt;code&gt;??=&lt;/code&gt;와 같은 복합 할당 연산자를 추가했습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 연산자들은 순서대로 다음 코드와 동일하게 동작합니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
a = a &amp;amp;&amp;amp; b;
a = a || b;
a = a ?? b;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;code&gt;if&lt;/code&gt; 블록으로 표현하면 이렇습니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
// 'a ||= b'와 동일하게 동작
if (!a) {
    a = b;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 연산자는 다음과 같이 변수 초기화 시점을 지연시키는 용도로도 활용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
let values: string[];

// 이전 버전
(values ?? (values = [])).push(&quot;hello&quot;);

// 4.0
(values ??= []).push(&quot;hello&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;(저희가 작성한 코드가 모두 아름다운 것은 아닙니다...) &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자주 사용되지는 않겠지만 게터, 세터와 함께 활용하는 방법도 있습니다. 이 방식을 활용하면 어떤 변수의 값이 없을 때만 특정 로직을 실행해서 해당 변수로 할당할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
obj.prop ||= foo();

// 아래 코드들과 비슷합니다.

obj.prop || (obj.prop = foo());

if (!obj.prop) {
    obj.prop = foo();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://www.typescriptlang.org/play?ts=Nightly#code/MYewdgzgLgBCBGArGBeGBvAsAKBnmA5gKawAOATiKQBQCUGO+TMokIANkQHTsgHUAiYlChFyMABYBDCDHIBXMANoBuHI2Z4A9FpgAlIqXZTgRGAFsiAQg2byJeeTAwAslKgSu5KWAAmIczoYAB4YAAYuAFY1XHwAXwAaWxgIEhgKKmoAfQA3KXYALhh4EA4iH3osWM1WCDKePkFUkTFJGTlFZRimOJw4mJwAM0VgKABLcBhB0qCqplr63n4BcjGCCVgIMd8zIjz2eXciXy7k+yhHZygFIhje7BwFzgblgBUJMdlwM3yAdykAJ6yBSQGAeMzNUTkU7YBCILgZUioOBIBGUJEAHwxUxmqnU2Ce3CWgnenzgYDMACo6pZxpYIJSOqDwSkSFCYXC0VQYFi0NMQHQVEA&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript Playground&lt;/a&gt;에서 다음 코드가 어떻게 실행되는지 확인해 보세요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
const obj = {
    get prop() {
        console.log(&quot;게터가 실행되었습니다.&quot;);

        // Replace me!
        return Math.random() &amp;lt; 0.5;
    },
    set prop(_val: boolean) {
        console.log(&quot;세터가 실행되었습니다.&quot;);
    }
};

function foo() {
    console.log(&quot;연산자 오른쪽이 실행되었습니다.&quot;);
    return true;
}

console.log(&quot;이 코드는 항상 세터를 실행합니다.&quot;);
obj.prop = obj.prop || foo();

console.log(&quot;이 코드는 *가끔* 세터를 실행합니다.&quot;);
obj.prop ||= foo();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 기능을 추가하는데 기여한 커뮤니티 멤버 &lt;a href=&quot;https://github.com/Kingwl&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Wenlu Wang&lt;/a&gt;에게 감사드립니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자세한 내용은 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/37727&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요. &lt;a href=&quot;https://github.com/tc39/proposal-logical-assignment/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TC39 제안 문서&lt;/a&gt;도 참고할만합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;catch&lt;/code&gt; 절에 바인딩되는 에러 타입은 &lt;code&gt;unknown&lt;/code&gt;입니다.&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 4.0 이전 버전까지는 &lt;code&gt;catch&lt;/code&gt; 절에 바인딩되는 에러 객체의 타입이 &lt;code&gt;any&lt;/code&gt; 였습니다. 그래서 이 에러 객체는 아무렇게나 사용할 수 있었습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
try {
    // ...
}
catch (x) {
    // x는 `any` 타입입니다. 갖고 놀아봅시다!
    console.log(x.message);
    console.log(x.toUpperCase());
    x++;
    x.yadda.yadda.yadda();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이런 방식은 &lt;code&gt;catch&lt;/code&gt; 절에서 받은 에러를 또 다른 에러로 연계해야 할 때 오동작할 여지가 있습니다. 에러 객체가 &lt;code&gt;any&lt;/code&gt; 타입으로 간주되기 때문에 이 객체를 다룰 때 타입을 신경 쓰지 않고 아무렇게나 사용할 수 있기 때문입니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이제 TypeScript 4.0부터는 &lt;code&gt;catch&lt;/code&gt; 절에 전달되는 에러 객체가 &lt;code&gt;unknown&lt;/code&gt;으로 간주됩니다. &lt;code&gt;unknown&lt;/code&gt; 타입은 타입을 지정해야 사용할 수 있기 때문에 &lt;code&gt;any&lt;/code&gt; 타입보다 안전합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
try {
    // ...
}
catch (e: unknown) {
    // 에러!
    // `unknown` 타입에는 `toUpperCase`가 존재하지 않습니다.
    console.log(e.toUpperCase());

    if (typeof e === &quot;string&quot;) {
        // 동작합니다!
        // `e` 객체가 `string` 타입일 때만 실행됩니다.
        console.log(e.toUpperCase());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;하지만 이 기능은 개발자들이 충분히 익숙해지기 전까지는 &lt;code&gt;--strict&lt;/code&gt; 모드에서만 동작합니다. 조만간 린트(link) 규칙을 통해서 &lt;code&gt;catch&lt;/code&gt; 절에 전달되는 에러 타입을 &lt;code&gt;: any&lt;/code&gt;나 &lt;code&gt;: unknown&lt;/code&gt; 중 하나로 선택할 수 있을 것입니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자세한 내용은 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/39015&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커스텀 JSX 팩토리&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;JSX에서 이야기하는 &lt;a href=&quot;https://reactjs.org/docs/fragments.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프래그먼트(fragment)&lt;/a&gt;는 자식 엘리먼트를 여러 개 반환하는 JSX 엘리먼트 타입을 의미합니다. 그런데 TypeScript에 프래그먼트를 처음 추가할 때는 이 타입이 앞으로 어떻게 활용될지 심각하게 고려하지 않았습니다. 하지만 지금은 많은 라이브러리들이 JSX 사용을 권장하고 있으며 프래그먼트용 API 지원도 늘려가고 있습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이제 TypeScript 4.0부터는 &lt;code&gt;jsxFragmentFactory&lt;/code&gt; 옵션을 사용해서 프래그먼트 팩토리를 커스터마이징 할 수 있습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그래서 &lt;code&gt;tsconfig.json&lt;/code&gt; 파일을 다음과 같이 작성하면 React에 적합한 JSX 팩토리를 구성할 수 있습니다. 다만, 이렇게 사용하려면 &lt;code&gt;React.createElement&lt;/code&gt; 대신 &lt;code&gt;h&lt;/code&gt;를 사용해야 하며 &lt;code&gt;React.Fragment&lt;/code&gt; 대신 &lt;code&gt;Fragment&lt;/code&gt;를 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;
{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;esnext&quot;,
    &quot;module&quot;: &quot;commonjs&quot;,
    &quot;jsx&quot;: &quot;react&quot;,
    &quot;jsxFactory&quot;: &quot;h&quot;,
    &quot;jsxFragmentFactory&quot;: &quot;Fragment&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그리고 파일마다 JSX 팩토리를 다르게 사용하려면 &lt;code&gt;/** @jsxFrag */&lt;/code&gt; 전처리문(pragma comment)을 사용하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
// 참고: 이 전처리문은 JSDoc-style로 작성해야 합니다.
/** @jsx h */
/** @jsxFrag Fragment */

import { h, Fragment } from &quot;preact&quot;;

let stuff = &amp;lt;&amp;gt;
    &amp;lt;div&amp;gt;Hello&amp;lt;/div&amp;gt;
&amp;lt;/&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 파일을 빌드하면 다음과 같은 JavaScript 코드가 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
// 참고: 이 전처리문은 JSDoc-style로 작성해야 합니다.
/** @jsx h */
/** @jsxFrag Fragment */
import { h, Fragment } from &quot;preact&quot;;
let stuff = h(Fragment, null,
    h(&quot;div&quot;, null, &quot;Hello&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 기능을 추가하는데 기여해준 커뮤니티 멤버 &lt;a href=&quot;https://github.com/nojvek&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Noj Vek&lt;/a&gt;에게 감사드립니다.&lt;/p&gt;
&lt;p&gt;자세한 내용은 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/38720&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;--noEmitOnError&lt;/code&gt; 옵션을 사용할 때 빌드 속도 개선&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이전 버전까지는 &lt;code&gt;--incremental&lt;/code&gt; 옵션을 사용해서 증분 빌드를 할 때 &lt;code&gt;--noEmitOnError&lt;/code&gt; 옵션을 함께 사용하면 속도가 아주 느렸습니다. 이 문제는 &lt;code&gt;--noEmitOnError&lt;/code&gt; 플래그를 사용하면 이전에 컴파일 결과에 대한 정보가 &lt;code&gt;.tsbuildinfo&lt;/code&gt; 파일에 캐싱되지 않기 때문이었습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 4.0은 이 문제를 해결해서 &lt;code&gt;--incremental&lt;/code&gt; 옵션과 &lt;code&gt;--noEmitOnError&lt;/code&gt; 옵션이 활성화된 상태에서 빌드되는 속도를 크게 개선했습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자세한 내용은 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/38853&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;--incremental&lt;/code&gt;과 &lt;code&gt;--noEmit&lt;/code&gt; 함께 사용하기&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이전 버전까지는 &lt;code&gt;--incremental&lt;/code&gt; 옵션을 사용해서 증분 빌드를 할 때 &lt;code&gt;--noEmitOnError&lt;/code&gt; 옵션을 함께 사용하면 속도가 아주 느렸습니다. 이 문제는 &lt;code&gt;--noEmitOnError&lt;/code&gt; 플래그를 사용하면 이전에 컴파일했던 정보가 &lt;code&gt;.tsbuildinfo&lt;/code&gt; 파일에 캐싱되지 않기 때문이었습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 4.0은 이 문제를 해결해서 &lt;code&gt;--incremental&lt;/code&gt; 옵션과 &lt;code&gt;--noEmitOnError&lt;/code&gt; 옵션이 활성화된 상태에서 빌드되는 속도를 크게 개선했습니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자세한 내용은 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/38853&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;에디터 지원 개선&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 컴파일러는 TypeScript 코드를 컴파일하는 용도 외에 TypeScript를 지원하는 에디터에서 코딩 생산성을 향상시키는 용도로도 활용됩니다. Visual Studio 제품군에서 JavaScript를 개발할 때도 마찬가지입니다. 그래서 이번 버전은 에디터에서 활용할 수 있는 시나리오를 강화하는 데에도 힘썼습니다. 원하는 기능을 개발하는 시간이 크게 줄어들 것입니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript/JavaScript 언어 지원 기능은 에디터마다 다르게 동작할 수 있지만&lt;/p&gt;
&lt;p&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;Visual Studio Code에서는 &lt;a href=&quot;https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-the-workspace-version-of-typescript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;원하는 TypeScript 버전을 선택&lt;/a&gt;할 수 있습니다. 이 기능은 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-next&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JavaScript/TypeScript Nightly 확장 플러그인&lt;/a&gt;을 설치하면 확인할 수 있습니다. 이 확장 플러그인은 현재 안정 버전으로 제공합니다.&lt;/li&gt;
&lt;li&gt;Visual Studio 2017/2019에는 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SDK 설치 파일과&lt;/a&gt; &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MSBuild 설치 파일&lt;/a&gt; 형태로 제공됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;옵셔널 체이닝 지원&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;옵셔널 체이닝(optional chaining)은 최근에 등장해서 큰 사랑을 받고 있는 기능입니다. 그래서 TypeScript 4.0에서도 &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/#optional-chaining&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;옵셔널 체이닝&lt;/a&gt;과 &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/#nullish-coalescing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;null 병합 연산자(nullish coalescing)&lt;/a&gt;를 사용할 수 있습니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eqPHZc/btqHnlO7flC/UmKNcqKQAkDkRpYUAZkpm1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eqPHZc/btqHnlO7flC/UmKNcqKQAkDkRpYUAZkpm1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eqPHZc/btqHnlO7flC/UmKNcqKQAkDkRpYUAZkpm1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/eqPHZc/btqHnlO7flC/UmKNcqKQAkDkRpYUAZkpm1/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;다만 코드를 이런 방식으로 변경하면 JavaScript 컨텍스트에서 참으로 평가되거나/거짓으로 평가되는 것에 따라 이전과 다르게 동작할 수 있습니다. 타입을 명확하게 지정할수록 의도대로 동작할 것입니다. &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자세한 내용은 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/39135&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;/** @deprecated */&lt;/code&gt; 지원&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 언어 지원 서비스는 이제 JSDoc 스타일로 작성된 &lt;code&gt;/** @deprecated */&lt;/code&gt; 주석을 지원합니다. 그래서 자동완성 기능을 사용할 때 지원이 중단된 것을 바로 확인할 수 있습니다. VS Code를 예로 들면, 지원이 중단된 항목에는 &lt;s&gt;취소선&lt;/s&gt;이 표시됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI4U1R/btqHlURMsSq/AYD0LCT9ctbCoKFXBWLDs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI4U1R/btqHlURMsSq/AYD0LCT9ctbCoKFXBWLDs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI4U1R/btqHlURMsSq/AYD0LCT9ctbCoKFXBWLDs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI4U1R%2FbtqHlURMsSq%2FAYD0LCT9ctbCoKFXBWLDs1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;새 기능을 추가하는 데에 기여해주신 &lt;a href=&quot;https://github.com/Kingwl&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Wenlu Wang&lt;/a&gt;에게 감사드립니다. 자세한 내용은 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/38523&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;에디터 시작 시점에 활용할 수 있는 부분 지원 모드&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;에디터 실행 시간이 긴 것에 많은 유저들이 불편을 겪는다는 이야기를 들었습니다. 프로젝트 규모가 클수록 더 그렇습니다. 이 문제의 원인은 프로그램 구축(program construction)이라고 하는 과정 때문입니다. 이 과정은 최상위 폴더에 있는 파일을 찾아와서 파싱하고, 의존성을 분석하며, 의존성의 의존성을 따라가며 분석하는 과정입니다. 그래서 프로젝트 규모가 클수록 이 과정을 처리하는 시간이 오래 걸리기 때문에 에디터를 켠 직후에는 TypeScript 언어 지원 기능을 활용할 수 없었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 4.0은 이 문제를 해결하기 위해 언어 지원 서비스가 전체 로딩되기 전에 활용할 수 있는 부분 지원 모드(partial semantic mode)를 추가했습니다. 이 모드의 핵심은 에디터를 실행했을 때 열리는 현재 파일만이라도 언어 지원 서비스를 지원하는 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;실행 환경과 프로젝트에 따라 달라질 수 있지만, 일반적으로 Visual Studio Code에서 TypeScript 언어 지원 서비스는 초기화되기 전까지 20초 ~ 1분 동안 동작하지 않았습니다. 하지만 이제 부분 지원 모드를 활용하면 단 몇 초만 있어도 언어 지원 서비스를 활용할 수 있습니다. 아래 영상을 확인해 보세요. 왼쪽 화면은 TypeScript 3.9를 적용한 화면이며, 오른쪽은 TypeScript 4.0을 적용한 화면입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;video loop=&quot;&quot; autoplay=&quot;&quot; muted=&quot;&quot; style=&quot;width:100%;height:100%;&quot; src=&quot;https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2020/08/partialModeFast.mp4&quot; class=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;코드량이 많은 프로젝트를 열고 에디터를 재시작하면 TypeScript 3.9가 적용된 쪽에서는 코드 자동완성 기능이나 빠른 참조 기능이 제대로 동작하지 않습니다. 하지만 TypeScript 4.0이 적용된 화면은 현재 파일과 관련된 내용이라면 완전히 초기화되지 않은 시점에도 사용자가 원하는 정보를 제공할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;아직까지 이 모드는 &lt;span style=&quot;color: #333333;&quot;&gt;&lt;a href=&quot;http://code.visualstudio.com/insiders&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Visual Studio Code Insiders&lt;/a&gt;가 설치된 &lt;/span&gt;&lt;a href=&quot;http://code.visualstudio.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Visual Studio Code&lt;/a&gt;에서만 제대로 동작합니다. 그리고 이 모드는 아직 UX나 기능 측면에서 부족함이 있기 때문에 &lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/39035&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;개선할 내용&lt;/a&gt;을 정리해서 따로 관리하고 있습니다. 개선할 아이디어가 있다면 피드백을 주셔도 좋습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자세한 내용은 &lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/37713&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;제안 초안&lt;/a&gt;이나 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/38561&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;, 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/39035&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이슈&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;더 똑똑해진 심볼 자동 로드&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;심볼을 자동으로 로드하는 기능은 코딩 생산성을 높여주는 측면에서 아주 훌륭한 기능이지만 이 기능이 언제나 완벽하게 동작하는 것은 아닙니다. 아직은 개발자가 개입해야만 하는 부분이 있습니다. 저희가 확인한 바로는 TypeScript로 작성된 라이브러리라도 프로젝트에 한번도 로드되지 않은 심볼은 자동으로 로드할 수 없는 현상도 있었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;code&gt;`@types`&lt;/code&gt; 패키지에서는 잘 동작하던 자동 로드 기능이 개별 패키지에서는 왜 동작하지 않을까요? 이 현상의 원인은 심볼 자동 로드 기능이 해당 프로젝트에 한 번이라도 로드된 패키지들 대상으로만 동작하기 때문이었습니다. TypeScript는 기특하게도 &lt;code&gt;node_modules/@types&lt;/code&gt;에 있는 패키지를 프로젝트에 자동으로 추가하고 이 패키지 대상으로는 심볼 자동 로드 기능이 잘 동작했습니다. 하지만 일반 &lt;code&gt;node_modules&lt;/code&gt;에 설치된 npm 패키지를 크롤링하는 과정은 상대적으로 비용이 높은 작업이기 때문에 심볼 자동 로드 기능이 제대로 동작하지 않는 경우가 있었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이런 상황은 npm 패키지를 새로 설치한 직후에 프로젝트에 로드해서 사용하려고 할 때도 종종 발생합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;TypeScript 4.0은 이런 시나리오를 보완하기 위해 &lt;code&gt;package.json&lt;/code&gt; 파일의 &lt;code&gt;dependencies&lt;/code&gt; 필드와 &lt;code&gt;peerDependencies&lt;/code&gt; 필드를 처리하는 로직을 따로 추가했으며, 이 필드를 처리하기 위해 수집한 정보는 심볼 자동 로드 기능에만 활용되고 타입을 검사할 때는 활용되지 않습니다. 이 정보를 활용하면 &lt;code&gt;node_modules&lt;/code&gt; 폴더를 전부 뒤지지 않고 설치한 npm 패키지만 대상으로 심볼 자동 로드 기능을 활용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이런 일은 별로 없겠지만 &lt;code&gt;package.json&lt;/code&gt;에 추가된 npm 패키지 중 프로젝트에 아직 로드된 적이 없는 타입 정의 관련 패키지가 10개를 넘어가면 프로젝트 로딩을 방해하지 않기 위해 이 기능이 자동으로 비활성화됩니다. 이 경우에 기능을 명시적으로 활성화하거나 전체를 대상으로 비활성화하려면 에디터에서 환경설정 값을 변경하면 됩니다. Visual Studio Code라면 &quot;Include Package JSON AUto Imports&quot; 메뉴(또는 &lt;code&gt;typescript.preferences.includePackageJsonAutoImports&lt;/code&gt;)에서 설정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC6YHI/btqHhFVuesi/kMPBdC8i5EkLkwSTchk4Vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC6YHI/btqHhFVuesi/kMPBdC8i5EkLkwSTchk4Vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC6YHI/btqHhFVuesi/kMPBdC8i5EkLkwSTchk4Vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC6YHI%2FbtqHhFVuesi%2FkMPBdC8i5EkLkwSTchk4Vk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자세한 내용은 &lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/37812&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;제안 이슈&lt;/a&gt;와 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/38923&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹사이트 개편&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://www.typescriptlang.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript 웹사이트&lt;/a&gt;가 새롭게 개편되었습니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ML35u/btqG7Ro3eGa/cR3AvkJzg4sgK9b22ekTi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ML35u/btqG7Ro3eGa/cR3AvkJzg4sgK9b22ekTi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ML35u/btqG7Ro3eGa/cR3AvkJzg4sgK9b22ekTi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FML35u%2FbtqG7Ro3eGa%2FcR3AvkJzg4sgK9b22ekTi1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-the-new-typescript-website/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;사이트가 개편된 것은 사실 좀 됐습니다&lt;/a&gt;. 하지만 이렇게 바뀐 것을 어떻게 생각하실지 지금이라도 이야기를 들어보고 싶습니다! 사용방법이 궁금하거나 개선할 아이디어가 있다면 &lt;a href=&quot;https://github.com/microsoft/TypeScript-Website&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;웹사이트 이슈&lt;/a&gt;로 등록해 주세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Breaking Changes&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;lib.d.ts&lt;/code&gt; 수정&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;code&gt;lib.d.ts&lt;/code&gt; 정의 파일이 수정되었습니다. 특히 DOM과 관련된 내용이 변경되었는데, IE의 오래된 버전에서 사용되던 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/origin&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;document.origin&lt;/code&gt;&lt;/a&gt;이 제거되고 Safari MDN이 권장하는 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/origin&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;self.origin&lt;/code&gt;&lt;/a&gt;이 추가되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로퍼티-게터/세터 오버라이딩 금지&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이전 버전까지는 &lt;code&gt;useDefineForClassFields&lt;/code&gt; 옵션을 사용한 상태에서 프로퍼티가 게터/세터를 오버라이딩하거나 게터/세터가 프로퍼티를 오버라이딩하는 문법은 에러로 처리했습니다. 이제는 이 옵션이 없어도 이 시나리오에 해당되면 에러를 발생시킵니다. 이 에러는 클래스 상속관계에서만 발생합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
class Base {
    get foo() {
        return 100;
    }
    set foo() {
        // ...
    }
}

class Derived extends Base {
    foo = 10;
//  ~~~
// 에러!
// 'foo'는 `Base` 클래스에서 접근자로 정의되어 있습니다.
// `Derived` 클래스에서 프로퍼티 멤버로 오버라이드할 수 없습니다.
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
class Base {
    prop = 10;
}

class Derived extends Base {
    get prop() {
    //  ~~~~
    // 에러!
    // 'prop'는 `Base` 클래스에서 프로퍼티로 정의되어 있습니다.
    // `Derived` 클래스에서 접근자로 오버라이드할 수 없습니다.
        return 100;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자세한 내용은 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/37894&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;delete&lt;/code&gt; 대상은 반드시 옵션 항목이어야 합니다.&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;code&gt;strictNullChecks&lt;/code&gt; 옵션을 활성화한 상태에서 &lt;code&gt;delete&lt;/code&gt; 연산자를 사용한다면 이 연산자의 대상이 반드시 &lt;code&gt;any&lt;/code&gt;, &lt;code&gt;unknown&lt;/code&gt;, &lt;code&gt;never&lt;/code&gt;이거나 옵션 항목(이 경우에는 &lt;code&gt;undefined&lt;/code&gt; 타입도 가능)이어야 합니다. 그렇지 않다면 에러가 발생합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;
interface Thing {
    prop: string;
}

function f(x: Thing) {
    delete x.prop;
    //     ~~~~~~
    // 에러! `delete` 연산자의 대상은 반드시 옵션 항목이어야 합니다.
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;자세한 내용은 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/37921&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TypeScript 노드 팩토리 지원 중단&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;지금까지 TypeScript는 AST(Abstract Syntax Tree, 추상 구문 트리) 노드 생성을 위해 팩토리 함수들을 제공했습니다. 그리고 TypeScript 4.0부터는 새로운 API 형태로 노드 팩토리를 제공하기로 결정했습니다. 노드 팩토리 사용방법이 변경되니 자세한 내용을 확인하려면 관련 &lt;a href=&quot;https://github.com/microsoft/TypeScript/pull/35282&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;풀 리퀘스트&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그다음은?&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이제 릴리즈 노트를 마무리할 때가 되었습니다. 하지만 이 시점에도 &lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/40124&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript 4.1 개발 계획&lt;/a&gt;은 이미 준비되어 있습니다. TypeScript 4.1 버전에 제공될 기능은 &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/nightly-builds.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;최신 빌드 버전&lt;/a&gt;과 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-next&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;에디터 확장 플러그인&lt;/a&gt;을 설치하면 확인해볼 수 있습니다. TypeScript 4.0이든 4.1이든 피드백은 언제든 환영합니다! &lt;a href=&quot;https://twitter.com/typescript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;트위터&lt;/a&gt;나 &lt;a href=&quot;https://github.com/Microsoft/TypeScript/issues/new/choose&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub 이슈&lt;/a&gt;로 등록해 주세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;다시 한번 언급하자면, 저희는 커뮤니티 멤버들의 헌신에 크게 신세 지고 있다고 생각합니다. 그래서 앞으로도 TypeScript나 JavaScript를 코딩하는 시간이 순수한 즐거움으로 남도록 만들어 드리고 싶습니다. 이런 목적으로 언어 자체를 개선하고 코딩 환경을 개선하며, 동작 성능을 항상 신경쓰고, 개발 UX를 다시 돌아보면서 코딩과 관련된 모든 경험을 개선할 수 있도록 노력하겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;감사합니다. 이제 TypeScript 4.0을 즐겨보세요!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;- Daniel Rosenwasser, TypeScript 팀 드림.&lt;/p&gt;</description>
      <category>TypeScript</category>
      <category>typescript</category>
      <author>한장현</author>
      <guid isPermaLink="true">https://han41858.tistory.com/52</guid>
      <comments>https://han41858.tistory.com/52#entry52comment</comments>
      <pubDate>Wed, 26 Aug 2020 03:13:15 +0900</pubDate>
    </item>
    <item>
      <title>Angular 10.0.0 릴리즈 노트</title>
      <link>https://han41858.tistory.com/51</link>
      <description>&lt;p&gt;&amp;nbsp;안녕하세요. 한장현입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;한국시각으로 6/25 새벽 03:46에 Angular 10.0.0이 출시되었습니다.&lt;br /&gt;&amp;nbsp;이전 메이저 업데이트가 Ivy 때문에 오래 걸려서 그런지 이번 업데이트는 기존 주기보다 약간 빠른 시기에 발표되었는데, 어떤 내용이 바뀌었는지 릴리즈 노트를 살펴봤습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Angular 10.0.0 버전이 드디어 나왔습니다! 이번 &lt;a href=&quot;https://semver.org/#spec-item-8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;메이저&lt;/a&gt; 릴리즈는 Angular가 실행되는 플랫폼 관련 코드 전체와 Angular Material, Angular CLI를 모두 포함하는 대규모 업데이트 입니다. 하지만 Angular 9.0을 발표한지 아직 4개월밖에 되지 않았기 때문에, 이번 릴리즈는 지금까지 있었던 릴리즈와 비교하면 변동량이 적은 편입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2bfqz/btqFaMI2vEg/21qpsKR5Tzref3qw7K1RtK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2bfqz/btqFaMI2vEg/21qpsKR5Tzref3qw7K1RtK/img.jpg&quot; data-alt=&quot;Butterfly Beach by Minko Gechev&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2bfqz/btqFaMI2vEg/21qpsKR5Tzref3qw7K1RtK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2bfqz%2FbtqFaMI2vEg%2F21qpsKR5Tzref3qw7K1RtK%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Butterfly Beach by Minko Gechev&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Angular 팀은 급변하는 JavaScript 생태계와 속도를 맞추기 위해 1년에 두 번 메이저 버전 릴리즈하는 것을 목표로 하고 있습니다. 그래서 Angular 11은 이번 가을에 나올 예정입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;이번 릴리즈에는 어떤 것이 달라졌나요?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;기간 선택 컴포넌트&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;기간을 선택할 수 있는 컴포넌트가 &lt;span style=&quot;color: #333333;&quot;&gt;Angular Material에&lt;span&gt; &lt;/span&gt;&lt;/span&gt;추가되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PPzk3/btqFbVEN6Bv/5RjU56dB4HQ7Hq0zTxf5WK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PPzk3/btqFbVEN6Bv/5RjU56dB4HQ7Hq0zTxf5WK/img.gif&quot; data-alt=&quot;기간 선택 컴포넌트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PPzk3/btqFbVEN6Bv/5RjU56dB4HQ7Hq0zTxf5WK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/PPzk3/btqFbVEN6Bv/5RjU56dB4HQ7Hq0zTxf5WK/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;기간 선택 컴포넌트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 컴포넌트는 &lt;code&gt;mat-date-range-input&lt;/code&gt;이나 &lt;code&gt;mat-date-range-picker&lt;/code&gt; 컴포넌트로 사용하면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://stackblitz.com/angular/nknyovevygv?file=src%2Fapp%2Fdate-range-picker-overview-example.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;StackBlitz에서 동작하는 것&lt;/a&gt;을 직접 확인해 보세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그리고 &lt;a href=&quot;https://next.material.angular.io/components/datepicker/overview#date-range-selection&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Angular Material 문서&lt;/a&gt;도 확인해 보세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;CommonJS 모듈이 사용되면 경고 표시&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;CommonJS 모듈 방식으로 패키징된 의존성 패키지를 사용하면 애플리케이션의 &lt;a href=&quot;https://web.dev/commonjs-larger-bundles/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;빌드 결과물 용량이 커지고 앱도 느려질 수 있습니다.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그래서 Angular 10 버전부터는 이런 패키지가 사용되었을 때 경고 메시지를 표시합니다. 이 경고 메시지가 출력되면 CommonJS 모듈로 빌드된 패키지 대신 ECMAScript 모듈(ESM)로 빌드된 패키지를 사용할 수 있는지 검토해 보는 것이 좋습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMbeP8/btqE9K6eKDm/iXUlhf86f10pcVfbQg5swK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMbeP8/btqE9K6eKDm/iXUlhf86f10pcVfbQg5swK/img.png&quot; data-alt=&quot;CommonJS나 AMD 방식으로 빌드된 패키지가 사용되었을 때 표시되는 경고&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMbeP8/btqE9K6eKDm/iXUlhf86f10pcVfbQg5swK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMbeP8%2FbtqE9K6eKDm%2FiXUlhf86f10pcVfbQg5swK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;CommonJS나 AMD 방식으로 빌드된 패키지가 사용되었을 때 표시되는 경고&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;엄격한 규칙 옵션&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Angular 10 버전부터는 &lt;code&gt;ng new&lt;/code&gt; 명령으로 워크스페이스를 생성할 때 좀 더 엄격한 규칙을 지정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ng new --strict&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 플래그를 사용하면 새로운 프로젝트를 생성하면서 이 프로젝트의 TypeScript 환경설정과 Angular CLI 환경설정에 엄격한 개발 규칙을 적용합니다. 이런 구성 방식은 버그를 사전에 발견할 수 있다는 점에서 유리하기 때문에 유지보수 측면에서 도움이 될 수 있습니다. 어떤 내용이 적용되는지 자세하게 알아보면 이렇습니다:&lt;/p&gt;
&lt;p&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;TypeScript strict 모드를 활성화합니다.&lt;/li&gt;
&lt;li&gt;템플릿 타입 검사 모드를 strict 모드로 설정합니다.&lt;/li&gt;
&lt;li&gt;번들 결과물의 한계 용량을 20% ~ 40% 정도로 줄입니다.&lt;/li&gt;
&lt;li&gt;TypeScript 규칙에 &lt;a href=&quot;https://palantir.github.io/tslint/rules/no-any/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;no-any 규칙&lt;/a&gt;을 추가합니다.&lt;/li&gt;
&lt;li&gt;애플리케이션이 받는 트리 셰이킹의 효과를 최대화하기 위해 &quot;sideEffects&quot;: false 설정을 추가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;JavaScript 생태계와 발 맞추기&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그리고 지금까지 그러했듯이 JavaScript 생태계의 발전 속도에 맞추기 위해 의존 관계에 있는 패키지 버전을 업데이트했습니다.&lt;/p&gt;
&lt;p&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;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript 3.9 버전&lt;/a&gt;을 기준으로 지원합니다.&lt;/li&gt;
&lt;li&gt;TSLib v2.0 버전을 적용했습니다.&lt;/li&gt;
&lt;li&gt;TSLint v6 버전을 적용했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;프로젝트 파일 구조도 약간 변경되었습니다. Angular 10 버전부터는 &lt;code&gt;tsconfig.base.json&lt;/code&gt; 파일이 새로 추가됩니다. 이 파일은 기존에 있던 &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/tsconfig-json.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;tsconfig.json&lt;/code&gt; 파일&lt;/a&gt;과 비슷한 역할을 하지만, 코드 에디터에 활용되는 타입정보와 패키지 정보를 더 효율적으로 제공하기 위해 도입했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;지원 브라우저 변경&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Angular 10 버전으로 생성하는 프로젝트는 오래된 브라우저를 더 조금만 지원합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;v9 기본 목록&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S72mu/btqFaU09hrE/SBgmea3Pjv3kyKmLyk0dy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S72mu/btqFaU09hrE/SBgmea3Pjv3kyKmLyk0dy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S72mu/btqFaU09hrE/SBgmea3Pjv3kyKmLyk0dy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS72mu%2FbtqFaU09hrE%2FSBgmea3Pjv3kyKmLyk0dy1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;v10 기본 목록&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSU3nL/btqFagqgGVZ/RSckgtoFGlON1RsfAvXvV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSU3nL/btqFagqgGVZ/RSckgtoFGlON1RsfAvXvV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSU3nL/btqFagqgGVZ/RSckgtoFGlON1RsfAvXvV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSU3nL%2FbtqFagqgGVZ%2FRSckgtoFGlON1RsfAvXvV0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이렇게 오래된 브라우저를 버리면서 얻게된 이득이 있습니다. 이제는 설정을 변경하지 않는한 애플리케이션을 ES5 버전으로 빌드하지 않습니다. 정책상 IE나 UC 브라우저를 지원하기 위해 증분 로딩용 ES5 빌드가 필요하다면 &lt;code&gt;.browserlistrc&lt;/code&gt; 파일에 &lt;a href=&quot;https://github.com/browserslist/browserslist#browserslist-&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;지원 브라우저 목록을 추가&lt;/a&gt;하면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;오래된 이슈 처리&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이번 메이저 릴리즈를 준비하면서 커뮤니티 활동에도 큰 노력을 기울였습니다. 그래서 최근 3주간 Angular 팀이 처리한 이슈는 &lt;a href=&quot;https://github.com/angular/angular/issues&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프레임워크&lt;/a&gt;나 &lt;a href=&quot;https://github.com/angular/angular-cli/issues&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;툴&lt;/a&gt;, &lt;a href=&quot;https://github.com/angular/components/issues&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;컴포넌트&lt;/a&gt; 모두 포함해서 700건 정도 됩니다. 그리고 현재 처리중인 이슈는 2,000개가 넘으며, 앞으로 몇달동안 좀 더 집중해서 그동안 해결하지 못했던 이슈를 최대한 처리하려고 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;지원이 중단되는 기능&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;지원이 중단되는 것으로 새롭게 지정된 내용이 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://g.co/ng/apf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Angular 프레임워크 패키지&lt;/a&gt;는 더이상 ESM5 버전이나 FESM5 버전으로 제공되지 않습니다. 이제는 굳이 ES5 버전으로 프레임워크 코드를 제공하지 않아도 된다고 판단했기 때문에 이런 결정을 내렸습니다. 이 결정으로 인해 이제는 &lt;code&gt;yarn&lt;/code&gt;이나 &lt;code&gt;npm&lt;/code&gt;을 사용해서 Angular 패키지를 설치할 때 받아야 하는 용량이 119MB 정도 줄었으며, 설치 시간도 당연히 줄어들었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그리고 커뮤니티와 오랫동안 대화를 나눠본 결과, IE 9/10, &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Explorer_Mobile&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Internet Explorer Mobile&lt;/a&gt;은 지원을 중단하기로 결정했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;지원이 중단되는 기능과 정책에 대해 자세하게 알아보려면 &lt;a href=&quot;http://v10.angular.io/guide/deprecations&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;역주: Angular에서 지원을 중단하기로 결정한 기능은 수정할 시간을 제공하기 위해 메이저 버전 2개까지는 해당 코드를 제거하지 않습니다. 따라서 IE 9/10 지원은 Angular 11 버전까지 유효할 것이며, 메이저 버전이 6개월마다 발표되기 때문에 약 1년 후 발표되는 Angular 12 버전부터 IE 9/10 지원이 중단될 것으로 보입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;Angular 10 버전으로 업그레이드하려면&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;버전 업그레이드와 관련된 설명과 진행방법을 확인하려면 이전과 마찬가지로 &lt;a href=&quot;https://update.angular.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;update.angular.io&lt;/a&gt; 사이트를 확인하면 됩니다. 그리고 이 때 메이저 버전을 여러개 바꿔야 한다면 &lt;span style=&quot;color: #333333;&quot;&gt;원활한 업그레이드를 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;한 번에 메이저 버전 하나씩 작업하는 것이 좋습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Angular 9버전에서 간단하게 업그레이드 하려면 다음 명령을 실행하면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ng update @angular/cli @angular/core&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;버전 업그레이드 과정에 대해 자세하게 알아보려면 &lt;a href=&quot;https://v10.angular.io/guide/updating-to-version-10&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이전과 비교했을 때 이번 메이저 릴리즈를 적용한다고 해도 기존 코드를 수정할 필요는 거의 없습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;기존 프로젝트에서 ng update를 실행해 봤을 때 browserlist 파일을 .browserlistrc 파일로 바꾸는 것이나 tsconfig.base.json 파일이 생성되는 것은 모두 ng update 명령이 자동으로 처리했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그리고 ng serve로 이전 버전에서 만든 프로젝트를 바로 실행할 수 있었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;다만, TypeScript 기준 버전이 3.9로 올라갔기 때문에 이와 관련된 코드를 수정해야 할 수 있으며, strict 모드를 적용한다면 코드 수정량이 매우 많아질 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;저는 strict 모드에서 적당히 잡다가 다시 이전 모드로 돌아왔습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이 글이 Angular를 사용하시는 분들에게 도움이 되기를 바랍니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;감사합니다.&lt;/p&gt;</description>
      <category>Angular</category>
      <category>Angular</category>
      <author>한장현</author>
      <guid isPermaLink="true">https://han41858.tistory.com/51</guid>
      <comments>https://han41858.tistory.com/51#entry51comment</comments>
      <pubDate>Sat, 27 Jun 2020 02:41:38 +0900</pubDate>
    </item>
    <item>
      <title>Deno 1.0</title>
      <link>https://han41858.tistory.com/50</link>
      <description>&lt;p&gt;안녕하세요. 한장현입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한국 시각으로 5월 14일 07:38에 deno v1.0.0이 릴리즈 되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;node.js를 대체하기 위해 나온 deno가 얼마나 완성되었는지, deno v1.0.0 릴리즈와 함께 작성된 &lt;a href=&quot;https://deno.land/v1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;블로그 글&lt;/a&gt;을 번역해 봤습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;v1_wide.jpg&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIHONc/btqEbQL8LRb/k53aYoWmjWboPbNG0m63Q0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIHONc/btqEbQL8LRb/k53aYoWmjWboPbNG0m63Q0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIHONc/btqEbQL8LRb/k53aYoWmjWboPbNG0m63Q0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIHONc%2FbtqEbQL8LRb%2Fk53aYoWmjWboPbNG0m63Q0%2Fimg.jpg&quot; data-filename=&quot;v1_wide.jpg&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1024&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Deno 1.0&lt;/h2&gt;
&lt;p&gt;by Ryan Dahl, Bert Belder, and Bartek Iwańczuk&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;동적 언어는 그 자체로도 훌륭한 툴이라고 할 수 있습니다. 빠르고 간결하게 작성할 수 있지만 복잡한 시스템은 물론이고 아이디어를 간단하게 구현하는 용도로도 활용할 수 있습니다. 시스템 환경이나 메모리 관리를 신경 쓸 필요도 없습니다. 최근에는 특히 Rust나 Go와 같은 언어를 활용하면서 복잡한 기계어를 좀 더 쉽게 활용할 수 있었고, 컴퓨터 인프라 환경을 발전시키는 데에도 큰 기여를 했습니다. 하지만 우리는 다양한 영역에서 발생하는 문제를 해결할 수 있는 강력한 스크립트 환경을 갖추는 것이 여전히 중요하다고 생각합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;현재 동적 언어 중에서 가장 널리 사용되고 있는 것은 JavaScript입니다. JavaScript는 웹 브라우저가 동작하는 모든 디바이스에서 실행할 수 있습니다. JavaScript를 자유롭게 활용할 수 있는 개발자들은 이미 세상에 널려있으며, 개발자들은 이제 코드 최적화에 더 힘쓰고 있습니다. ECMA International이라는 표준 기구가 제 역할을 하면서 JavaScript도 조금씩 개선되고 있습니다. 브라우저 환경이나 독립 프로세스로 실행되는 환경이라면 JavaScript를 선택하는 것이 이제는 당연해 보입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이전에 개발했던 Node.js는 소프트웨어 플랫폼으로써 아주 성공적이었습니다. Node.js는 웹 개발에 활용되는 툴이나 독립적인 웹 서버를 구현하는 것 외에도 수많은 용도로 활용되고 있습니다. 하지만 Node.js는 JavaScript가 지금과는 많이 다른 언어였던 2009년에 설계되었습니다. 그때는 필요했기 때문에 Node.js에 자체적으로 도입된 컨셉들이, JavaScript 표준 기구에서 논의되면서 다른 방식으로 도입된 것들이 있습니다. 자세한 내용은 &lt;a href=&quot;https://www.youtube.com/watch?v=M3BM9TB-8yA&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Node의 설계 실수&lt;/a&gt; 발표 영상에서 확인할 수 있습니다. 하지만 이제 Node.js 사용자는 너무나 많기 때문에 Node.js의 잘못된 것들을 당장 바로잡는 것은 너무 어려운 일이 되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;JavaScript가 변화하면서 TypeScript와 같은 파생 프로젝트도 생겨났습니다. 이제 Node 프로젝트는 빌드 시스템과 프로젝트 자체를 관리하는 툴만으로도 굉장히 복잡해져서 동적 언어의 즐거움을 느끼기 힘들어졌습니다. 게다가 외부 라이브러리가 꼭 npm 저장소를 통해야만 한다는 것은 웹의 이상적인 방향과도 맞지 않습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;우리는 JavaScript 생태계와 관련 인프라 환경은 더 단순해져야 한다고 생각했습니다. 그래서 다양한 영역에 활용할 수 있으면서도 즐거움과 생산성을 함께 찾을 수 있는 스크립트 환경을 찾게 되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커맨드 라인에서 동작하는 웹 브라우저&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno는 JavaScript나 TypeScript를 웹 브라우저 밖에서 실행하는 새로운 실행환경입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno는 복잡한 기능을 단독으로 실행할 수 있는 툴이 되어야 한다고 생각합니다. Deno 애플리케이션은 파일 하나로 구성할 수 있습니다. 그리고 웹 브라우저처럼 추가 라이브러리가 필요하면 가져와서 활용하면 됩니다. Deno를 활용하면 다른 툴 없이도 복잡한 로직을 실행할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;line-numbers&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { serve } from &quot;https://deno.land/std@0.50.0/http/server.ts&quot;;

for await (const req of serve({ port: 8000 })) {
  req.respond({ body: &quot;Hello World\n&quot; });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;HTTP 서버 모듈은 코드 한 줄로 불러올 수 있습니다. 이 모듈에 대한 환경설정은 필요 없으며 추가로 설치해야 할 것도 없습니다. &lt;code class=&quot;language-bash&quot;&gt;deno run example.js&lt;/code&gt;라고 실행하기만 하면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;다른 브라우저들과 마찬가지로 이 코드는 안전한 샌드박스 안에서 실행됩니다. 이 스크립트 파일은 하드 드라이브에 접근할 수 없으며, 네트워크를 맘대로 연결할 수도 없고, 권한없이 악성 코드를 실행할 수도 없습니다. 브라우저에서 카메라나 마이크를 활용하려면 먼저 사용자에게 권한을 받아야 합니다. Deno도 이와 비슷한 방식을 터미널에서 활용합니다. 사실 위 코드는 &lt;code class=&quot;language-bash&quot;&gt;--allow-net&lt;/code&gt; 옵션을 지정하지 않으면 제대로 동작하지 않습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno는 브라우저의 표준 JavaScript API를 지키기 위해 노력했습니다. 물론 모든 브라우저 API가 Deno와 관련이 있는 것은 아니지만, 최소한 Deno가 표준에서 벗어난 것은 아닙니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TypeScript 1급 클래스(first class) 지원&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;우리는 Deno가 한 줄짜리 스크립트부터 복잡한 서버사이드 비즈니스 로직을 다루는 용도로도 활용되기를 바랬습니다. 그리고 프로그램은 날이 갈수록 복잡해지기 때문에 타입을 제대로 구별해야 할 필요성도 계속해서 증가합니다. TypeScript는 JavaScript가 기반이며 개발자가 타입에 대한 정보를 추가할 수 있기 때문에 적절한 대안이 되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno는 다른 툴 없이도 TypeScript를 지원합니다. 실행환경도 TypeScript를 염두에 두고 설계되었습니다. 커맨드 라인에서 &lt;code class=&quot;language-bash&quot;&gt;deno types&lt;/code&gt; 명령을 실행해보면 Deno가 제공하는 타입 정의를 확인할 수 있습니다. 그리고 Deno의 기본 모듈은 모두 TypeScript로 작성되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모든 것은 Promise입니다.&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Node는 JavaScript가 Promise, async/await 컨셉을 도입하기 이전에 설계되었습니다. Node는 Promise를 EventEmitter로 구현했으며 이 방식으로 소켓 API, HTTP API를 설계했습니다. async/await의 장점을 살펴보지 않아도 EventEmitter 패턴은 백 프레셔(back-pressure) 이슈가 있습니다. TCP 소켓 하나를 받는 경우를 생각해 봅시다. 소켓이 패킷을 받으면 데이터 이벤트를 발생시킵니다. 이렇게 발생한 데이터는 콜백 함수들을 거치는 방식으로 자유롭게 처리될 수 있었기 때문에 프로세스에는 곧 수많은 이벤트들이 발생했었습니다. 하지만 이 와중에도 Node는 계속 새로운 데이터 이벤트를 받기 때문에 TCP 소켓이 데이터를 모두 처리할 수 없는 상태가 되더라도 외부에서는 이 상황을 알 수 없고 결국 데이터 이벤트가 넘쳐나는 상황이 될 수 있습니다. 이 문제를 해결하기 위해 &lt;code class=&quot;language-typescript&quot;&gt;pause()&lt;/code&gt; 메소드가 추가되었습니다. 이 메소드는 문제 상황을 해결할 수는 있었지만 코드를 추가로 작성해야 한다는 점에서 베스트 솔루션은 아닙니다. 그리고 프로세스가 바쁜 상태에서는 이 방법으로도 완전히 해결될 수 없기 때문에 응답 시간이 길어지는 문제는 여전히 존재합니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;Deno에서도 소켓은 여전히 비동기로 동작하지만 새로운 데이터는 &lt;code class=&quot;language-typescript&quot;&gt;read()&lt;/code&gt;가 있을 때만 받습니다. 소켓을 천천히 받기 위해 잠시 멈추는 코드는 이제 필요 없습니다. TCP 소켓 이외에도 이런 방식을 활용했습니다. 시스템과 맞닿은 계층부터 Promise를 활용합니다. 우리는 이런 바인딩을 &quot;ops&quot;라고 부릅니다. Deno에서 실행되는 모든 콜백은 Promise로 동작합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Rust에도 Promise와 비슷한 구현체가 있긴 합니다. Future라고 하는데, Deno는 Rust에서 활용하던 future 방식의 API를 JavaScript Promise로 구현했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rust&amp;nbsp;API&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno 구성요소 중 가장 중요한 것은 Deno 커맨드라인 인터페이스(CLI)입니다. 그리고 이 CLI의 버전은 오늘로 1.0입니다. 다만 Deno는 통으로 짜인 프로그램이 아니라 Rust 크레이트(crate) 묶음과 비슷하게 설계되었기 때문에 다양한 계층으로 존재합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;&lt;a href=&quot;https://crates.io/crates/deno_core&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;deno_core&lt;/a&gt; 크레이트는 Deno의 뼈대입니다. 이 크레이트는 TypeScript나 &lt;a href=&quot;https://tokio.rs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Tokio&lt;/a&gt;와도 직접 관련이 없습니다. 이 패키지는 단순하게 Op과 Resource 인프라 환경만을 제공합니다. 그리고 Rust future와 JavaScript Promise를 바인딩하는 컨셉을 제공합니다. 물론 CLI도 deno_core를 활용하도록 설계되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;&lt;a href=&quot;https://crates.io/crates/rusty_v8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;rusty_v8&lt;/a&gt; 크레이트는 Rust와 V8 C++ API를 바인딩하는 크레이트입니다. 이 크레이트는 기존 C++ API를 최대한 활용하도록 구현되었습니다. 그래서 API가 접근하는 Rust 객체는 거의 정확하게 C++ 객체와 매칭되기 때문에 바인딩 비용이 없다고도 할 수 있습니다. 이 크레이트를 활용하면 Github Action CI가 제공하는 바이너리 코드를 그대로 활용할 수 있으면서도 V8 빌드 설정을 변경해서 컴파일하는 방식으로 활용할 수도 있습니다. V8의 소스 코드는 모두 크레이트에 담겨 배포됩니다. 마지막으로 rusty_v8 크레이트는 안전한(safe) 인터페이스를 제공하려고 합니다. 아직 100% 안전하다고 할 수는 없지만, 이 작업은 거의 끝나갑니다. VM과 V8은 아주 복잡하게 상호작용하는 구조였지만 Deno에서 발생하는 수많은 버그를 처리하면서 결국 안전하게 동작할 수 있는 결과물을 만들어 냈습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;안정성 (Stability)&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno는 일관된 API를 제공할 것을 약속합니다. Deno에는 수많은 인터페이스와 컴포넌트들이 있습니다. 우리가 말하는 &quot;안정성&quot;이라는 것이 어떤 것인지 확실하게 짚고 넘어갑시다. JavaScript로 OS와 통신하는 API는 모두 &quot;Deno&quot;라는 네임스페이스 안에 있습니다. &lt;code class=&quot;language-typescript&quot;&gt;Deno.open()&lt;/code&gt;과 같은 식입니다. 이런 메소드 이름은 신중하게 검토되어 결정되었으며 앞으로도 변경되지 않을 것입니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;그리고 아직 안정화되지 않은 기능들은 &lt;code class=&quot;language-bash&quot;&gt;--unstable&lt;/code&gt; 옵션을 붙여야 활용할 수 있습니다. 안정화되지 않은 인터페이스를 확인하려면 &lt;a href=&quot;https://doc.deno.land/https/raw.githubusercontent.com/denoland/deno/master/cli/js/lib.deno.unstable.d.ts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;를 참고하세요. 앞으로 릴리즈될 때마다 이 API 중 일부는 안정화될 수 있습니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;code class=&quot;language-bash&quot;&gt;setTimeout()&lt;/code&gt;이나 &lt;code class=&quot;language-bash&quot;&gt;fetch()&lt;/code&gt;&amp;nbsp;같이 전역 네임스페이스에서 참조할 수 있는 객체들이 있습니다. 이 인터페이스들은 일단 브라우저에서 사용하던 대로 남겨두기로 했지만 호환성에 문제가 발견되면 다른 방식으로 변경될 수 있습니다. 이런 인터페이스들은 브라우저의 표준이지 우리의 표준은 아니기 때문입니다. 그리고 이런 이유로 인터페이스가 변경된다면 이것은 &quot;인터페이스 변경&quot;이 아니라 &quot;버그 픽스&quot;로 처리할 것입니다. 브라우저 표준 API의 호환성에 문제가 발견되면 이 문제는 메이저 릴리즈 전에 수정될 것입니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;Deno에는 Rust를 활용하는 API가 많습니다. deno_core 크레이트와 rusty_v8 크레이트가 특히 그렇습니다. 그래서 이 API들은 아직 1.0이 아닙니다. 물론 작업은 계속되고 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;한계&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno는 Node를 기반으로 개선된 것이 아닙니다. Deno는 완전히 새롭게 작성되었습니다. Deno는 아직 개발한 지 2년밖에 되지 않았지만, Node가 10년에 걸쳐 개선된 것처럼 Deno도 Node처럼 계속해서 발전하면서 성숙될 것입니다. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;당장 Deno를 사용해서 애플리케이션을 만들 수는 있지만 어떤 기능을 활용하느냐에 따라 한계가 있습니다. Deno를 빠르게 사용해보고 싶은 분들을 위해 지금 Deno가 갖고 있는 한계를 정확하게 안내해 드리겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;호환성&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno를 사용하게 되면 먼저 JavaScript 툴을 사용할 수 없다는 점이 당황스러울 수 있습니다. Deno는 대부분의 경우에 Node (NPM) 패키지와 호환되지 않습니다. Deno의 기본 레이어는 Node와 호환성을 맞추려고 하지만 아직 이 작업은 시간이 더 많이 필요합니다. &lt;a href=&quot;https://deno.land/std/node/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://deno.land/std/node/&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그리고 Deno는 모듈 시스템을 좀 더 간략화하고 싶었지만 이 부분은 Node와 지향하는 바가 비슷했기 때문에 인터페이스도 비슷합니다. 앞으로는 Deno가 좀 더 발전하면서 Node에서 벗어나기를 바랍니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;HTTP 서버 성능&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;a href=&quot;https://deno.land/benchmarks&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;우리는 계속해서 Deno HTTP 서버의 성능을 검토해 왔습니다.&lt;/a&gt; 현재 hello-world Deno HTTP 서버는 초당 25,000 요청을 처리할 수 있으며 최대 지연시간은 1.3ms 입니다. 같은 기능을 하는 Node 서버는 초당 34,000 요청을 처리할 수 있으며 최대 지연시간은 2~300ms입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno HTTP 서버는 네이티브 TCP 소켓을 활용하는 TypeScript 코드로 작성되었습니다. Node HTTP 서버는 C언어로 작성되었으며 JavaScript로 활용할 수 있도록 바인딩되었습니다. Deno에도 네이티브 HTTP 서버를 바인딩하는 방식을 사용할 수 있었지만, TCP 소켓 레이어를 좀 더 최적화하고 op 인터페이스를 일관되게 유지하기 위해 그렇게 하지는 않았습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;초당 25,000 요청을 비동기로 처리할 수 있다는 것만으로도 당장 활용하는 데에는 문제가 없습니다. 이정도가 문제라면 JavaScript를 사용하지 않는 것을 고려해야 합니다. 앞으로 Deno에서 Promise를 사용하는 코드가 개선된다면 좀 더 나은 성능을 낼 수 있을 것입니다. 다음 릴리즈를 기대해 주세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TSC 병목&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno는 내부적으로 Microsoft TypeScript 컴파일러를 활용해서 타입을 체크하고 javaScript 코드를 생성합니다. 그래서 이전에 Node가 그랬던 것처럼 V8이 JavaScript 코드를 파싱하는 것과 비교해보면 상당히 느립니다. &quot;V8 스냅샷&quot;을 활용하면서 이 성능은 눈에 띄게 개선되었지만 아직 충분하지 않습니다. 물론 TypeScript 컴파일러도 계속 개선될 것이라 생각합니다. 하지만 우리는 결국 Rust로 타입 체크 로직을 구현해야겠다는 결론을 냈습니다. 물론 이 작업이 금방 끝나진 않을 것입니다. 하지만 작업이 끝난 후엔 비교할 수 없는 성능 차이가 날 것이고 결국 개발자가 체감하는 것은 이 포인트이기 때문에 이 방향으로 진행하려고 합니다. TSC는 반드시 Rust로 포팅되어야 합니다. 이 문제를 함께 해결하려는 분이 있다면 연락 부탁드립니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;플러그인 / 확장&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Deno는 런타임 환경을 확장할 수 있는 플러그인 시스템을 갖추고 있습니다. 하지만 이 시스템은 아직 개발 중이며 안정된 상태라고 하기 어렵습니다. Deno가 제공하는 기능 외에는 네이티브 시스템에 접근하기 어려울 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;감사한 분들&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;이번 릴리즈가 나올 수 있게 도와주신 &lt;a href=&quot;https://github.com/denoland/deno/graphs/contributors&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;컨트리뷰터들&lt;/a&gt;에게 감사드립니다. 특히 &lt;a href=&quot;https://github.com/kitsonk&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@kitsonk&lt;/a&gt;는 TypeScript 컴파일러 호스트, deno_typescript, deno bundle, deno install, deno types, 스트림 구현 등 시스템 각 영역에서 큰 공헌을 해주셨습니다. 그리고 &lt;a href=&quot;https://github.com/kevinkassimo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@kevinkassimo&lt;/a&gt;는 이 프로젝트가 진행되는 내내 수많은 버그를 해결해 주셨습니다. 타이머 시스템, TTY 통합, wasm, Deno.makeTempFile, Deno.kill, Deno.hostname, Deno.realPath, std/node 연결부, window.queueMicrotask, REPL 등 여러 분야에 걸쳐 도움을 주셨으며 특히 로고도 만들어 주셨죠! &lt;a href=&quot;https://github.com/kt3k&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@kt3k&lt;/a&gt;는 우리가 계속 사용하고 있는 벤치마크 시스템, 시그널 핸들러, 권한 API를 만드는데 도움을 주셨으며 수많은 크리티컬 버그를 수정해 주셨습니다. &lt;a href=&quot;https://github.com/nayeemrmn&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@nayeemrmn&lt;/a&gt;도 Deno의 각 부분에서 발생하는 버그를 수정하는 데에 도움을 주셨습니다. 특히 @nayeemrmn이 스택 트레이스와 에러 리포팅하는 코드를 훌륭하게 개선해주셨기 때문에 오늘 API가 1.0으로 안정될 수 있었습니다. &lt;a href=&quot;https://github.com/justjavac&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@justjavac&lt;/a&gt;은 웹 표준과 deno API가 연결되는 부분에서 발생하는 크고 작은 버그들을 해결해 주셨으며 VS Code deno 플러그인을 구현해 주셨습니다. &lt;a href=&quot;https://github.com/zekth&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@zekth&lt;/a&gt;는 std/encoding/csv, std/encoding/toml, std/http/cookies와 같이 std와 관련된 모듈을 개발하는 데에 도움을 주셨으며 버그도 많이 해결해 주셨습니다. &lt;a href=&quot;https://github.com/axetroy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@axetroy&lt;/a&gt;는 코드 가독성 향상에 도움을 주셨으며 버그 수정도 도와주셨습니다. VS Code 플러그인을 관리하는 분도 이분입니다. &lt;a href=&quot;https://github.com/afinch7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@afinch7&lt;/a&gt;는 플러그인 시스템 개발에 도움을 주셨습니다. &lt;a href=&quot;https://github.com/keroxp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@keroxp&lt;/a&gt;는 웹소켓 서버 구현에 도움을 주셨으며 이 분이 고친 버그도 아주 많습니다. &lt;a href=&quot;https://github.com/cknight&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@cknight&lt;/a&gt;는 수많은 문서작업에 도움을 주셨고 std/node 폴리필 구현도 도와주셨습니다. &lt;a href=&quot;https://github.com/lucacasonato&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@lucacasonata&lt;/a&gt;는 거의 혼자서 deno.land 웹사이트를 만들어 주셨습니다. &lt;a href=&quot;https://github.com/hashrock&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@hashrock&lt;/a&gt;은 훌륭한 아트워크 작업을 해주셨습니다. doc.deno.land에 있는 로딩 페이지와 이 문서 제일 위쪽에 있는 이미지도 이 분이 작업하셨습니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;One last thing&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Deno&amp;nbsp;v1.0&amp;nbsp;후드티를&amp;nbsp;주문하시면&amp;nbsp;이&amp;nbsp;오픈&amp;nbsp;소스&amp;nbsp;개발에&amp;nbsp;큰&amp;nbsp;도움을&amp;nbsp;주실&amp;nbsp;수&amp;nbsp;있습니다:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;v1_hoodie_mock.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;916&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cl9LX7/btqEbRjZMAP/cAVU2meB2FMFSpO7iZQ1fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cl9LX7/btqEbRjZMAP/cAVU2meB2FMFSpO7iZQ1fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cl9LX7/btqEbRjZMAP/cAVU2meB2FMFSpO7iZQ1fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcl9LX7%2FbtqEbRjZMAP%2FcAVU2meB2FMFSpO7iZQ1fk%2Fimg.png&quot; data-filename=&quot;v1_hoodie_mock.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;916&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://deno.land/v1/hoodie&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;구매 링크&lt;/a&gt;&lt;/p&gt;</description>
      <category>DENO</category>
      <author>한장현</author>
      <guid isPermaLink="true">https://han41858.tistory.com/50</guid>
      <comments>https://han41858.tistory.com/50#entry50comment</comments>
      <pubDate>Fri, 15 May 2020 17:37:28 +0900</pubDate>
    </item>
    <item>
      <title>Angular v.9.0.0 릴리즈 노트</title>
      <link>https://han41858.tistory.com/49</link>
      <description>&lt;p&gt;안녕하세요. 한장현입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;꽤 오래 걸렸네요.&lt;/p&gt;
&lt;p&gt;Angular는 원래 6개월 주기로 메이저 버전을 업데이트하는 정책을 유지했지만 이번 메이저 업데이트는 2019년 10~11월로 예정된 것에 비해 2개월가량 늦어졌습니다. 프론트엔드 프레임워크가 템플릿 엔진을 다른 것으로 바꾼다는 것이 얼마나 힘든 일인지,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/angular/angular/blob/master/CHANGELOG.md&quot;&gt;v.9.0.0 버전 CHANGELOG.md 파일&lt;/a&gt;을 봐도 Angular 팀이 굉장히 고생했던 것 같네요.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그만큼 Ivy를 포함해서 큰 변동사항이 있었던 것으로 보입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;어떤 부분이 얼마나 바뀌었는지, 도움이 될만한 기능이 있는지 Angular 블로그의 9.0.0 릴리즈 노트를 살펴봤습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr style=&quot;border: none; font-size: 0px; line-height: 0; height: 20px; margin: 20px auto; background: url('https://t1.daumcdn.net/keditor/dist/0.4.0/image/divider-line.svg') 0px -180px / 200px 200px no-repeat #ffffff; cursor: pointer !important; width: 200px; color: #666666; font-family: 'Noto Sans', sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Angular 9 버전이 배포되었습니다 - Ivy 프로젝트 정식 도입&lt;/h2&gt;
&lt;p&gt;드디어 Angular 9.0.0 버전이 배포되었습니다! 이번 메이저 릴리즈는 플랫폼과 프레임워크는 물론이고 Angular Material, CLI에도 영향을 미치는 릴리즈입니다. 이번 릴리즈부터 Angular 애플리케이션은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://angular.io/guide/ivy&quot;&gt;Ivy&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;컴파일러를 빌드 타임과 실행 타임에 기본으로 사용하며, 컴포넌트를 테스트하는 방식도 개선되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZlUU8/btqBOCoNrlE/3jO9blgIE4xhrnkPNuyO00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZlUU8/btqBOCoNrlE/3jO9blgIE4xhrnkPNuyO00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZlUU8/btqBOCoNrlE/3jO9blgIE4xhrnkPNuyO00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZlUU8%2FbtqBOCoNrlE%2F3jO9blgIE4xhrnkPNuyO00%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이번 업데이트는 Angular가 개발되는 3년동안 있었던 것 중에 가장 큰 규모의 업데이트입니다. 이 업데이트를 통해 개발자들이 더 나은 애플리케이션을 개발할 수 있기를 바라며, Angular 생태계도 더 발전할 수 있기를 바랍니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Angular 9 버전으로 업데이트하는 방법&lt;/h3&gt;
&lt;p&gt;버전업 방법에 대해 자세하게 알아보려면 역시 &lt;a href=&quot;https://update.angular.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;update.angular.io&lt;/a&gt;를 활용하는 것이 좋습니다. 그리고 업데이트를 수월하게 하려면 마지막 메이저 버전인 Angular 8에서 업데이트하는 것을 권장합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;먼저, 애플리케이션을 Angular 8버전으로 업데이트합니다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;ng update @angular/cli@8 @angular/core@8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 9버전으로 업데이트합니다&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;ng update @angular/cli @angular/core&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Angular 9버전으로 업데이트하면서 중요하게 바뀌는 부분이나 지원이 중단된 API를 확인하려면 Angular 문서의 &lt;a href=&quot;https://v9.angular.io/guide/updating-to-version-9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Angular 9버전으로 업데이트하기 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Ivy&lt;/h3&gt;
&lt;p&gt;Angular 9버전부터는 &lt;a href=&quot;https://v9.angular.io/guide/ivy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Ivy 컴파일러와 런타임 엔진&lt;/a&gt;을 기본으로 사용하며, Ivy를 도입하기 위해 수백건의 버그 픽스가 있었습니다. 그 결과로 이제 다음과 같이 개선되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;번들 결과물의 크기가 작아졌습니다.&lt;/li&gt;
&lt;li&gt;테스트 실행 속도가 개선되었습니다.&lt;/li&gt;
&lt;li&gt;디버깅이 편해졌습니다.&lt;/li&gt;
&lt;li&gt;CSS 클래스와 스타일 바인딩이 개선되었습니다.&lt;/li&gt;
&lt;li&gt;테입 체킹 로직이 개선되었습니다.&lt;/li&gt;
&lt;li&gt;빌드 에러를 쉽게 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;빌드 시간이 줄었으며 AOT 빌드 옵션이 기본값으로 변경되었습니다.&lt;/li&gt;
&lt;li&gt;다국어 처리가 편해졌습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이제 이 내용들에 대해 자세하게 알아봅시다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;더 작아진 번들 결과물 크기&lt;/h3&gt;
&lt;p&gt;Ivy 컴파일러는 원래 &lt;a href=&quot;https://webpack.js.org/guides/tree-shaking/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;트리 셰이킹&lt;/a&gt;과 관계없는 Angular 코드 부분을 제거하기 위해 설계되었으며, 개별 Angular 컴포넌트가 빌드된 코드를 작게 줄이는 것이 목표였습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 Ivy가 정식 도입된 이번 업데이트부터 Angular 애플리케이션의 크기가 클수록 획기적인 용량 변화를 확인할 수 있을 것입니다.&lt;/p&gt;
&lt;p&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;Angular 구성요소를 조금만 사용하는 작은 Angular 애플리케이션은 Angular 쪽의 트리 셰이킹만로도 용량이 많이 줄어듭니다.&lt;/li&gt;
&lt;li&gt;컴포넌트의 개수가 많고 애플리케이션의 규모가 크다면 팩토리 코드의 용량이 줄어든 혜택을 크게 받을 수 있습니다.&lt;/li&gt;
&lt;li&gt;중간 정도 크기의 애플리케이션도 빌드 결과물의 크기가 약간 줄어드는 것을 확인할 수 있습니다. 다만, 이 경우에는 Angular의 트리 셰이킹의 영향이 크지 않고 팩토리 코드가 줄어든 영향을 제대로 느끼기에는 컴포넌트의 개수가 조금 적은 편입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz0zXW/btqBM7pGs7o/pyTykSsWZO339E6tZskj81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz0zXW/btqBM7pGs7o/pyTykSsWZO339E6tZskj81/img.png&quot; data-alt=&quot;작은 규모의 앱은 30% 정도 번들 결과물 크기가 줄어들고, 큰 규모의 앱은 25~40% 정도 줄어듭니다. 중간 규모의 앱도 소폭 감소합니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz0zXW/btqBM7pGs7o/pyTykSsWZO339E6tZskj81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz0zXW%2FbtqBM7pGs7o%2FpyTykSsWZO339E6tZskj81%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;작은 규모의 앱은 30% 정도 번들 결과물 크기가 줄어들고, 큰 규모의 앱은 25~40% 정도 줄어듭니다. 중간 규모의 앱도 소폭 감소합니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트 속도 개선&lt;/h3&gt;
&lt;p&gt;Ivy 환경에서 실행되는 &lt;a href=&quot;https://angular.io/api/core/testing/TestBed&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;TestBed&lt;/code&gt;&lt;/a&gt; 객체의 구현방식이 좀 더 효율적으로 개선되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이전까지 &lt;code&gt;TestBed&lt;/code&gt;는 각각의 테스트가 실행될 때마다 모든 컴포넌트를 다시 컴파일했습니다. 이 컴포넌트의 내용이 변경되지 않았어도 그랬습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 Ivy 환경에서는 컴포넌트가 수동으로 오버라이드 되지 않는 한 컴포넌트를 다시 컴파일하지 않습니다. 테스트할 때마다 이 과정을 생략하는 것만으로도 테스트 속도는 크게 개선되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그 결과 테스트 실행 속도는 40% 정도 빨라졌습니다. 개발자들이 각자 애플리케이션으로 직접 확인해봐도 테스트 시간이 40~50% 정도 줄어든 것을 확인할 수 있을 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디버깅 방식 개선&lt;/h3&gt;
&lt;p&gt;Ivy는 디버깅 툴을 좀 더 다양하게 제공합니다. Ivy가 개발자 모드에서 실행되고 있다면 이제부터 Angular 앱을 디버깅하기 위해 &lt;a href=&quot;https://v9.angular.io/api/core/global&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;ng&lt;/code&gt; 객체&lt;/a&gt;를 활용할 수 있습니다.&lt;/p&gt;
&lt;p&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;개발자 도구에서 컴포넌트, 디렉티브 등과 같은 Angular 인스턴스에 접근할 수 있습니다.&lt;/li&gt;
&lt;li&gt;이 인스턴스에 있는 메소드를 직접 실행하거나 상태를 변경할 수 있습니다.&lt;/li&gt;
&lt;li&gt;변화 감지 로직이 제대로 도는지 확인하기 위해 &lt;code&gt;applyChanges&lt;/code&gt; 함수를 수동으로 실행할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nTyrF/btqBNL7B949/SMZOuNPQxW7hvQVdvumeA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nTyrF/btqBNL7B949/SMZOuNPQxW7hvQVdvumeA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nTyrF/btqBNL7B949/SMZOuNPQxW7hvQVdvumeA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnTyrF%2FbtqBNL7B949%2FSMZOuNPQxW7hvQVdvumeA0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그리고 Ivy는 함수 실행 스택도 좀 더 보기 편하게 개선되었습니다. &lt;code&gt;ExpressionChangedAfterItHasBeenCheckedError&lt;/code&gt;를 예로 들면, 이전에 이렇게 표시되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QSNBD/btqBM6RVOqw/uqKOG0COMwkSkjoHKyk9C0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QSNBD/btqBM6RVOqw/uqKOG0COMwkSkjoHKyk9C0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QSNBD/btqBM6RVOqw/uqKOG0COMwkSkjoHKyk9C0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQSNBD%2FbtqBM6RVOqw%2FuqKOG0COMwkSkjoHKyk9C0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제부터는 이 함수 실행 스택이 좀 더 유용한 단위로 표시되기 때문에 수정이 필요한 부분으로 바로 이동할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y8rvZ/btqBNjKkaY4/GalgbsiJzAUZ44srGJ8yIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y8rvZ/btqBNjKkaY4/GalgbsiJzAUZ44srGJ8yIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y8rvZ/btqBNjKkaY4/GalgbsiJzAUZ44srGJ8yIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy8rvZ%2FbtqBNjKkaY4%2FGalgbsiJzAUZ44srGJ8yIk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 함수 실행 스택에서 &lt;code&gt;AppComponent_Template&lt;/code&gt; 부분을 클릭하면 Angular가 생성한 코드의 어느 부분에서 에러가 발생했는지 바로 이동해서 확인할 수 있습니다:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmLcbx/btqBNMMbior/MK8zpgHmgXiOiDk3tkSI81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmLcbx/btqBNMMbior/MK8zpgHmgXiOiDk3tkSI81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmLcbx/btqBNMMbior/MK8zpgHmgXiOiDk3tkSI81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmLcbx%2FbtqBNMMbior%2FMK8zpgHmgXiOiDk3tkSI81%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그리고 이 부분이 문제인 것이 맞다면, Angular 사용방법이 잘못되지 않았는지, 컴포넌트의 구현 방식은 제대로 되었는지 이어서 확인할 수 있을 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CSS 클래스와 스타일 바인딩 방식 개선&lt;/h3&gt;
&lt;p&gt;Ivy 컴파일러와 실행환경은 스타일을 다루는 방식도 개선되었습니다. 이전까지는 서로 충돌하는 스타일이 존재할 때 이 스타일들이 작게 쪼개져서 해당 스타일을 대체하는 방식으로 동작했습니다. 이제 Ivy에서는 이 스타일들이 좀 더 예측가능한 범위로 병합됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음과 같은 컴포넌트가 있다고 합시다:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/StephenFluin/3a928ef5eebc56fa57d3d5132cb25c2b.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/StephenFluin/0fa8be53c3c1aa7d8e2a39b463046579.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이전까지는 유효하다고 평가되는(win) 스타일만 바인딩되었고, 표현식이 실행되는 시점에도 영향을 받았습니다. 이 코드에서는 &lt;code&gt;myColor&lt;/code&gt;와 &lt;code&gt;myOtherColor&lt;/code&gt;가 &lt;code&gt;undefined&lt;/code&gt;라고 해도 정적으로 선언된 'red' 색상은 무시됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 9버전부터는 더이상 타이밍에 영향을 받지 않으며, 좀 더 명확하고 일관된 순서로 스타일이 지정됩니다. 이제는 가장 구체적으로 지정된 스타일이 항상 우선 적용됩니다. 그래서 &lt;code&gt;[style.color]&lt;/code&gt;는 언제나 &lt;code&gt;[style]&lt;/code&gt; 보다 우선 순위로 바인딩됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다만, 하위호환성을 보장하기 위해 &lt;code&gt;[ngStyle]&lt;/code&gt;과 &lt;code&gt;[ngClass]&lt;/code&gt;가 바인딩되는 과정은 이전과 동일하게 유지했습니다. 이 두 방식은 이전에 존재하는 스타일과 충돌하더라도 새롭게 지정하는 값으로 바인딩됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;스타일 우선순위에 대해 자세히 알아보려면 &lt;a href=&quot;https://v9.angular.io/guide/template-syntax#styling-precedence&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;템플릿 문법 가이드 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 굳이 의도하지는 않았지만 이런 방식으로 개선되면서 이제 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CSS 커스텀 프로퍼티(CSS 변수)&lt;/a&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/StephenFluin/2980ff04af6ed5caefd854e389361e62.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;타입 체크 방식 강화&lt;/h3&gt;
&lt;p&gt;Angular 컴파일러는 TypeScript보다 좀 더 깐깐하게 타입을 체크합니다. 이것은 개발자들이 애플리케이션을 개발단계에서부터 빠르게 버그를 발견하고 처리할 수 있도록 하기 위한 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이번 릴리즈부터 타입 체크와 관련된 플래그가 2개 더 추가됩니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;fullTemplateTypeCheck&lt;/code&gt; - 이 옵션을 활성화하면 컴파일러가 템플릿에 있는 모든 항목을 검사합니다. (ngIf, ngFor, ng-template 등)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strictTemplates&lt;/code&gt; - 이 옵션을 활성화하면 가장 깐깐한 타입 체크 룰을 적용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;템플릿 타입 체크 옵션에 대해 자세하게 알아보려면 &lt;a href=&quot;https://v9.angular.io/guide/template-typecheck&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;템플릿 타입 체크 가이드 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;빌드 에러 개선&lt;/h3&gt;
&lt;p&gt;Ivy 컴파일러는 개발자가 좀 더 쉽게 이해할 수 있는 빌드 에러 메시지를 제공합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;View Engine을 사용하는 Angular 8 버전에서는 컴파일 에러가 이런 식으로 표시되었습니다:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BWe3L/btqBMNLPGNu/5UMklNsD5ZIyVNaYmhtic1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BWe3L/btqBMNLPGNu/5UMklNsD5ZIyVNaYmhtic1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BWe3L/btqBMNLPGNu/5UMklNsD5ZIyVNaYmhtic1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBWe3L%2FbtqBMNLPGNu%2F5UMklNsD5ZIyVNaYmhtic1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Ivy를 사용하는 Angular 9 버전에서는 같은 에러가 이렇게 표시됩니다:&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6oydP/btqBNLNjbcJ/7MkzRjcoFLU5iPMOgKknxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6oydP/btqBNLNjbcJ/7MkzRjcoFLU5iPMOgKknxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6oydP/btqBNLNjbcJ/7MkzRjcoFLU5iPMOgKknxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6oydP%2FbtqBNLNjbcJ%2F7MkzRjcoFLU5iPMOgKknxK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;빌드 속도 개선, AOT 컴파일러가 기본으로 변경됨&lt;/h3&gt;
&lt;p&gt;Ivy를 도입하면서 컴파일 성능도 크게 개선되었습니다.&lt;/p&gt;
&lt;p&gt;이 때 이야기하는 컴파일 성능이란, TypeScript 컴파일러와 비교할때 Angular 컴파일러의 부하가 얼마나 큰지를 의미합니다. Angular 가이드 문서 앱(angular.io)로 컴파일 성능 개선을 확인해보니 이 부하는 0.8배 에서 0.5배로 줄었으며, 40% 정도 개선되었다고 판단됩니다.&lt;/p&gt;
&lt;p&gt;이 결과는 &lt;a href=&quot;https://angular.io/guide/aot-compiler&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AOT 빌드&lt;/a&gt; 속도도 굉장히 빨라졌다는 것을 의미하기 때문에 이제부터는 개발 단계에서 빌드할 때도 AOT 빌드를 제대로 활용할 수 있습니다. 이제 `&lt;code&gt;ng serve&lt;/code&gt;` 로 빌드해도 운영용으로 빌드할 때와 같은 시간이 걸립니다. Angular 개발 환경이 좀 더 개선되었기를 바랍니다.&lt;/p&gt;
&lt;p&gt;그리고 이 과정을 거치면서 이제 &lt;a href=&quot;https://v9.angular.io/guide/deprecations#entryComponents&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;entryComponents&lt;/code&gt;&lt;/a&gt;를 지정하지 않아도 됩니다. 이런 유형의 컴포넌트는 이제 Angular가 자동으로 인식해서 필요할 때만 컴파일됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다국어 지원&lt;span style=&quot;color: #333333;&quot;&gt;(i18n)&lt;/span&gt; 개선&lt;/h3&gt;
&lt;p&gt;다국어 지원 기능은 Angular가 제공하는 기능 중에서도 가장 중요한 것 중 하나로 꼽힙니다. 이 기능을 활용하면 개별 다국어를 효율적으로 처리할 수 있으며 각 문구에 따라 애플리케이션도 타이트하게 최적화할 수 있습니다. Angular 9버전부터는 다국어 추출 단계가 빌드 이후 시점로 옮겨졌고 10배 정도 더 빠르게 개선되었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;@angular/localize&lt;/code&gt;와 &lt;code&gt;angular.json&lt;/code&gt; 설정에 대해 자세하게 알아보려면 &lt;a href=&quot;https://v9.angular.io/guide/i18n&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기타 개선사항&lt;/h3&gt;
&lt;p&gt;Angular 팀은 이 외에도 Angular 전반의 사용성을 개선하기 위해 열심히 노력하고 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ng update 개선&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ng update&lt;/code&gt;가 동작하는 방식이 안정되었으며, 좀 더 많은 정보를 제공하도록 개선되었습니다.&lt;/p&gt;
&lt;p&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;항상 최신 Angular CLI로 실행합니다. Angular 8.3.19부터는 패키지 버전을 업데이트할 때 업데이트하는 버전의 Angular CLI를 사용합니다.&lt;/li&gt;
&lt;li&gt;진행상황이 표시되는 방식이 개선되었습니다. 이제는 &lt;code&gt;ng update&lt;/code&gt;가 실행되는 동안 어떤 처리를 하고 있는지 좀 더 많은 정보를 제공합니다.&lt;/li&gt;
&lt;li&gt;업데이트 디버깅이 편해졌습니다. 지금까지는 ng update 실행이 종료되었을 때 최종 결과만 간단하게 표시했습니다. Angular 9 버전은 이제 &lt;code&gt;--create-commits&lt;/code&gt; 플래그를 제공합니다. &lt;code&gt;ng update --create-commits&lt;/code&gt; 명령을 실행하면 이제 각 마이그레이션 단계가 끝날때마다 어떻게 영향을 주었는지 표시합니다. 이 내용을 확인하면 마이그레이션 과정이 어떻게 진행되는지 알 수 있으며, 문제가 생겼을 때 처리하기도 편해질 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;'&lt;code&gt;providedIn&lt;/code&gt;' 옵션 추가&lt;/h3&gt;
&lt;p&gt;Angular에서 &lt;code&gt;@Injectable&lt;/code&gt; 서비스를 만들면 이 서비스가 어떤 인젝터에 등록되어야 할지 반드시 선택해야 합니다. 이전까지는 모듈과 `root`를 선택할 수 있었지만 이제 두 옵션이 추가되었습니다.&lt;/p&gt;
&lt;p&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;code&gt;platform&lt;/code&gt; - providedIn: 'platform'과 같이 지정하면 서비스가 플랫폼 인젝터에 등록됩니다. 플랫폼 인젝터는 단 하나만 존재하기 때문에 화면에 존재하는 모든 애플리케이션에서 이 서비스를 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;any&lt;/code&gt; - 서비스를 모든 모듈(지연로딩되는 모듈 포함)에 등록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;providedIn에 대해 자세하게 알아보려면 &lt;a href=&quot;https://v9.angular.io/api/core/Injectable&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;컴포넌트 하네스(harnesses, 보조기구)&lt;/h3&gt;
&lt;p&gt;컴포넌트를 테스트하려면 원하는 컴포넌트를 찾거나 이벤트를 발생시키기 위해 CSS 셀렉터와 같은 기법을 활용했습니다. 그래서 컴포넌트 라이브러리가 변경되면 이 컴포넌트를 사용하는 코드도 수정해야 했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Angular 9는 이 과정을 개선하기 위해 컴포넌트 하네스를 도입했습니다. 이제는 좀 더 추상적으로 컴포넌트에 접근하기 때문에 컴포넌트의 필요한 부분만 안정적으로 테스트할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;실제로 Angular Material의 컴포넌트들은 이 방식으로 테스트하고 있습니다. &lt;a href=&quot;https://material.angular.io/cdk/categories&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Component Dev Kit(CDK)&lt;/a&gt;에서도 이 내용을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이전까지는 이렇게 테스트했습니다:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/StephenFluin/af5443f01d0f67bae39044481a036442.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 하네스를 사용하면 이렇게 테스트할 수 있습니다:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/StephenFluin/de96c9f1869b497c8f7a373ab8f12280.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://v9.material.angular.io/guide/using-component-harnesses&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Angular Material 컴포넌트의 하네스&lt;/a&gt;나 &lt;a href=&quot;https://v9.material.angular.io/cdk/testing/overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;커스텀 컴포넌트 만들기&lt;/a&gt; 문서도 참고해 보세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;새로운 컴포넌트&lt;/h3&gt;
&lt;p&gt;이제는 YouTube나 Google Maps를 애플리케이션에 직접 내장할 수 있습니다.&lt;/p&gt;
&lt;p&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;a href=&quot;https://github.com/angular/components/tree/master/src/youtube-player&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;youtube-player&lt;/a&gt;를 사용하면 YouTube 플레이어를 애플리케이션에 인라인으로 렌더링할 수 있습니다. 이전처럼 IFrame 플레이어 API를 사용하는 것보다 훨씬 나을 것입니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/angular/components/tree/master/src/google-maps&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;google-maps&lt;/a&gt; 컴포넌트도 활용할 수 있습니다. 이 컴포넌트는 일반 Angular 컴포넌트와 거의 비슷하게 동작하기 때문에 기존 &lt;a href=&quot;https://developers.google.com/maps/documentation/javascript/tutorial&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Google Maps API&lt;/a&gt;를 활용하는 것과 비교했을 때 지도를 렌더링한다던지, 마커를 표시하는 것과 같은 작업을 좀 더 편하게 할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IDE &amp;amp; 언어 지원 서비스 개선&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbKqX1/btqBM8besAl/YVzxGJHzjlVj7CKGKcZmnK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbKqX1/btqBM8besAl/YVzxGJHzjlVj7CKGKcZmnK/img.gif&quot; data-alt=&quot;Go to definition과 개선된 기능 데모&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbKqX1/btqBM8besAl/YVzxGJHzjlVj7CKGKcZmnK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cbKqX1/btqBM8besAl/YVzxGJHzjlVj7CKGKcZmnK/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Go to definition과 개선된 기능 데모&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Visual Studio Marketplace로 제공하는 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Angular.ng-template&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Angular 언어 지원 서비스 확장기능&lt;/a&gt;이 대폭 개선되었습니다. 새 버전에서는 객체를 참조하는 성능과 안정성이 개선되었고, 처리가 지연된 버그도 다수 해결되었습니다. 그리고 이런 기능도 추가되었습니다:&lt;/p&gt;
&lt;p&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;TextMate 문법 지원기능이 추가되었습니다. 이제 인라인 템플릿과 외부 파일 템플릿에 문법 하이라이트 기능이 제대로 동작합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;templateUrl&lt;/code&gt;과 &lt;code&gt;styleUrls&lt;/code&gt;에 &quot;Go to definition&quot; 기능을 활용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;툴팁으로 NgModule과 타입 정보가 표시됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TypeScript 3.7 지원&lt;/h3&gt;
&lt;p&gt;Angular는 현재 TypeScript 3.6과 3.7 버전에 동작하도록 만들어졌습니다. TypeScript 3.7에 도입된 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;선택 체이닝(optional chaining)&lt;/a&gt; 기능이 워낙 강력하기 때문에 3.7도 포함되었으며, 생태계의 호환성을 유지하기 위해 Zone.JS나 RxJS과 같은 패키지 버전도 일부 올라갔습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커뮤니티 분들에게 감사드립니다.&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이번 릴리즈는 2년간 모두가 노력했던 결실입니다. 이 릴리즈를 통해 만들어낼 미래와 가능성을 생각하면 흥분하지 않을 수 없습니다. 그리고 이 결실은 커뮤니티의 수많은 분들이 있었기 때문에 이룰 수 있었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(컨트리뷰터 명단 생략)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;언급되지 않은 커뮤니티 분들과 GDE 분들에게도 물론 감사드립니다. Angular 팀은 모든 분들이 전해준 피드백과 이슈 리포트, 문제 재현을 활용하며 최선의 결과물을 만들기 위해 노력했습니다. 이 과정에서 4000개가 넘는 Angular 9 버전의 앱을 만들기도 했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/pkozlowski-opensource&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Pawel Kozlowski&lt;/a&gt;와 그의 스폰서 &lt;a href=&quot;https://amadeus.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Amadeus&lt;/a&gt;에 대해서도 감사드립니다. Pawel은 2년 넘게 Ivy 프로젝트에 참여하면서 프로젝트가 성공에 이르기까지 아주 중요한 역할을 했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;릴리즈에 대한 피드백은 &lt;a href=&quot;https://github.com/angular/angular/issues/new/choose&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;github&lt;/a&gt;이나 &lt;a href=&quot;https://twitter.com/angular&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;twitter&lt;/a&gt;로 부탁드립니다!&lt;/p&gt;</description>
      <category>Angular</category>
      <author>한장현</author>
      <guid isPermaLink="true">https://han41858.tistory.com/49</guid>
      <comments>https://han41858.tistory.com/49#entry49comment</comments>
      <pubDate>Fri, 7 Feb 2020 14:43:31 +0900</pubDate>
    </item>
    <item>
      <title>angular.kr 사이트를 오픈합니다.</title>
      <link>https://han41858.tistory.com/48</link>
      <description>&lt;p&gt;안녕하세요. 한장현입니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;회사를 다녀서 그렇기도 하지만 블로그에 거의 글을 쓰지 않았습니다.&lt;/p&gt;&lt;p&gt;작년에는 Angular 메이저 버전 업데이트과 관련된 글 2개가 전부였네요 ㅎ&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;그동안 이런걸 만들고 있었습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 820px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99E71E4C5C37489B0E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99E71E4C5C37489B0E&quot; width=&quot;820&quot; height=&quot;776&quot; filename=&quot;main.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;이 사이트는 &lt;a href=&quot;https://angular.io/&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;Angular 공식 가이드 문서&lt;/a&gt;를 생성하는&amp;nbsp;&lt;a href=&quot;https://github.com/angular/angular/tree/master/aio&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;angular.io 프로젝트&lt;/a&gt;를 번역해서 만든 웹사이트입니다.&lt;/p&gt;&lt;p&gt;angular.io가 워낙 잘 만들어져 있어서&amp;nbsp;사이트를 다루는 데에 큰 문제는 없었습니다.&lt;/p&gt;&lt;p&gt;(버그가 있어서 이슈로 올렸더니 금방 고쳐줬어요.)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;저는 별거 안했는데 PWA도 잘 되더라구요.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 820px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99DDA8465C3748FD03&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99DDA8465C3748FD03&quot; width=&quot;820&quot; height=&quot;525&quot; filename=&quot;pwa.png&quot; filemime=&quot;image/jpeg&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;번역 작업을 언제부터 시작했는지는 기억이 잘 안나네요.&lt;/p&gt;&lt;p&gt;2017년 말부터 시작했던 것 같으니&amp;nbsp;1년 좀 넘게 작업하고 있는&amp;nbsp;것 같습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;혼자서 공부하려고 시작했는데 내용이 너무 방대해서 힘들긴 합니다.&lt;/p&gt;&lt;p&gt;웹 사이트로 빌드된 용량이 45MB나 될 줄 몰랐어요.&lt;/p&gt;&lt;p&gt;그래도 공식 가이드 문서의 문장이나 예제 코드를 하나하나 꼼꼼히 볼 수 있는 좋은 기회라고 생각하고 있습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;작업하던 중에 angular.kr 도메인이 풀렸길래 낼름 집어왔는데, 제 용도에 맞게 사용할 수 있어서 다행이네요 ㅎ&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;원래 계획으로는 모든 내용을 번역한 이후에 사이트를 오픈하려고 했습니다.&lt;/p&gt;&lt;p&gt;그런데 Google Angular 코어 팀이 일을 너무 열심히 하더라구요.&lt;/p&gt;&lt;p&gt;최신 코드로 머지하고 한달쯤 번역하다 보면 커밋이 500개쯤 쌓여있어요.&lt;/p&gt;&lt;p&gt;다 끝내고 오픈하면 더 좋겠지만, 지금까지 작업한 내용이라도 Angular를 사용하는 분들께 도움이 되는 것이 낫겠다 생각해서 사이트를 오픈하기로 했습니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;번역은 80% 정도 끝난 것 같습니다.&lt;/p&gt;&lt;p&gt;사이트를 오픈하고 나서도 틈틈이 번역 안된 문서들 번역하고, 새 버전 나오면 되도록 빠르게 업데이트 하려고 합니다.&lt;/p&gt;&lt;p&gt;다 하고 나면 또 새로운 커밋이 있겠죠. 하하하.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;번역이 잘못되었거나 이상한 내용이 있으면&amp;nbsp;&lt;a href=&quot;https://github.com/han41858/angular-ko/issues&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;이슈&amp;nbsp;게시판&lt;/a&gt;에 올려 주시면 됩니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Angular를 사용하시는 분들에게&amp;nbsp;미약하게나마 도움이 되기를 바랍니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;감사합니다.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;ps. 초반에 함께 했지만 육아에 전념하느라 지금은 함께 하지 못하는&amp;nbsp;이영빈님 아쉽네요...&lt;/p&gt;&lt;p&gt;ps. 묵묵히 지켜봐 주시는 루비페이퍼 한창훈 대표님께 감사드립니다. 하기로 한 건 잊지 않고 진행하겠습니다 ㅎ&lt;/p&gt;&lt;p&gt;ps. GitHub Page 설정에 막혔을 때 해결방법을 찾아&amp;nbsp;주신 정문창(Murry Jeong)님께 감사드립니다.&lt;/p&gt;</description>
      <category>Angular</category>
      <author>한장현</author>
      <guid isPermaLink="true">https://han41858.tistory.com/48</guid>
      <comments>https://han41858.tistory.com/48#entry48comment</comments>
      <pubDate>Thu, 10 Jan 2019 23:09:32 +0900</pubDate>
    </item>
  </channel>
</rss>