<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Jaddong's Lab</title>
    <link>https://jaddong.tistory.com/</link>
    <description>Front-end web developer based in Seoul.</description>
    <language>ko</language>
    <pubDate>Sun, 28 Jun 2026 13:06:54 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>jaddong</managingEditor>
    <image>
      <title>Jaddong's Lab</title>
      <url>https://tistory1.daumcdn.net/tistory/3083815/attach/159bd91fbf074534ae9ae0413b2b67e8</url>
      <link>https://jaddong.tistory.com</link>
    </image>
    <item>
      <title>코드관리와 퍼포먼스 관점에서의 CSS-in-JS vs CSS in CSS</title>
      <link>https://jaddong.tistory.com/entry/%EC%BD%94%EB%93%9C%EA%B4%80%EB%A6%AC%EC%99%80-%ED%8D%BC%ED%8F%AC%EB%A8%BC%EC%8A%A4-%EA%B4%80%EC%A0%90%EC%97%90%EC%84%9C%EC%9D%98-CSS-in-JS-vs-CSS-in-CSS</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CSS-in-CSS&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드타임에서 모든 스타일을 생성되어 빌드의 결과물인 css파일의 클래스네임으로 html에 스타일링된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) bootstrap, tailwinds, sass, less&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;우리 컴포넌트에서 알아보기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미 빌드타임에 만들어져 있는 stylesheet로 스타일이 변경되는 걸 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순한 CSS 작성으로 러닝 커브가 낮다.&lt;/li&gt;
&lt;li&gt;styled-component나 emotion같은 라이브러리가 포함되어있지 않아 번들 사이즈가 작다.&lt;/li&gt;
&lt;li&gt;style과 관련하여 가독성을 해치지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1719988844729&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;span class={&quot;line-clamp-2 truncate break-all leading-normal&quot;}&amp;gt;{text}&amp;lt;/span&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 로딩에 거대한 stylesheet 파일들이 포함될 수 있다.&lt;/li&gt;
&lt;li&gt;중복 코드가 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List라는 컴포넌트가 아래와 같은 스타일이 필요했고, 이에 row라는 클래스네임을 지정했다. Item이라는 컴포넌트가 필요했고 이에 동일한 스타일이 필요해서 같은 클래스네임을 넣어줬다.&lt;/p&gt;
&lt;pre id=&quot;code_1719988964721&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.row {
  padding: 0.5rem;
  border: 1px solid #ddd;
}
 
&amp;lt;List className=&quot;row&quot;&amp;gt;&amp;lt;/List&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719988979953&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Item className=&quot;row&quot;&amp;gt;&amp;lt;/Item&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;몇 달 뒤 다른 동료가 코드를 보았을 때 row라는 스타일이 어디에서 왔는지 딱 코드만 봤을 때 알 수가 없다. row클래스네임을 vscode에서 검색해봐야하고, 비슷한 유형의 스타일을 작성할 때 중복되진 않은 지 확인해봐야한다.&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BEM으로 className을 잘 확장할 수 있지만 코드베이스에서 여러 사람이 작업할 때 일관성을 유지하는 것이 문제이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩된 CSS 규칙은 아래와 같이 유지 관리가 어려워진다.&lt;/p&gt;
&lt;pre id=&quot;code_1719989091290&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.row {
  padding: 0.5rem;
  border: 1px solid #ddd;
	 
	span{
		color: black;
	}
}
.tx-blue{
	color:blue
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719989121553&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Item className=&quot;row&quot;&amp;gt;
	&amp;lt;span className=&quot;tx-blue&quot;&amp;gt;text&amp;lt;span&amp;gt;
&amp;lt;/Item&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;또한 자바스크립트 변수와 이용해 동적 style을 구성할 수 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1719989202305&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ExampleDiv({ width, height }:{width: number; height: number;}) {
  return &amp;lt;div class=`bg-black	w-[${width}px] h-[${height}px]`&amp;gt;안녕하세요&amp;lt;/div&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1719989214042&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.w-{변수}px{
	width: {변수}px;
}

.h-{변수}px{
	height: {변수}px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CSS-in-JS&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 런타임에 global css가 작성되고 prop, state따라 runtime에서 javascript가 스타일을 동적으로 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) styled-components, emotion&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;우리 컴포넌트에서 알아보기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;prop에 따라 class가 바뀌고 그에 따른 stylesheet가 자동 생성되는 것을 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;성능 심층 분석&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a href=&quot;https://junghan92.medium.com/%EB%B2%88%EC%97%AD-%EC%9A%B0%EB%A6%AC%EA%B0%80-css-in-js%EC%99%80-%ED%97%A4%EC%96%B4%EC%A7%80%EB%8A%94-%EC%9D%B4%EC%9C%A0-a2e726d6ace6&quot;&gt;Spot : Emotion 에서 Sass로&lt;/a&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멤버 브라우저 화면은 20명의 유저를 보여줄 것이며,&lt;/li&gt;
&lt;li&gt;리스트 아이템을 감싸던&amp;nbsp;React.memo를 제거했습니다. 그리고,&lt;/li&gt;
&lt;li&gt;맨 위에 있는&amp;nbsp;&amp;lt;BrowseMembers&amp;gt;&amp;nbsp;컴포넌트가 1초마다 강제로 렌더링하도록 하고, 처음 10개의 렌더링 시간을 기록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (16).png&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DnCTQ/btsIlQm5NX5/hjLWf64UdDEH80mjQxAthK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DnCTQ/btsIlQm5NX5/hjLWf64UdDEH80mjQxAthK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DnCTQ/btsIlQm5NX5/hjLWf64UdDEH80mjQxAthK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDnCTQ%2FbtsIlQm5NX5%2FhjLWf64UdDEH80mjQxAthK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;179&quot; data-filename=&quot;Untitled (16).png&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;With Emotion : React DevTools를 사용해 페이지를 프로파일링 했고 처음 10번의 렌더링 시간 평균은&amp;nbsp;&lt;b&gt;54.3ms&lt;/b&gt; 였습니다.&lt;/li&gt;
&lt;li&gt;Without Emotion : 위에서 설명한 것과 동일한 테스트를 반복했고 처음 10개 렌더링의 평균으로 &lt;b&gt;27.7ms&lt;/b&gt;를 얻었습니다. 이는 원래 시간보다 &lt;b&gt;48%&lt;/b&gt; 감소한 값입니다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (17).png&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b22UnV/btsIk3tTO6j/Ife4MUku4emdfvQb1XxXJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b22UnV/btsIk3tTO6j/Ife4MUku4emdfvQb1XxXJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b22UnV/btsIk3tTO6j/Ife4MUku4emdfvQb1XxXJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb22UnV%2FbtsIk3tTO6j%2FIfe4MUku4emdfvQb1XxXJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;475&quot; height=&quot;344&quot; data-filename=&quot;Untitled (17).png&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;a href=&quot;https://medium.com/airbnb-engineering/airbnbs-trip-to-linaria-dc169230bd12&quot;&gt;Airbnb : Sass에서 CSS in JS(react-with-styled)를 거쳐 Linaria로&lt;/a&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;런타임 프레임워크&amp;nbsp;(react-with-styles, Emotion)와&amp;nbsp;빌드 타임 프레임워크&amp;nbsp;(Linaria, Treat)로 나누어 측정했고&lt;/li&gt;
&lt;li&gt;&lt;b&gt;성능&lt;/b&gt;&amp;nbsp;향상은 주로 빌드시 JS에서 정적 CSS 파일로 스타일을 추출하는 Linaria 덕분에 이루어졌으므로 JS 번들 또는 런타임 CPU 오버헤드가 없으므로 거의 제로에 가까운 런타임 Treat보다 약간 우위에 있습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (18).png&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;293&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKLDYW/btsImBQAKlo/QaPvEpKWqSRcYhYzn4pTP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKLDYW/btsImBQAKlo/QaPvEpKWqSRcYhYzn4pTP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKLDYW/btsImBQAKlo/QaPvEpKWqSRcYhYzn4pTP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKLDYW%2FbtsImBQAKlo%2FQaPvEpKWqSRcYhYzn4pTP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;641&quot; height=&quot;259&quot; data-filename=&quot;Untitled (18).png&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;293&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&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;b&gt; &amp;zwj;♀️&amp;nbsp;CSS in JS&lt;/b&gt; 가 우수하다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;className 규칙을 만들 필요나 팀원간의 유지해야하는 비용 소모가 적다.&lt;/li&gt;
&lt;li&gt;자바스크립트 변수와 리액트 props와 state를 이용해 다이나믹 스타일링에 제한없이 개발하기때문에 속도가 빠르다.&lt;/li&gt;
&lt;li&gt;중복 코드를 방지를 방지할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;렌더링 퍼포먼스&lt;/b&gt;에 있어서는 &lt;b&gt; &amp;zwj;♀️&amp;nbsp;CSS in CSS&lt;/b&gt; 가 우수한 편이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;런타임에서 css를 생산할 필요가 없기때문에, 런타임 오버헤드를 줄일 수 있다.&lt;/li&gt;
&lt;li&gt;브라우저에서 리액트가 렌더링 하는 동안 모든 프레임에 대해 모든 CSS 규칙을 효과적으로 재계산하게 할 필요가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;near-zero runtime&lt;/b&gt;&lt;/h2&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://stitches.dev/&quot;&gt;stitches.js&lt;/a&gt; : stitches.js는 styled-components와 유사한 api를 가진 css-in-js라이브러리이지만&amp;nbsp;&lt;b&gt;near-zero runtime&lt;/b&gt; 을 표방하고 있습니다. 단어 그대로 runtime을 아예 가지지 않는 것은 아니지만, component prop에 의한 interpolation을 최소화하는 방향의 API를 제공합니다.&lt;/li&gt;
&lt;li&gt;tailwind + &lt;a href=&quot;https://github.com/ben-rogerson/twin.macro#gh-light-mode-only&quot;&gt;twin.macro&lt;/a&gt; : tailwind에 emotion css같이 사용할 수 있도록 추가된 형태로 최초에 로드하는 CSS크기는 작지만, 이후 runtime overhead 발생하긴 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fe-developers.kakaoent.com/2022/221013-tailwind-and-design-system/&quot;&gt;카카오 엔터: Emotion에서 Tailwind CSS + Twin.Macro로&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;카카오페이지 웹을 새로 개편할 때는 Emotion CSS를 사용함에 따라 발생하는 이런 문제들을 그대로 답습하면 안 된다고 생각했습니다. 우리가 극복해야 했던 문제들은 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;디자인 시스템을 위한 부가적인 코드가 실제 기능 코드를 침범한다. (Theme)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디자인 코드가 컴포넌트의 가독성을 크게 해치거나 (CSS-in-JS), 기능 코드와 파편화되어 추가 비용을 발생시킨다 (Styled-Component).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제들을 해결하려면 다음과 같은 전제가 보장되어야 합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;디자인 시스템을 위한 부가적인 코드가 실제 기능 코드를 침범하지 않아야 한다. (적어도 실제 컴포넌트 내부 구현을 침범하면 안 된다)&lt;/li&gt;
&lt;li&gt;디자인 코드가 파편화되지 않아야 하며, 컴포넌트의 가독성을 해치는 부분을 최소화해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀은 Tailwind CSS라는 라이브러리가 이 전제들을 만족시키는 가장 적절한 해결책이라고 생각했고, 따라서 문제 해결을 위한 도구로 프로젝트에 tailwind를 도입하도록 결정했습니다. tailwind를 사용하면 디자인 시스템에서 정의한 네이밍을 사용하기 위해 컴포넌트 내에 어떠한 코드도 추가할 필요가 없으며, CSS-in-JS 방식과 유사하게 HTML 요소 자체에 디자인을 명시하기 때문에 기능 코드와 디자인 코드의 파편화를 막되 유틸리티(utility)라는 개념을 통해 가독성을 해치는 부분을 최소화할 수 있습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;나의 생각&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt; 만들고 있는 서비스에서 rendering 최적화가 필요하지 않은 정도의 컴포넌트들만 다룬다면&lt;u&gt; &lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;1&quot;&gt;runtime overhead는 무시할만한 수준일 수도&lt;/span&gt; 있고&lt;/u&gt;, 인터넷 속도나 지역에 따라 runtime overhead가 유저에게 영향을 미칠 수도 있다. 그런데 오히려 runtime이 존재하지 않음으로써 이를 따로 해결해주어야 하는 상황을 만날 수 있다. 그렇기 때문에, 무조건 &amp;lsquo;좋다 나쁘다&amp;rsquo;로 나눌 수 없으며 &lt;u&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;3&quot;&gt;서비스의 특성과 계획을 고려하여 알맞은 CSS 사용방법을 선택해야 할 것이다.&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt; 개인적으로는 서비스의 크기가 커지면 커질 수록 CSS in JS에 맞는 부분이라고 생각이 든다. stylesheet은 일정하고 그 일정한 stylesheet를 이용해 개발을 하면 좋지만, 현실적으로 서비스 규모에 따라 stylesheet의 양이 많아지는 경우가 많고&amp;hellip;.(컴포넌트도 늘고&amp;hellip;.) 그래서 &lt;u&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;5&quot;&gt;페이지에서 진입해 불러오는 stylesheet리소스를 최소화&lt;/span&gt;&lt;/u&gt;하고 그 이후 유저의 액션에 따라 stylesheet생성 및 렌더링 퍼포먼스가 유저에게 느껴질 정도가 아니라면 문제가 없다는 생각도 든다. &lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;7&quot;&gt;zero-runtime 혹은 near-zero runtime처럼&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;8&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;9&quot;&gt;하이브리드하게 가는 것이 결국엔 좋아보이긴하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;9&quot;&gt; &lt;/span&gt; &lt;span&gt; &lt;/span&gt; SPA가 크게 유행을 한 이유가 자바스크립트를 이용한 자연스러운 인터렉션와 스타일, jQuery처럼 직접 DOM을 조작하지 않고 virtural DOM을 사용한다는 점에서 떴다고 생각하는데, &lt;u&gt;그러면서 &lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;11&quot;&gt;간과했던 SEO 라던가 렌더링 퍼포먼스 같은 것들을 보완하기위한 style 라이브러리나 Next.js같은 프레임워크에 초점이 맞춰지는 느낌&lt;/span&gt;이다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fe-developers.kakaoent.com/2022/221013-tailwind-and-design-system/&quot;&gt;FE개발그룹에서는 Tailwind CSS를 왜 도입했고, 어떻게 사용했을까?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://junghan92.medium.com/%EB%B2%88%EC%97%AD-%EC%9A%B0%EB%A6%AC%EA%B0%80-css-in-js%EC%99%80-%ED%97%A4%EC%96%B4%EC%A7%80%EB%8A%94-%EC%9D%B4%EC%9C%A0-a2e726d6ace6&quot;&gt;(번역) 우리가 CSS-in-JS와 헤어지는 이유&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://so-so.dev/web/css-in-js-whats-the-defference/&quot;&gt;CSS-in-JS, 무엇이 다른가요?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pustelto.com/blog/css-vs-css-in-js-perf/&quot;&gt;Real-world CSS vs. CSS-in-JS performance comparison&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://engineering.fb.com/2020/05/08/web/facebook-redesign/&quot;&gt;Rebuilding our tech stack for the new Facebook.com&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.samsungsds.com/kr/insights/web_component.html&quot;&gt;웹 컴포넌트 스타일링 관리 : CSS-in-JS vs CSS-in-CSS&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.cometkim.kr/posts/css-optimization-in-jamstack/&quot;&gt;Jamstack에서 스타일시트를 최적화하는 법&lt;/a&gt;&lt;/p&gt;</description>
      <category>Developer/CSS</category>
      <category>cssincss</category>
      <category>CSSinjs</category>
      <category>Emotion</category>
      <category>styledcomponent</category>
      <category>tailwindcss</category>
      <author>jaddong</author>
      <guid isPermaLink="true">https://jaddong.tistory.com/93</guid>
      <comments>https://jaddong.tistory.com/entry/%EC%BD%94%EB%93%9C%EA%B4%80%EB%A6%AC%EC%99%80-%ED%8D%BC%ED%8F%AC%EB%A8%BC%EC%8A%A4-%EA%B4%80%EC%A0%90%EC%97%90%EC%84%9C%EC%9D%98-CSS-in-JS-vs-CSS-in-CSS#entry93comment</comments>
      <pubDate>Wed, 3 Jul 2024 16:01:48 +0900</pubDate>
    </item>
    <item>
      <title>디자인 시스템을 개발하면서 알게 된 peerDependencies의 역할</title>
      <link>https://jaddong.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EA%B0%9C%EB%B0%9C%ED%95%98%EB%A9%B4%EC%84%9C-%EC%95%8C%EA%B2%8C-%EB%90%9C-peerDependencies%EC%9D%98-%EC%97%AD%ED%95%A0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;package.json에서는 우리가 해당 프로젝트에 대한 정보 및 의존 정보들을 작성하게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name, verson, repository, scripts, dependencies, devDependencies 등의 정보가 json형태로 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json에서 대부분 쓰는 항상 항목들만 쓰는 경우가 많을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 만약 &lt;u&gt;&lt;b&gt;공통 모듈 패키지나 라이브러리를 개발중이라면 peerDependencies에 집중할 필요가 있다는 걸 알게되었다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 문제 발견&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-app 리액트 프로젝트에서 내가 개발한 라이브러리 my-packge와 another-package를 설치해 사용하려고했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 package.json를 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718007876525&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;my-app&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;^17.0.0&quot;
    &quot;my-package&quot;: &quot;^1.0.0&quot;,
    &quot;another-package&quot;: &quot;^1.0.0&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-app에서 yarn install을 하니 아래와 같은 warning을 발견할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718007780104&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn install

warning &quot;my-package@1.0.0&quot; has unmet peer dependency &quot;react@^17.0.0&quot;.
warning &quot;another-package@1.0.0&quot; has unmet peer dependency &quot;react@^17.0.0&quot;.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 동작에는 크게 문제가 없어보였다. 아마 리액트 버전이 같아서 그럴지도 모르겠다.  &amp;zwj;♀️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 경우를 찾아보니 실제로 종속성 에러가 나는 경우도 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 warning이나 error는 가능하면 깨끗하게 없애고 싶어진다.   그래서 찾게 된 peerDependencies!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 문제 파악하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;Peer dependencies는 특정 패키지가 다른 패키지를 필요로 하지만, 그 패키지를 직접 포함하지 않고, 상위 프로젝트(패키지를 설치하는 프로젝트)에서 해당 의존성을 설치하도록 요구하는 방식&quot;&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;이렇게만 보면 무슨 소린지 모를 수 있으니 직접 peerDependencies를 작성했을 때와 작성하지 않았을 때를 비교해보도록 했다.&lt;/p&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;peerDependencies를 작성하지 않았을 때 의존성 트리 확인하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 만든 라이브러리인 my-package와 another-package는 react &quot;^17.0.0&quot;을 의존성으로 가지고 있고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-app 도 똑같이 react &quot;^17.0.0&quot;를 의존하고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1718008324853&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;my-app&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;^17.0.0&quot;
    &quot;my-package&quot;: &quot;^1.0.0&quot;,
    &quot;another-package&quot;: &quot;^1.0.0&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718008341016&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;my-package&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;^17.0.0&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1718008361452&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;another-package&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;^17.0.0&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm ls react 를 커맨드창에 쳐보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 peerDependencies를 작성하지 않았을 때의 의존성 트리 살펴보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-package의 node_moules에도 another-package의 node_moudules 에도 똑같이 react가 설치되어진다! 무려 세 번!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718008902163&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;my-app
├── node_modules
│   ├── my-package
│   │   └── node_modules
│   │       └── react (17.0.0) 
│   └── another-package
│   │   └── node_modules
│   │       └── react (17.0.0) 
│   └── react (17.0.0)
└── package.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;peerDependencies를 작성했을 때 의존성 트리 확인하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;peerDependencies는 이 라이브러리를 사용하게 될 프로젝트에게, &lt;/span&gt;&lt;u&gt;&lt;b&gt;react ^17.0.0&lt;/b&gt;&lt;/u&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;버전을 사용해주세요!&lt;/b&gt;&lt;/u&gt; 라고 알려주는 것과 비슷하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718013813120&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;my-package&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;17.0.2&quot;,
  }
  &quot;peerDependencies&quot;: {
    &quot;react&quot;: &quot;^17.0.0&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718013822819&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;another-package&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;17.0.2&quot;,
  }
  &quot;peerDependencies&quot;: {
    &quot;react&quot;: &quot;^17.0.0&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm ls react 를 커맨드창에 쳐서 프로젝트에서 의존성 트리를 살펴보면 react가 하나로만 node_modules에 들어가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot;&gt;deduped 라고 중복무시된 것을 확인할 수 있다.&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718009732139&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;my-app
├── node_modules
│   ├── my-package
│   │   └── node_modules
│   │       └── react (17.0.0) deduped
│   └── another-package
│   │   └── node_modules
│   │       └── react (17.0.0) deduped
│   └── react (17.0.0)
└── package.json&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;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;차이 요약&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Peer Dependencies 사용 전&lt;/b&gt;: &lt;u&gt;각 패키지가 자체적으로 필요한 의존성을 포함&lt;/u&gt;하여 node_modules 폴더가 중복 설치된 라이브러리로 인해 커진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Peer Dependencies 사용 후&lt;/b&gt;: &lt;u&gt;상위 프로젝트에서 의존성을 관리하여 중복 설치를 방지&lt;/u&gt;하고, node_modules 폴더의 크기를 줄인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실제 코드로 라이브러리의 빌드 결과물에서 확인해보기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;라이브러리에 peerDependencies 없이 빌드했을 때&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-package와 another-package에 peerDependencies에 react와 react-router-dom 를 넣지 않았을 때 dist/index.js 결과물을 보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&quot;var reactExports = {} ... &quot;&lt;/span&gt; 로 시작하는 react 코드가 빌드 결과에 포함되어 있는 걸 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2106&quot; data-origin-height=&quot;1112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/90es5/btsHUNQ8ss7/loKWR9WWPcBki7x8q6Pxh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/90es5/btsHUNQ8ss7/loKWR9WWPcBki7x8q6Pxh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/90es5/btsHUNQ8ss7/loKWR9WWPcBki7x8q6Pxh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F90es5%2FbtsHUNQ8ss7%2FloKWR9WWPcBki7x8q6Pxh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2106&quot; height=&quot;1112&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2106&quot; data-origin-height=&quot;1112&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OFTva/btsHUPg579u/UAdF40ViIDUZY02KwPI9rK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OFTva/btsHUPg579u/UAdF40ViIDUZY02KwPI9rK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OFTva/btsHUPg579u/UAdF40ViIDUZY02KwPI9rK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOFTva%2FbtsHUPg579u%2FUAdF40ViIDUZY02KwPI9rK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1480&quot; height=&quot;288&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 dist/index.js 을 용량은 180KB로 측정되었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;라이브러리에 peerDependencies 에 react, react-router-dom 를 추가하여 빌드했을 때&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-package와 another-package에 peerDependencies에 react와 react-router-dom를 넣고 빌드를 하면, react가 아래와 같이 한 줄로만 보여진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&quot;var reactExports = {} ... &quot;&lt;/span&gt; 로 시작하던 약 2493줄의 리액트 코드가 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;var React = require('react')&amp;nbsp;&lt;/b&gt;&lt;/span&gt;으로 바뀌게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2078&quot; data-origin-height=&quot;1112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7B7cf/btsHTVo1Y4M/XeB9TfYkZ2oHgFHACdOPrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7B7cf/btsHTVo1Y4M/XeB9TfYkZ2oHgFHACdOPrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7B7cf/btsHTVo1Y4M/XeB9TfYkZ2oHgFHACdOPrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7B7cf%2FbtsHTVo1Y4M%2FXeB9TfYkZ2oHgFHACdOPrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2078&quot; height=&quot;1112&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2078&quot; data-origin-height=&quot;1112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KRdGM/btsHUpwpH9p/ZMxSIsbsXxDTJqIu4S4y91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KRdGM/btsHUpwpH9p/ZMxSIsbsXxDTJqIu4S4y91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KRdGM/btsHUpwpH9p/ZMxSIsbsXxDTJqIu4S4y91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKRdGM%2FbtsHUpwpH9p%2FZMxSIsbsXxDTJqIu4S4y91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1486&quot; height=&quot;254&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;254&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;그러다보니 index.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;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로, peer dependencies를 사용하면 패키지 간의 중복 의존성을 줄이고, &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;빌드된 결과물의 용량을 효율적으로 관리&lt;/span&gt;&lt;/b&gt;할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;react와 react-router-dom만 peer dependencies에 추가해도 라이브러리 번들 결과를 약 50%나 줄일 수 있었다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;의존성 충돌을 방지&lt;/span&gt;&lt;/b&gt;하기 위해 의존성 트리를 확인하는 것이 좋을 것 같고, 특히나 react 가 포함된 라이브러리를 리액트 프로젝트에 사용하는 경우 꼭 확인해야할 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Developer/React</category>
      <category>package.json</category>
      <category>peerdependencies</category>
      <category>React</category>
      <category>의존성</category>
      <category>최적화</category>
      <author>jaddong</author>
      <guid isPermaLink="true">https://jaddong.tistory.com/92</guid>
      <comments>https://jaddong.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EA%B0%9C%EB%B0%9C%ED%95%98%EB%A9%B4%EC%84%9C-%EC%95%8C%EA%B2%8C-%EB%90%9C-peerDependencies%EC%9D%98-%EC%97%AD%ED%95%A0#entry92comment</comments>
      <pubDate>Mon, 10 Jun 2024 20:22:35 +0900</pubDate>
    </item>
    <item>
      <title>css 애니메이션, position대신 transform으로 하면 좋은 이유(예제 포함)</title>
      <link>https://jaddong.tistory.com/entry/css-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-position%EB%8C%80%EC%8B%A0-transform%EC%9C%BC%EB%A1%9C-%ED%95%98%EB%A9%B4-%EC%A2%8B%EC%9D%80-%EC%9D%B4%EC%9C%A0%EC%98%88%EC%A0%9C-%ED%8F%AC%ED%95%A8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;reflow와 repaint가 많을수록 성능저하를 유발&lt;/b&gt;&lt;/u&gt;한다고 알고있는데, 이론으로 말고 실제로 구현과 측정을 통해서 어떤 차이가 있는지 알아보고 싶어서 테스트를 작성해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp; DevTool에서 성능(performance)탭에서 알아본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 레이아웃이 다시 되는지 확인한다. -&amp;gt; reflow를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;relfow&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #3c4858; text-align: start;&quot;&gt;생성된 DOM 노드의 레이아웃(너비, 높이 등) 변경 시 영향받는 모든 노드(자식, 부모)의 &lt;u&gt;&lt;b&gt;수치를 다시 계산&lt;/b&gt;&lt;/u&gt;하여 &lt;b&gt;&lt;u&gt;렌더 트리를 재생성&lt;/u&gt;&lt;/b&gt;하는 작업입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;repaint&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;r&lt;span&gt;&lt;span style=&quot;background-color: #ffffff; color: #3c4858; text-align: start;&quot;&gt;eflow 과정이 끝난 후, 재생성된 렌더 트리를 다시 그리는 작업으로 수치와 상관없는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #3c4858; text-align: start;&quot;&gt;background-color, visibility, outline&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #3c4858; text-align: start;&quot;&gt;&amp;nbsp;등의 &lt;b&gt;&lt;u&gt;스타일 변경시&lt;/u&gt;&lt;/b&gt;에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #3c4858; text-align: start;&quot;&gt;reflow&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #3c4858; text-align: start;&quot;&gt;&amp;nbsp;과정이 생략 된&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #3c4858; text-align: start;&quot;&gt;repaint&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #3c4858; text-align: start;&quot;&gt; 작업만 수행합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;그냥 쉽게 해서 종이에 상자 그림이 있고 그걸 다시 그린다고 가정했을 때 아래와 같지 않을까 싶다.&lt;br /&gt;&lt;br /&gt;step1 : 종이에 그릴 상자의 너비, 높이, 위치를 &lt;u&gt;&lt;b&gt;다시&lt;/b&gt;&lt;/u&gt; 계산한다. -&amp;gt; reflow&lt;br /&gt; step2 : 상자를 빨간색으로&amp;nbsp;&lt;u&gt;&lt;b&gt;다시&lt;/b&gt;&lt;/u&gt; 칠한다. -&amp;gt; repaint&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;left로 박스의 위치를 이동시켰을 때&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTML 코드&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1713254289831&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
		&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
		&amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
		&amp;lt;style&amp;gt;
			@keyframes reposition {
				0% {
				}
				100% {
					left: 200px;
				}
			}
			body {
				margin: 0;
			}
			#box {
				width: 200px;
				height: 200px;
				background-color: black;
				position: absolute;
				left: 0;
			}
			.move {
				animation: reposition 3s ease-in forwards;
			}
		&amp;lt;/style&amp;gt;
	&amp;lt;/head&amp;gt;
	&amp;lt;body&amp;gt;
		&amp;lt;button id=&quot;button&quot;&amp;gt;Move!&amp;lt;/button&amp;gt; // 버튼
		&amp;lt;div id=&quot;box&quot;&amp;gt;&amp;lt;/div&amp;gt; // 박스
		&amp;lt;script&amp;gt;
			const box = document.getElementById(&quot;box&quot;);
			button.addEventListener(&quot;click&quot;, () =&amp;gt; {
				box.className = &quot;move&quot;;
			});
		&amp;lt;/script&amp;gt;
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과 영상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출 트리를 보면 &lt;u&gt;레이아웃에 11.0ms 가량&lt;/u&gt; 시간이 걸린 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Apr-16-2024 16-37-45.gif&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Aeuw/btsGFAfdEWU/s1dV3lOu6ghI63hOp4wdh0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Aeuw/btsGFAfdEWU/s1dV3lOu6ghI63hOp4wdh0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Aeuw/btsGFAfdEWU/s1dV3lOu6ghI63hOp4wdh0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/5Aeuw/btsGFAfdEWU/s1dV3lOu6ghI63hOp4wdh0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;301&quot; data-filename=&quot;Apr-16-2024 16-37-45.gif&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;결과 사진&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 로그에서 &quot;레이아웃&quot;으로 검색하면 이동할 때마다 &lt;span style=&quot;background-color: #ffffff; color: #3c4858; text-align: start;&quot;&gt;&lt;u&gt;reflow가 발생함&lt;/u&gt;을 확인할 수 있다.&lt;/span&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;스크린샷 2024-04-16 오후 4.33.19.png&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ErbF7/btsGFCEneYd/TDrpGgeU4zh8bAkvfV6Up0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ErbF7/btsGFCEneYd/TDrpGgeU4zh8bAkvfV6Up0/img.png&quot; data-alt=&quot;호출트리를 보면 레이아웃에 11.0밀리초가 걸린 걸 확인할 수 있었다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ErbF7/btsGFCEneYd/TDrpGgeU4zh8bAkvfV6Up0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FErbF7%2FbtsGFCEneYd%2FTDrpGgeU4zh8bAkvfV6Up0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;265&quot; data-filename=&quot;스크린샷 2024-04-16 오후 4.33.19.png&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;호출트리를 보면 레이아웃에 11.0밀리초가 걸린 걸 확인할 수 있었다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-17 오후 12.46.25.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zuiLa/btsGGOR3fQE/bNwQYYUf3WT3K3UHlyRnO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zuiLa/btsGGOR3fQE/bNwQYYUf3WT3K3UHlyRnO1/img.png&quot; data-alt=&quot;이벤트 로그에서도 계속적으로 레이아웃 감지됨을 확인 할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zuiLa/btsGGOR3fQE/bNwQYYUf3WT3K3UHlyRnO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzuiLa%2FbtsGGOR3fQE%2FbNwQYYUf3WT3K3UHlyRnO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;774&quot; height=&quot;431&quot; data-filename=&quot;스크린샷 2024-04-17 오후 12.46.25.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이벤트 로그에서도 계속적으로 레이아웃 감지됨을 확인 할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;transform으로 박스의 위치를 이동시켰을 때&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;HTML 코드&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1713325908774&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
		&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
		&amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
		&amp;lt;style&amp;gt;
			body {
				margin: 0;
			}
			#box {
				width: 200px;
				height: 200px;
				background-color: black;
				position: absolute;
				left: 0;
			}
			.move {
				transform: translateX(200px);
				transition: transform 3s ease-in;
			}
		&amp;lt;/style&amp;gt;
	&amp;lt;/head&amp;gt;
	&amp;lt;body&amp;gt;
		&amp;lt;button id=&quot;button&quot;&amp;gt;Move!&amp;lt;/button&amp;gt;
		&amp;lt;div id=&quot;box&quot;&amp;gt;&amp;lt;/div&amp;gt;
		&amp;lt;script&amp;gt;
			const box = document.getElementById(&quot;box&quot;);
			button.addEventListener(&quot;click&quot;, () =&amp;gt; {
				box.className = &quot;move&quot;;
			});
		&amp;lt;/script&amp;gt;
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;결과 영상&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Apr-16-2024 16-37-56.gif&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tKhMR/btsGHlIBgKM/3ABDlRMctcgJzXwsh7ihdK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tKhMR/btsGHlIBgKM/3ABDlRMctcgJzXwsh7ihdK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tKhMR/btsGHlIBgKM/3ABDlRMctcgJzXwsh7ihdK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/tKhMR/btsGHlIBgKM/3ABDlRMctcgJzXwsh7ihdK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;301&quot; data-filename=&quot;Apr-16-2024 16-37-56.gif&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;결과 사진&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 로그에서 &quot;레이아웃&quot;으로 검색하면&lt;u&gt;&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #3c4858; text-align: start;&quot;&gt;reflow가 발생하지 않는 걸 확인할 수 있다.&lt;/span&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-16 오후 4.34.20.png&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kQ1Rp/btsGG8C61U8/d9dUfKcCibcIPgJcvgmCB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kQ1Rp/btsGG8C61U8/d9dUfKcCibcIPgJcvgmCB1/img.png&quot; data-alt=&quot;호출트리를 보면 레이아웃이 없는 걸 확인할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kQ1Rp/btsGG8C61U8/d9dUfKcCibcIPgJcvgmCB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkQ1Rp%2FbtsGG8C61U8%2Fd9dUfKcCibcIPgJcvgmCB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;566&quot; height=&quot;246&quot; data-filename=&quot;스크린샷 2024-04-16 오후 4.34.20.png&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;호출트리를 보면 레이아웃이 없는 걸 확인할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;스크린샷 2024-04-17 오후 12.46.01.png&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qNief/btsGFgBKFw9/1JMUxjhNAikukQxgjjKehk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qNief/btsGFgBKFw9/1JMUxjhNAikukQxgjjKehk/img.png&quot; data-alt=&quot;이벤트 로그에서도 레이아웃는 없다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qNief/btsGFgBKFw9/1JMUxjhNAikukQxgjjKehk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqNief%2FbtsGFgBKFw9%2F1JMUxjhNAikukQxgjjKehk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;771&quot; height=&quot;282&quot; data-filename=&quot;스크린샷 2024-04-17 오후 12.46.01.png&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이벤트 로그에서도 레이아웃는 없다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&lt;i&gt;작성한 단순한 코드여서 레이아웃에 11.0ms의 차이가 있지만, 상자를 50개라면 어떨까 궁금했다. &lt;/i&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상자 50개로 다시 테스트해 본 결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;left로 구현했을 때 레이아웃에 &lt;u&gt;19ms가 걸렸다.&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;293&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2fxiA/btsGG7qIWCJ/1vqbpHaj0yaI1OhPLeEzG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2fxiA/btsGG7qIWCJ/1vqbpHaj0yaI1OhPLeEzG0/img.png&quot; data-alt=&quot;left : 레이아웃에 19.0ms가 걸렸다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2fxiA/btsGG7qIWCJ/1vqbpHaj0yaI1OhPLeEzG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2fxiA%2FbtsGG7qIWCJ%2F1vqbpHaj0yaI1OhPLeEzG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;293&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;293&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;left : 레이아웃에 19.0ms가 걸렸다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transform으로 구현했을 때는 역시나 &lt;u&gt;레이아웃에 소모되는 시간이 없다.&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kDeNx/btsGIW2QwBC/MBQsyIh7uLF6EgJ6OuKZOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kDeNx/btsGIW2QwBC/MBQsyIh7uLF6EgJ6OuKZOK/img.png&quot; data-alt=&quot;transform : 레이아웃이 없다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kDeNx/btsGIW2QwBC/MBQsyIh7uLF6EgJ6OuKZOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkDeNx%2FbtsGIW2QwBC%2FMBQsyIh7uLF6EgJ6OuKZOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;257&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;transform : 레이아웃이 없다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트용이라서 엄청 큰 차이는 없어보일 수 있으나, 코드가 복잡해지고 애니메이션이 많아진다면 영향을 미칠 수 있다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소 애니메이션을 구현할 때 width, height, left, top 같은 property들을 지양하는 습관을 들이는 게 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 어떤 property가 reflow를 일으키는지 궁금할 수 있는데, &lt;a title=&quot;링크&quot; href=&quot;https://docs.google.com/spreadsheets/u/0/d/1Hvi0nu2wG3oQ51XRHtMv-A_ZlidnwUYwgQsPQUg1R2s/pub?single=true&amp;amp;gid=0&amp;amp;output=html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;에서 참고할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;transform은 &lt;span style=&quot;text-align: center;&quot;&gt;composite로 구분되어 있는데, 이것도 나중에 알아봐야겠다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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://wit.nts-corp.com/2017/06/05/4571&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wit.nts-corp.com/2017/06/05/4571&lt;/a&gt;&lt;/p&gt;</description>
      <category>Developer/CSS</category>
      <category>animation</category>
      <category>css</category>
      <category>HTML</category>
      <category>javascript</category>
      <category>reflow</category>
      <category>repaint</category>
      <category>성능개선</category>
      <author>jaddong</author>
      <guid isPermaLink="true">https://jaddong.tistory.com/91</guid>
      <comments>https://jaddong.tistory.com/entry/css-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-position%EB%8C%80%EC%8B%A0-transform%EC%9C%BC%EB%A1%9C-%ED%95%98%EB%A9%B4-%EC%A2%8B%EC%9D%80-%EC%9D%B4%EC%9C%A0%EC%98%88%EC%A0%9C-%ED%8F%AC%ED%95%A8#entry91comment</comments>
      <pubDate>Wed, 17 Apr 2024 15:01:31 +0900</pubDate>
    </item>
    <item>
      <title>i18n(국제화 언어)리액트에서 적용하기</title>
      <link>https://jaddong.tistory.com/entry/i18n%EA%B5%AD%EC%A0%9C%ED%99%94-%EC%96%B8%EC%96%B4%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목적&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국제화(internalization)된 애플리케이션은 다양한 언어로 서비스를 할 수 있습니다. 그러기 위해서는 사용자의 언어/지역을 자동으로 감지하거나 사용자가 직접 선택할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;라이브러리 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 사용할 수 있는 국제화(internalization, 이하 i18n) 라이브러리는 두 종류가 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (11).png&quot; data-origin-width=&quot;2716&quot; data-origin-height=&quot;1516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zNUR0/btsFF1rLwGd/OliwRa5x8Gjs6WAUkLjTck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zNUR0/btsFF1rLwGd/OliwRa5x8Gjs6WAUkLjTck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zNUR0/btsFF1rLwGd/OliwRa5x8Gjs6WAUkLjTck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzNUR0%2FbtsFF1rLwGd%2FOliwRa5x8Gjs6WAUkLjTck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2716&quot; height=&quot;1516&quot; data-filename=&quot;Untitled (11).png&quot; data-origin-width=&quot;2716&quot; data-origin-height=&quot;1516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/formatjs/react-intl&quot;&gt;react-intl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/i18next/react-i18next&quot;&gt;react-i18next&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Next.js지원을 강화한&amp;nbsp;&lt;a href=&quot;https://github.com/isaachinman/next-i18next&quot;&gt;next-i18next&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Next.js와 Server Side Rendering(SSR)을 사용해야 한다면 next-i18next&lt;/li&gt;
&lt;li&gt;Next.js의 Static Site Generation(SSG)을 사용해야 한다면 react-i18next
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSR이 필요해질 경우 next-i18next로 갈아타기 편리하기 때문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Next.js를 사용하지 않는다면 react-intl&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;셋팅&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;i18next&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번역 기능을 제공하는 JavaScript 라이브러리로, React와 같은 프론트엔드 프레임워크와 독립적으로 사용될 수 있습니다. 이 라이브러리는 다국어 지원을 위해 필요한 핵심 기능을 제공하며, 텍스트의 번역, 다국어 문구들의 로딩과 캐싱, 언어 변경 등을 처리할 수 있습니다. &lt;b&gt;i18next &lt;/b&gt;는 다양한 환경에서 사용할 수 있으며, 웹 앱 뿐만 아니라 모바일 앱이나 서버 측 앱에서도 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;react-i18next(&lt;a href=&quot;https://react.i18next.com/guides/quick-start&quot;&gt;Quick start&lt;/a&gt;)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;i18next&lt;/b&gt; 를 React 애플리케이션에서 쉽게 사용할 수 있도록 해주는 고급적인 React 바인딩 라이브러리입니다. &lt;b&gt;react-i18next&lt;/b&gt; 는 &lt;b&gt;i18next&lt;/b&gt; 와 함께 사용하기 위해 React 컴포넌트와 훅을 제공하여 React 앱에서 다국어 지원을 쉽게 구현할 수 있도록 도와줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 &lt;b&gt;i18next&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;useTranslation&lt;/b&gt;: React 훅으로, 컴포넌트에서 다국어 텍스트를 가져오고 언어 변경을 처리할 수 있게 도와줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Trans&lt;/b&gt;: 다국어 텍스트를 렌더링하는 데 사용되는 컴포넌트입니다.&lt;/li&gt;
&lt;li&gt;기타 추가적인 유틸리티와 컴포넌트들로 구성되어 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;i18next-browser-languagedetector&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i18next-browser-languagedetector는 i18next 라이브러리의 브라우저용 언어 감지기입니다. 이것을 사용하여 웹 애플리케이션에서 사용자의 브라우저 언어를 감지하고, 해당 언어에 맞는 디폴트 값을 설정할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;yarn add react-i18next i18next i18next-browser-languagedetector
&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;1. src/i18n/index.ts&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;src/i18n/index.ts&lt;/p&gt;
&lt;pre id=&quot;code_1710145971251&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import LanguageDetector from 'i18next-browser-languagedetector'

import {
  koHome,
  koGnb,
  enHome,
  enGnb,
  frHome,
  frGnb,
  jpHome,
  jpGnb,
} from '../locale'

// the translations
// (tip move them in a JSON file and import them,
// or even better, manage them separated from your code: https://react.i18next.com/guides/multiple-translation-files)
const resources = {
  ko: {
    home: koHome,
    gnb: koGnb,
  },
  en: {
    home: enHome,
    gnb: enGnb,
  },
  fr: {
    home: frHome,
    gnb: frGnb,
  },
  jp: {
    home: jpHome,
    gnb: jpGnb,
  },
}

i18n
  .use(LanguageDetector)
  .use(initReactI18next) // passes i18n down to react-i18next
  .init({
    resources,
    fallbackLng: 'en',
    supportedLngs: ['ko', 'en', 'fr', 'jp'],
		// language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
    // you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
    // if you're using a language detector, do not define the lng option

    interpolation: {
      escapeValue: false, // react already safes from xss
    },
  })

export default i18n&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;2. src/main.tsx 에 import i18n 추가&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;src/main.tsx&lt;/p&gt;
&lt;pre id=&quot;code_1710146083843&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'

import {
  ContainerModalProvider,
  ProgressModalProvider,
  SnackbarProvider,
  ThemeProvider,
} from '@nwaycorp/global-designsystem'
import { nWeb3 } from '@nwaycorp/web3-react'
import './i18n' // 추가

import { App } from './App.tsx'

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;nWeb3.ReactProvider&amp;gt;
      &amp;lt;BrowserRouter&amp;gt;
        &amp;lt;ThemeProvider theme={undefined}&amp;gt;
          &amp;lt;SnackbarProvider&amp;gt;
            &amp;lt;ProgressModalProvider&amp;gt;
              &amp;lt;ContainerModalProvider&amp;gt;
                &amp;lt;App /&amp;gt;
              &amp;lt;/ContainerModalProvider&amp;gt;
            &amp;lt;/ProgressModalProvider&amp;gt;
          &amp;lt;/SnackbarProvider&amp;gt;
        &amp;lt;/ThemeProvider&amp;gt;
      &amp;lt;/BrowserRouter&amp;gt;
    &amp;lt;/nWeb3.ReactProvider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById('root')
)&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;h4 data-ke-size=&quot;size20&quot;&gt;3. src/locale에 언어별 json 파일&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src/locale/en/gnb.json&lt;/p&gt;
&lt;pre id=&quot;code_1710146137994&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;about&quot;: &quot;about&quot;,
  &quot;web3test&quot;: &quot;web3 test&quot;,
  &quot;designSystem&quot;: &quot;design system&quot;,
  &quot;connectWallet&quot;: &quot;Connect Wallet&quot;,
  &quot;disconnectWallet&quot;: &quot;Disconnect Wallet&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src/locale/en/home.json&lt;/p&gt;
&lt;pre id=&quot;code_1710146235890&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;welcome&quot;: &quot;Welcome to CCBB&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. src/locale/index.ts 에서 json 파일을 export&lt;/h3&gt;
&lt;pre id=&quot;code_1710146441033&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import koHome from '../locale/ko/home.json'
import koGnb from '../locale/ko/gnb.json'
import enHome from '../locale/en/home.json'
import enGnb from '../locale/en/gnb.json'
import frHome from '../locale/fr/home.json'
import frGnb from '../locale/fr/gnb.json'
import jpHome from '../locale/jp/home.json'
import jpGnb from '../locale/jp/gnb.json'

export { koHome, koGnb, enHome, enGnb, frHome, frGnb, jpHome, jpGnb }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. src/constants.ts에 화이트 리스트 language 상수 선언&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;src/constants.ts&lt;/p&gt;
&lt;pre id=&quot;code_1710146346063&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const languages = [
  {
    label: 'English',
    value: 'en',
  },
  {
    label: 'French',
    value: 'fr',
  },
  {
    label: 'Korean',
    value: 'ko',
  },
  {
    label: 'Japanese',
    value: 'jp',
  },
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;useTranslation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i18n에서 제공하는 hook&lt;/p&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;const { t, i18n } = useTranslation(['gnb'])

return (&amp;lt;&amp;gt;
    &amp;lt;Typography&amp;gt;{i18n.language}&amp;lt;/Typography&amp;gt; // 현재 lanuage 상태를 보여줌
    &amp;lt;Typography&amp;gt;{t('about')}&amp;lt;/Typography&amp;gt; // gnb.about를 불러옴
&amp;lt;/&amp;gt;  
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SelectLanguage&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인 시스템의 Select 컴포넌트를 활용한 언어를 변경하는 컴포넌트&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useState } from 'react'
import { useTranslation } from 'react-i18next'

import { Select } from '@nwaycorp/global-designsystem'

import { languages } from '@src/constants'

export const SelectLanguage = () =&amp;gt; {
  const { i18n } = useTranslation()
	const initialIndex = languages.findIndex(
    (lang) =&amp;gt; lang.value === i18n.language
  )
  const [selectedIndex, setSelectedIndex] = useState&amp;lt;number&amp;gt;(initialIndex)

  const onClickLanguageChange = (index: number) =&amp;gt; {
    setSelectedIndex(index) // 선택된 index 값
    i18n.changeLanguage(languages[index].value) // 선택한 언어로 변경
  }

  return (
    &amp;lt;Select
      id=&quot;language&quot;
      items={languages}
      onSelect={onClickLanguageChange}
      selectedIndex={selectedIndex}
      size=&quot;s&quot;
    /&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&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;google sheets 연동으로 번역가와 소통 줄이기 : &lt;b&gt;&lt;a href=&quot;https://meetup.nhncloud.com/posts/295&quot;&gt;NHN 국제화(i18n) 자동화 가이드&lt;/a&gt;&lt;/b&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.wooslog.com/blog/internalization-automation&quot;&gt;https://www.wooslog.com/blog/internalization-automation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rldnd.net/react-i18next--&quot;&gt;https://www.rldnd.net/react-i18next--&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 참고 문서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://myeongjae.kim/blog/2020/04/12/react-internationalization-libraries-comparison&quot;&gt;https://myeongjae.kim/blog/2020/04/12/react-internationalization-libraries-comparison&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/ms-club-of-sliit/internationalization-using-i18next-with-react-typescript-d7c443df3be4&quot;&gt;https://medium.com/ms-club-of-sliit/internationalization-using-i18next-with-react-typescript-d7c443df3be4&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Developer/React</category>
      <author>jaddong</author>
      <guid isPermaLink="true">https://jaddong.tistory.com/90</guid>
      <comments>https://jaddong.tistory.com/entry/i18n%EA%B5%AD%EC%A0%9C%ED%99%94-%EC%96%B8%EC%96%B4%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0#entry90comment</comments>
      <pubDate>Mon, 11 Mar 2024 17:44:18 +0900</pubDate>
    </item>
    <item>
      <title>리액트에서 DOM 사이즈 줄이는 최적화 방법</title>
      <link>https://jaddong.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-DOM-%EC%82%AC%EC%9D%B4%EC%A6%88-%EC%A4%84%EC%9D%B4%EB%8A%94-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어셋 최적화를 포함한 &lt;u&gt;웹서비스에서의 최적화에 관심을 가지게 되면서 작업&lt;/u&gt;했던&amp;nbsp; 것 중 하나이다.&lt;/p&gt;
&lt;p style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아이템 리스트에서 처음에 썸네일 이미지가 보이기 전에 스켈레톤 UI를 보여주곤 한다. 그래서 스켈레톤 컴포넌트를 공통으로 쓰고 있었다. 그러다 보니 단순 이미지 엘리먼트뿐만 아니라 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;스켈레톤 UI의 DOM도 포함됨으로써 시각적으로 좀 느린 느낌이 느껴졌다!&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;물론 사용자 입장에서는 크게 느끼지 못할 수 있으나, 개발자 입장에서는 사소한 느린 로딩도 알아채기가 쉽다....&lt;/p&gt;
&lt;p style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 찾게 된 방법이다. 특히나 emotion이나 styled-component 같은 CSS-in-JS를 사용한다면 DOM구조가 나도 모르게 엄청 깊어지는 걸 알 수 있다. 그래서 이 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;최대한 DOM요소를 줄여 로드되는 시간을 줄이고자 했다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;96f5efa8-3e92-4374-92c1-aeddd012add0&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;DOM 사이즈가 중요한 이유&lt;/h2&gt;
&lt;p id=&quot;24296ee0-d6dc-4612-838f-a1bd2fa5b1d5&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 DOM이 클수록 처음에 해당 페이지를 렌더링하고 나중에 렌더링을 업데이트하는데 &lt;u&gt;더 많은 비용 (Recalucate Style, Pre-Paint 등)이 든다.&lt;/u&gt;&lt;/p&gt;
&lt;ul id=&quot;4fac74af-e956-4ff1-a994-f6c5ee064c68&quot; style=&quot;list-style-type: disc; color: #37352f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;요소 삽입&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;요소 삭제&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;스타일 수 정리&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;546b1f6f-b8c3-433f-bffe-d1924f3bf67c&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;DOM 사이즈 측정하기&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Lighthouse 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행하면 현재 페이지의 DOM에 대한 통계가 '진단' 제목 아래의 '과도한 DOM 크기 방지' 감사에 표시된다. &lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.chrome.com/en/docs/lighthouse/performance/dom-size/#how-the-lighthouse-dom-size-audit-fails&quot;&gt;Lighthouse에 따르면&lt;/a&gt; 페이지의 DOM 크기는 노드가 1,400개를 초과하면 과도한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. JavaScript 콘솔 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서는 DOM의 HTML 요소 수만 포함되어 있습니다 . DOM의 모든 노드를 포함하지는 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1704872177144&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.querySelectorAll('*').length;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. Dev Tool 성능 모니터 도구 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 도구를 사용하면 현재 DOM 크기와 함께 레이아웃 및 스타일 작업(및 기타 성능 측면)을 연관시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;h2 id=&quot;f6ee3ab0-fdeb-46f5-9e11-b0c7b7545eea&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;DOM 크기를 어떻게 줄일 수 있나요?&lt;/h2&gt;
&lt;ul id=&quot;9948de86-560f-4691-80d8-b8bc68c872b0&quot; style=&quot;list-style-type: disc; color: #37352f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;불필요한 마크업 줄이기&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;DOM 깊이를 줄이기&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Fragment 사용&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;모던 레이아웃 css 사용(flexbox, grid)&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;d3a93350-e3cd-4ec5-9b1c-12b5bba1c75b&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;0281d28b-92e1-4dee-bd8c-be723983de42&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;900b10ec-0639-4645-bbf4-306ac2e3aff3&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;로드 측정 하기&lt;/h2&gt;
&lt;h4 id=&quot;2ad11f03-50af-441d-a1f2-cb0175de249b&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;조건&lt;/h4&gt;
&lt;ul id=&quot;11bc610d-f4b5-4712-9386-c5b9e66f384d&quot; style=&quot;list-style-type: disc; color: #37352f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;DOM요소 깊이를 줄이기 전 후를 측정했다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;인피니티 스크롤이 포함된 페이지였는데 우선 스크롤을 하지 않은 첫 로드만 측정했다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;로컬에서 측정한 것이라&amp;hellip; 속도는 실제 운영과 다를 수 있습니다. &lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;성능 통계, lighthouse로 측정했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAdD1y/btsDkQ5Hlhs/KSIuvCV7cD2lnEOEoOz5vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAdD1y/btsDkQ5Hlhs/KSIuvCV7cD2lnEOEoOz5vK/img.png&quot; data-alt=&quot;데브툴에서 페이즈 로드 측정하는 방법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAdD1y/btsDkQ5Hlhs/KSIuvCV7cD2lnEOEoOz5vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAdD1y%2FbtsDkQ5Hlhs%2FKSIuvCV7cD2lnEOEoOz5vK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;653&quot; height=&quot;320&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데브툴에서 페이즈 로드 측정하는 방법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 id=&quot;25c098d0-c477-4684-b93a-bcf1f0d9eb11&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;결과&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로딩한 Insight를 보면 생각보다 많이 감소됨을 확인할 수 있었다. 1초대 미만으로 줄였지만 이미지가 엄청 크거나 양이 많아지면 이 갭은 훨씬 더 커지게 될 것이다. 간단한 컴포넌트라도 DOM의 깊어지지 않도록 설계 및 스타일하는 것이 매우 중요한 부분이라는 것을 알게 되었다.&lt;/p&gt;
&lt;ul id=&quot;6ab2cf33-8cb4-4318-bdc4-523b64f4cc24&quot; style=&quot;list-style-type: disc; color: #37352f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;FCP&lt;/b&gt; : 3.23s &amp;rarr; 2.7s &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;(0.53s 감소! ✌️)&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;LCP&lt;/b&gt; : 7.56s &amp;rarr; 6.8s &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;span style=&quot;color: #37352f; text-align: left;&quot;&gt;(0.76s 감소! &lt;span style=&quot;color: #37352f; text-align: left;&quot;&gt;✌️&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;DOM의 HTML&amp;nbsp;요소 수&lt;/b&gt; : 2847 &amp;rarr; 2104&lt;/li&gt;
&lt;/ul&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;Untitled 2.png&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;40&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddVU7q/btsDhxGjS6E/KEr4tiVppWHKxLzr2kTf9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddVU7q/btsDhxGjS6E/KEr4tiVppWHKxLzr2kTf9k/img.png&quot; data-alt=&quot;DOM 최적화 전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddVU7q/btsDhxGjS6E/KEr4tiVppWHKxLzr2kTf9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FddVU7q%2FbtsDhxGjS6E%2FKEr4tiVppWHKxLzr2kTf9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;277&quot; height=&quot;40&quot; data-filename=&quot;Untitled 2.png&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DOM 최적화 전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;297&quot; data-origin-height=&quot;35&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n92gm/btsDdIBVnZ5/E4YaPiEcsd2wHkoaprNcX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n92gm/btsDdIBVnZ5/E4YaPiEcsd2wHkoaprNcX1/img.png&quot; data-alt=&quot;DOM 최적화 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n92gm/btsDdIBVnZ5/E4YaPiEcsd2wHkoaprNcX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn92gm%2FbtsDdIBVnZ5%2FE4YaPiEcsd2wHkoaprNcX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;297&quot; height=&quot;35&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;297&quot; data-origin-height=&quot;35&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DOM 최적화 후&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;1924&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ohyqm/btsDkC7GXKZ/xg6yiobB3clWJPKopcevsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ohyqm/btsDkC7GXKZ/xg6yiobB3clWJPKopcevsk/img.png&quot; data-alt=&quot;DOM 최적화 전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ohyqm/btsDkC7GXKZ/xg6yiobB3clWJPKopcevsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fohyqm%2FbtsDkC7GXKZ%2Fxg6yiobB3clWJPKopcevsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1924&quot; height=&quot;1000&quot; data-filename=&quot;Untitled 3.png&quot; data-origin-width=&quot;1924&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DOM 최적화 전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;1933&quot; data-origin-height=&quot;999&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N6QuD/btsDkTOTUot/nlZtk3OKGHqUVXm5vU3VwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N6QuD/btsDkTOTUot/nlZtk3OKGHqUVXm5vU3VwK/img.png&quot; data-alt=&quot;DOM 최적화 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N6QuD/btsDkTOTUot/nlZtk3OKGHqUVXm5vU3VwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN6QuD%2FbtsDkTOTUot%2FnlZtk3OKGHqUVXm5vU3VwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1933&quot; height=&quot;999&quot; data-filename=&quot;Untitled 4.png&quot; data-origin-width=&quot;1933&quot; data-origin-height=&quot;999&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DOM 최적화 후&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;f89ec623-4960-4045-a973-1e13a25f1247&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p id=&quot;b9ccee81-e7ae-4dc7-a978-84a502dc553e&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://web.dev/dom-size-and-interactivity/&quot;&gt;https://web.dev/dom-size-and-interactivity/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1704873236394&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;큰 DOM 크기가 상호작용에 미치는 영향과 이에 대해 취할 수 있는 조치 &amp;nbsp;|&amp;nbsp; Articles &amp;nbsp;|&amp;nbsp; web.dev&quot; data-og-description=&quot;큰 DOM 크기는 상호작용이 빠른지 여부에 영향을 미칠 수 있습니다. DOM 크기와 INP의 관계, DOM 크기를 줄이기 위해 취할 수 있는 조치 및 페이지에 DOM 요소가 많은 경우 렌더링 작업을 제한하는 다&quot; data-og-host=&quot;web.dev&quot; data-og-source-url=&quot;https://web.dev/dom-size-and-interactivity/&quot; data-og-url=&quot;https://web.dev/articles/dom-size-and-interactivity?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wMMOL/hyU2pwQEor/RkqOiC6IZK9EDRfHz1CJI0/img.png?width=1081&amp;amp;height=864&amp;amp;face=0_0_1081_864,https://scrap.kakaocdn.net/dn/uzSqq/hyU2qWPRZO/2kJLCcy0pbPoSdrFnf8OU1/img.png?width=1353&amp;amp;height=462&amp;amp;face=0_0_1353_462,https://scrap.kakaocdn.net/dn/blJYGD/hyU2jKaIBi/rc7xg3mov8LWU9bE3dPRxk/img.png?width=891&amp;amp;height=276&amp;amp;face=0_0_891_276&quot;&gt;&lt;a href=&quot;https://web.dev/dom-size-and-interactivity/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://web.dev/dom-size-and-interactivity/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wMMOL/hyU2pwQEor/RkqOiC6IZK9EDRfHz1CJI0/img.png?width=1081&amp;amp;height=864&amp;amp;face=0_0_1081_864,https://scrap.kakaocdn.net/dn/uzSqq/hyU2qWPRZO/2kJLCcy0pbPoSdrFnf8OU1/img.png?width=1353&amp;amp;height=462&amp;amp;face=0_0_1353_462,https://scrap.kakaocdn.net/dn/blJYGD/hyU2jKaIBi/rc7xg3mov8LWU9bE3dPRxk/img.png?width=891&amp;amp;height=276&amp;amp;face=0_0_891_276');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;큰 DOM 크기가 상호작용에 미치는 영향과 이에 대해 취할 수 있는 조치 &amp;nbsp;|&amp;nbsp; Articles &amp;nbsp;|&amp;nbsp; web.dev&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;큰 DOM 크기는 상호작용이 빠른지 여부에 영향을 미칠 수 있습니다. DOM 크기와 INP의 관계, DOM 크기를 줄이기 위해 취할 수 있는 조치 및 페이지에 DOM 요소가 많은 경우 렌더링 작업을 제한하는 다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;web.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Developer/React</category>
      <category>DOM</category>
      <category>리액트</category>
      <category>성능최적화</category>
      <category>페이지로드시간줄이기</category>
      <author>jaddong</author>
      <guid isPermaLink="true">https://jaddong.tistory.com/89</guid>
      <comments>https://jaddong.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-DOM-%EC%82%AC%EC%9D%B4%EC%A6%88-%EC%A4%84%EC%9D%B4%EB%8A%94-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95#entry89comment</comments>
      <pubDate>Wed, 10 Jan 2024 16:57:38 +0900</pubDate>
    </item>
    <item>
      <title>사파리에서 webm 영상 백그라운드가 투명하게 나오지 않는 이슈</title>
      <link>https://jaddong.tistory.com/entry/%EC%82%AC%ED%8C%8C%EB%A6%AC%EC%97%90%EC%84%9C-webm-%EC%98%81%EC%83%81-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C%EA%B0%80-%ED%88%AC%EB%AA%85%ED%95%98%EA%B2%8C-%EB%82%98%EC%98%A4%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%8A%88</link>
      <description>&lt;p style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웹에서 비디오 최적화를 위해서는 webm 확장자를 사용하는 것이 좋았다. 하지만 사파리 브라우저에서 자동 재생 이슈와 배경이 투명한 영상이 정상적으로 보이지 않고 까맣게 백그라운드 처리가 되는 이슈를 발견해서 처리하는 과정을 설명해봤다.&lt;/p&gt;
&lt;p style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;503562dd-125e-4994-9054-6eaa75b4aaba&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;비디오 최적화하기&lt;/h2&gt;
&lt;p id=&quot;3b08d6f5-2a65-44db-9452-6aaf33d14dd5&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하나의 소스를 제공하는 것이 아니라, &lt;span&gt;최적화를 위해 여러 영상을 제공하고 브라우저가 렌더링 할 수 있는 영상을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;b6f5648f-5624-4486-8d8f-9b13163933bf&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래의 예시에서는 브라우저가 webm 영상을 렌더링할 수 있는 경우 선택한 영상 파일이다. 그렇지 않으면 다음 요소로 이동하여 두 번째 요소는 mp4 형식의 영상을 가리키게 된다 . 브라우저가 mp4 영상을 렌더링할 수 있는 경우 해당 영상 파일을 사용하고 webm의 용량이 mp4보다 훨씬 작아 우선순위가 높기 때문에 먼저 선언해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1704361505775&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;video controls&amp;gt;
  &amp;lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/video-and-source-tags/chrome.webm&quot; type=&quot;video/webm&quot;&amp;gt;
  &amp;lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/video-and-source-tags/chrome.mp4&quot; type=&quot;video/mp4&quot;&amp;gt;
  &amp;lt;p&amp;gt;Your browser cannot play the provided video file.&amp;lt;/p&amp;gt;
&amp;lt;/video&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;c11b1910-3538-419e-8240-c881bfdf72e7&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;자동 재생 정책(&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide&quot;&gt;링크&lt;/a&gt;)&lt;/h2&gt;
&lt;p id=&quot;1469333f-5055-46b6-9d6e-9ab240c84992&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용자의 관점에서 경고 없이 자발적으로 소음을 내기 시작하는 웹 페이지나 앱은 거슬리거나 불편하거나 불쾌할 수 있다. 그 때문에 브라우저는 일반적으로 특정 상황에서만 자동 재생이 성공적으로 발생하도록 허용한다.&lt;/p&gt;
&lt;p id=&quot;6353b6ec-82ec-4130-9835-f46816e75ae7&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 다음 중 하나 이상 이 참인 경우에만 미디어 자동 재생이 허용된다고 가정할 수 있다 .&lt;/p&gt;
&lt;ul id=&quot;2addbc27-e3d0-4355-8c07-f70459931996&quot; style=&quot;list-style-type: disc; color: #37352f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;오디오가 음소거되었거나 볼륨이 0으로 설정되어 있는 경우&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;사용자가 사이트와 상호작용 하는 경우(클릭, 탭, 키 누르기 등).&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;사이트가 허용된 경우&amp;nbsp;이것은 브라우저가 사용자가 미디어를 자주 사용한다고 판단하는 경우, 자동으로 발생하거나 기본 설정 또는 기타 사용자 인터페이스 기능을 통해 수동으로 발생 가능.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;자동&amp;nbsp;재생 권한 정책을&amp;lt;iframe&amp;gt;을 사용하여 문서에 자동 재생 지원을 부여하는 경우.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;9ae7d6d7-3b56-4c3e-9f77-b9b6d5207a6c&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;사파리 브라우저 이슈&lt;/h2&gt;
&lt;p data-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 style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 사파리에서 webm을 불러올 경우 백그라운드가 투명하게 되지 않는 경우, mp4를 불러오면 배경이 투명한 정상적으로 영상 재생됨.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버전 16.5.1(18615.2.9.11.7)&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;8b88729b-3120-4870-897f-9c8b9fda35e6&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 사파리에서 webm이든 mp4든&amp;nbsp;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.&lt;/span&gt;&amp;nbsp;에러가 나면서 아예 영상이 자동 재생되지 않고 이미지만 보이는 경우 &amp;rarr; 사파리 정책에서 자동재생을 막고 있음&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버전 15.5(17613.2.7.1.8)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;버전 16.3(18614.4.6.1.6)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;e540a586-c6a0-4429-b397-14f3ae769c7c&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;해결방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완벽하게 처리는 될 수 없었지만, 최대한 &lt;u&gt;webm을 로드할 수 있도록&lt;/u&gt; + &lt;u&gt;유저입장에서 불편하지 않도록&lt;/u&gt; 하는 방향으로 아래와 같이 처리했다.&lt;/p&gt;
&lt;ol id=&quot;8b0a23d8-b652-40d7-a400-cbebeafeef5e&quot; style=&quot;list-style-type: decimal; color: #37352f; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;영상은 webm과 mp4를 기본적으로 제공하는 걸로 했다.&lt;/li&gt;
&lt;li&gt;webm를 소스를 우선적으로 불러와 재생한다. 최적화를 위해!&lt;/li&gt;
&lt;li&gt;브라우저를 detect하여 mac OS + safari의 경우 mp4소스를 우선적으로 불러와 재생하도록 한다. (영상은 webm에 비해 무겁겠지만 어쩔 수 없었다ㅠㅠ)&lt;/li&gt;
&lt;li&gt;2번 혹은 3번에서 play에 에러가 있으면 영상없이 대체 이미지가 fallback 되도록 했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;b8db09c6-3886-4a09-a718-0e5b412f9957&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;출처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1704362313451&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Autoplay guide for media and Web Audio APIs - Web media technologies | MDN&quot; data-og-description=&quot;Automatically starting the playback of audio (or videos with audio tracks) immediately upon page load can be an unwelcome surprise to users. While autoplay of media serves a useful purpose, it should be used carefully and only when needed. In order to give&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bPZn1S/hyUXQO3bji/GPVMRhQKQHjqgvdTdJHGD1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bPZn1S/hyUXQO3bji/GPVMRhQKQHjqgvdTdJHGD1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Autoplay guide for media and Web Audio APIs - Web media technologies | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Automatically starting the playback of audio (or videos with audio tracks) immediately upon page load can be an unwelcome surprise to users. While autoplay of media serves a useful purpose, it should be used carefully and only when needed. In order to give&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1704362308680&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;&amp;lt;video&amp;gt;: The Video Embed element - HTML: HyperText Markup Language | MDN&quot; data-og-description=&quot;The &amp;lt;video&amp;gt; HTML element embeds a media player which supports video playback into the document. You can use &amp;lt;video&amp;gt; for audio content as well, but the &amp;lt;audio&amp;gt; element may provide a more appropriate user experience.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bDbzMJ/hyUXMslpqs/gf2XD2Vzhw3aldEwPntlvK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bDbzMJ/hyUXMslpqs/gf2XD2Vzhw3aldEwPntlvK/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;video&amp;gt;: The Video Embed element - HTML: HyperText Markup Language | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The &amp;lt;video&amp;gt; HTML element embeds a media player which supports video playback into the document. You can use &amp;lt;video&amp;gt; for audio content as well, but the &amp;lt;audio&amp;gt; element may provide a more appropriate user experience.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Developer/Web</category>
      <author>jaddong</author>
      <guid isPermaLink="true">https://jaddong.tistory.com/88</guid>
      <comments>https://jaddong.tistory.com/entry/%EC%82%AC%ED%8C%8C%EB%A6%AC%EC%97%90%EC%84%9C-webm-%EC%98%81%EC%83%81-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C%EA%B0%80-%ED%88%AC%EB%AA%85%ED%95%98%EA%B2%8C-%EB%82%98%EC%98%A4%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%8A%88#entry88comment</comments>
      <pubDate>Thu, 4 Jan 2024 18:56:49 +0900</pubDate>
    </item>
    <item>
      <title>리액트에서 라이브러리없이 간단하게 이미지 최적화하기</title>
      <link>https://jaddong.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%97%86%EC%9D%B4-%EA%B0%84%EB%8B%A8%ED%95%98%EA%B2%8C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #444447; text-align: start;&quot;&gt;서비스가 심미적이나 기능적으로 모두 구현되었다고 끝이 아님을 대부분 개발자들이 알고 있을 것이라 생각한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;그중에&lt;span style=&quot;background-color: #ffffff; color: #444447; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;꼭 짚고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;넘어가야 할&lt;span style=&quot;background-color: #ffffff; color: #444447; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;것이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;어셋들(image,&lt;span style=&quot;background-color: #ffffff; color: #444447; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;video, sound 등)에 대한 성능 최적화 작업이다. 성능 최적화 작업은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;프론트엔드,&lt;span style=&quot;background-color: #ffffff; color: #444447; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;벡엔드,&lt;span style=&quot;background-color: #ffffff; color: #444447; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;디자이너 단에서 모두 각자의 방식으로 작업이 가능하나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;그중&lt;span style=&quot;background-color: #ffffff; color: #444447; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;나는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;프론트엔드&lt;span style=&quot;background-color: #ffffff; color: #444447; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;쪽에서 작업한 경험을 간단하게&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;정리해 보았다.&lt;/p&gt;
&lt;h2 id=&quot;e1766ab6-2400-4bbb-8495-12228c685182&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;이미지 최적화하기&lt;/h2&gt;
&lt;h3 id=&quot;855a2a84-e9eb-4a08-8ef4-a3b348a45570&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;svg&lt;/b&gt;&lt;/h3&gt;
&lt;p id=&quot;30f4b402-f880-4104-8738-0e9d0106601f&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;svg 이미지는 sources로 사용하는 것보다 svg그대로 사용하는 것이 성능에 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1704264765536&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ReactComponent as Logo } from &quot;assets/icons/logo_bg.svg&quot;;

const LogoComponent = () =&amp;gt; {
  return (
      &amp;lt;Logo title=&quot;logo image&quot; /&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;cacbc718-d8a4-414a-b1be-8b33d28d91f9&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;sources&lt;/b&gt;&lt;/h3&gt;
&lt;p id=&quot;95a68057-70ab-4cba-966f-0d98f1eebf70&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하나의 소스를 제공하는 것이 아니라, &lt;span&gt;최적화를 위해 여러 이미지는 제공하고 브라우저가 렌더링 할 수 있는 이미지를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;8331f313-1f1a-4c57-b80e-8efcd5860278&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래의 예시에서는 브라우저가 AVIF 이미지를 렌더링할 수 있는 경우 선택한 이미지 파일이다. 그렇지 않으면 다음 요소로 이동해 두 번째 요소는 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://web.dev/serve-images-webp/&quot;&gt;WebP 형식&lt;/a&gt;source 의 이미지를 가리키게 된다. 브라우저가 WebP 이미지를 렌더링할 수 있는 경우 해당 이미지 파일을 사용하고 그렇지 않으면 img에 있는 이미지 파일(JPEG)로 대체된다.&lt;/p&gt;
&lt;pre id=&quot;code_1704264915413&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;picture&amp;gt;
  &amp;lt;source srcset=&quot;image.avif&quot; type=&quot;image/avif&quot;&amp;gt;
  &amp;lt;source srcset=&quot;image.webp&quot; type=&quot;image/webp&quot;&amp;gt;
  &amp;lt;img src=&quot;image.jpg&quot; alt=&quot;A description of the image.&quot; 
    width=&quot;300&quot; height=&quot;200&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&amp;gt;
&amp;lt;/picture&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;WebP 파일이 PNG 파일보다 26% 작고 JPEG 파일보다 최대 34% 작기 때문에 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://themeisle.com/blog/speed-up-wordpress-quick-wins/&quot;&gt;매우 빠른 로딩 시간을 제공한다.&lt;/a&gt;&lt;/p&gt;
&lt;blockquote id=&quot;b2437e47-4712-43eb-ab40-01ba2c918bf1&quot; data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://caniuse.com/&quot;&gt;Can I Use&lt;/a&gt; 에 따른 글로벌 WebP 및 AVIF 지원 수치는 다음과 같다.&lt;br /&gt;- WebP : 웹 사용자의 약 96.30%가 WebP를 지원하는 브라우저를 사용하고 있습니다.
&lt;p data-ke-size=&quot;size16&quot;&gt;- AVIF : 웹 사용자의 약 79.81%가 AVIF를 지원하는 브라우저를 사용하고 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;48f5f80d-2726-42d5-95bf-7d0f8d46d42b&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;lazy&lt;/b&gt;&lt;/h3&gt;
&lt;p id=&quot;43ed482b-c4d8-4ea1-ad76-f25d925585d5&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;true로 설정하면 페이지에서의 위치에 관계없이 리소스를 즉시 로드하지 않고 뷰포트로부터&amp;nbsp;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#distance-from-viewport-thresholds&quot;&gt;계산된 거리&lt;/a&gt;에 도달할 때까지 리소스 로딩을 지연시킨다. 스크롤 해야 보이는 이미지의 경우는 lazy를 처리하는 것이 첫 로딩에 부하를 줄일 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1704266195056&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;image.png&quot; loading=&quot;lazy&quot; alt=&quot;&amp;hellip;&quot; width=&quot;200&quot; height=&quot;200&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;a href=&quot;https://storage.googleapis.com/web-dev-assets/native-lazy-loading/lazyload.webm&quot;&gt;https://storage.googleapis.com/web-dev-assets/native-lazy-loading/lazyload.webm&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;f3cbec15-fbcb-40c9-879a-14a2370037db&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;srcSet&lt;/b&gt;&lt;/h3&gt;
&lt;p id=&quot;afc60602-8b55-449d-ac5e-933145ed575e&quot; style=&quot;color: #37352f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;디스플레이 사양(1x, 2x, 3x) 에 따라 &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;w 디스크립터(Width descriptor)를 사용해 포트 너비에 최적화된 이미지를 선택해 출력할 수 있다. &lt;/span&gt;다른 해상도의 이미지를 각각 제공하는 것이 시각적으로도 좋고 데이터 낭비도 줄여서 유저 경험에 긍정적인 효과를 줄 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #37352f; text-align: start;&quot; 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;Untitled.png&quot; data-origin-width=&quot;521&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JHe5O/btsCZ80J42Y/EvL5lyucOx2ODuCdTDTNWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JHe5O/btsCZ80J42Y/EvL5lyucOx2ODuCdTDTNWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JHe5O/btsCZ80J42Y/EvL5lyucOx2ODuCdTDTNWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJHe5O%2FbtsCZ80J42Y%2FEvL5lyucOx2ODuCdTDTNWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;521&quot; height=&quot;88&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;521&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 확장자와 dpr에 따른 이미지가 필요하다면 아래와 같이 구현이 가능하다.&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;Untitled 1.png&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lPo2L/btsC35a8dD8/BWUEbCVePp1LT7nuGj24x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lPo2L/btsC35a8dD8/BWUEbCVePp1LT7nuGj24x0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lPo2L/btsC35a8dD8/BWUEbCVePp1LT7nuGj24x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlPo2L%2FbtsC35a8dD8%2FBWUEbCVePp1LT7nuGj24x0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;120&quot; data-filename=&quot;Untitled 1.png&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;120&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;위의 이미지처럼 html 코드가 길어지기 때문에 공통적으로 사용할 수 있는 image 컴포넌트를 따로 만들어 구현하는 것이 서비스 전반적으로 적용하기에 훨씬 나았어서 구현했던 코드를 간단히 공유해본다. -&amp;gt; &lt;a href=&quot;https://github.com/lookatourdesignsystem/global-designsystem/blob/dev/packages/global-designsystem/src/stories/components/Image/index.tsx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub Link&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1704264742133&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import ImagePng from &quot;assets/images/fallback.png&quot;;
import ImageAvif1x from &quot;assets/images/image_1.avif&quot;;
import ImageAvif2x from &quot;assets/images/image_2.avif&quot;;
import ImageAvif3x from &quot;assets/images/image_3.avif&quot;;
import ImageWebp1x from &quot;assets/images/image_1.webp&quot;;
import ImageWebp2x from &quot;assets/images/image_2.webp&quot;;
import ImageWebp3x from &quot;assets/images/image_3.webp&quot;;


const MainImage = () =&amp;gt; {
  return (
      &amp;lt;Image
        src={ImagePng} // fallback 이미지
        sources={[
          { srcSet: `${ImageAvif1x} 1x, ${ImageAvif2x} 2x, ${ImageAvif3x} 3x`, type: &quot;avif&quot; },
          { srcSet: `${ImageWebp1x} 1x, ${ImageWebp2x} 2x, ${ImageWebp3x} 3x`, type: &quot;webp&quot; },
        ]}
        width=&quot;100%&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;/p&gt;
&lt;h1 id=&quot;76805281-0785-448d-bcfa-33105228c773&quot; style=&quot;color: #37352f; text-align: start;&quot;&gt;출처&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;a href=&quot;https://web.dev/learn/design/picture-element/#image-formats&quot;&gt;https://web.dev/learn/design/picture-element/#image-formats&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;a href=&quot;https://themeisle.com/blog/avif-vs-webp&quot;&gt;https://themeisle.com/blog/avif-vs-webp&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/&quot;&gt;https://web.dev/browser-level-image-lazy-loading/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1704362280377&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;웹용 브라우저 수준 이미지 지연 로드 &amp;nbsp;|&amp;nbsp; Articles &amp;nbsp;|&amp;nbsp; web.dev&quot; data-og-description=&quot;이 게시물에서는 로드 속성 및 이 속성을 사용하여 이미지 로드를 제어하는 방법을 다룹니다.&quot; data-og-host=&quot;web.dev&quot; data-og-source-url=&quot;https://web.dev/browser-level-image-lazy-loading/&quot; data-og-url=&quot;https://web.dev/articles/browser-level-image-lazy-loading?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tA6PM/hyUXQIhycA/WCimKgldEAENjq3UrnfXSk/img.png?width=1600&amp;amp;height=919&amp;amp;face=0_0_1600_919,https://scrap.kakaocdn.net/dn/eBFlN/hyUXTSx1v3/z0Wkfxy3os4HPpEQS4ILgk/img.png?width=1600&amp;amp;height=710&amp;amp;face=0_0_1600_710&quot;&gt;&lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://web.dev/browser-level-image-lazy-loading/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tA6PM/hyUXQIhycA/WCimKgldEAENjq3UrnfXSk/img.png?width=1600&amp;amp;height=919&amp;amp;face=0_0_1600_919,https://scrap.kakaocdn.net/dn/eBFlN/hyUXTSx1v3/z0Wkfxy3os4HPpEQS4ILgk/img.png?width=1600&amp;amp;height=710&amp;amp;face=0_0_1600_710');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;웹용 브라우저 수준 이미지 지연 로드 &amp;nbsp;|&amp;nbsp; Articles &amp;nbsp;|&amp;nbsp; web.dev&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 게시물에서는 로드 속성 및 이 속성을 사용하여 이미지 로드를 제어하는 방법을 다룹니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;web.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1704362273310&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;사진 요소 &amp;nbsp;|&amp;nbsp; web.dev&quot; data-og-description=&quot;이미지를 더욱 창의적으로 관리할 수 있습니다.&quot; data-og-host=&quot;web.dev&quot; data-og-source-url=&quot;https://web.dev/learn/design/picture-element/#image-formats&quot; data-og-url=&quot;https://web.dev/learn/design/picture-element?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hkPmv/hyUXXADxl6/e7qiy0xRkvAAkJQMVqNtk0/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/daclOg/hyUXOcEoum/NAt3eC2KmgBiNouvKEOuh0/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675&quot;&gt;&lt;a href=&quot;https://web.dev/learn/design/picture-element/#image-formats&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://web.dev/learn/design/picture-element/#image-formats&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hkPmv/hyUXXADxl6/e7qiy0xRkvAAkJQMVqNtk0/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/daclOg/hyUXOcEoum/NAt3eC2KmgBiNouvKEOuh0/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;사진 요소 &amp;nbsp;|&amp;nbsp; web.dev&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이미지를 더욱 창의적으로 관리할 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;web.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1704362266536&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;AVIF vs WebP: Which Image Format Reigns Supreme in 2023?&quot; data-og-description=&quot;AVIF and WebP are next-gen formats that reduce file sizes. However, you might not know which to choose. Here's our guide to AVIF vs WebP!&quot; data-og-host=&quot;themeisle.com&quot; data-og-source-url=&quot;https://themeisle.com/blog/avif-vs-webp&quot; data-og-url=&quot;https://themeisle.com/blog/avif-vs-webp/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/l9TzU/hyUXZFdz6I/8JNHBI9XAncjQ7qwzRBABK/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/62Jw8/hyUXTdV80p/gK76NMxmHpF9kGrlkkeNE1/img.png?width=2000&amp;amp;height=961&amp;amp;face=0_0_2000_961&quot;&gt;&lt;a href=&quot;https://themeisle.com/blog/avif-vs-webp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://themeisle.com/blog/avif-vs-webp&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/l9TzU/hyUXZFdz6I/8JNHBI9XAncjQ7qwzRBABK/img.png?width=1920&amp;amp;height=960&amp;amp;face=0_0_1920_960,https://scrap.kakaocdn.net/dn/62Jw8/hyUXTdV80p/gK76NMxmHpF9kGrlkkeNE1/img.png?width=2000&amp;amp;height=961&amp;amp;face=0_0_2000_961');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;AVIF vs WebP: Which Image Format Reigns Supreme in 2023?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;AVIF and WebP are next-gen formats that reduce file sizes. However, you might not know which to choose. Here's our guide to AVIF vs WebP!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;themeisle.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul id=&quot;969926cd-db9e-4351-bb0d-07eca899f0e2&quot; style=&quot;list-style-type: disc; color: #37352f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;/ul&gt;</description>
      <category>Developer/React</category>
      <category>리액트</category>
      <category>성능최적화</category>
      <category>이미지최적화</category>
      <author>jaddong</author>
      <guid isPermaLink="true">https://jaddong.tistory.com/87</guid>
      <comments>https://jaddong.tistory.com/entry/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%97%86%EC%9D%B4-%EA%B0%84%EB%8B%A8%ED%95%98%EA%B2%8C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0#entry87comment</comments>
      <pubDate>Wed, 3 Jan 2024 16:10:00 +0900</pubDate>
    </item>
    <item>
      <title>Throttle와 Debounce 개념 알고 상황에 맞게 쓰기</title>
      <link>https://jaddong.tistory.com/entry/Throttle%EC%99%80-Debounce-%EA%B0%9C%EB%85%90-%EC%95%8C%EA%B3%A0-%EC%83%81%ED%99%A9%EC%97%90-%EB%A7%9E%EA%B2%8C-%EC%93%B0%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Throttle 과 Debounce 의 필요성이 나온 이유&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 검색창에서 &quot;떡볶이&quot;를 검색할 때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타이핑을 할 때마다 텍스트 필드 하단에 입력한 검색어와 맞을 만한 추천 검색어를 하단에 보여주게된다. 이런 경우 매번 입력할 때마다 api를 호출해본다고 생각해보자. 사용자 경험으로는 추천 검색어를 바로 볼 수 있기 때문에 좋을 수는 있으나, &lt;b&gt;사용자의 의도와 무관한 요청이&lt;/b&gt; 계속 된다면 이것은 &lt;b&gt;성능적으로 매우 좋지 않은 결과&lt;/b&gt;가 있을 수 있다. 또한 api의 호출이 잦아 로딩이 길어진다면 사용자의 경험에도 안 좋은 영향을 줄 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNo5t8/btr1p1Kb3sM/B5H2DZmpNARs1kCk6aJCs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNo5t8/btr1p1Kb3sM/B5H2DZmpNARs1kCk6aJCs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNo5t8/btr1p1Kb3sM/B5H2DZmpNARs1kCk6aJCs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNo5t8%2Fbtr1p1Kb3sM%2FB5H2DZmpNARs1kCk6aJCs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;485&quot; height=&quot;115&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQdC4/btr06XpfSGC/RGAm0vwNb4U4dje48FvQBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQdC4/btr06XpfSGC/RGAm0vwNb4U4dje48FvQBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQdC4/btr06XpfSGC/RGAm0vwNb4U4dje48FvQBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQdC4%2Fbtr06XpfSGC%2FRGAm0vwNb4U4dje48FvQBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;484&quot; height=&quot;107&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b87ia9/btr1bkxGInx/FBdTrbUbVOkH1NYAb1slGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b87ia9/btr1bkxGInx/FBdTrbUbVOkH1NYAb1slGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b87ia9/btr1bkxGInx/FBdTrbUbVOkH1NYAb1slGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb87ia9%2Fbtr1bkxGInx%2FFBdTrbUbVOkH1NYAb1slGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;119&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rV3p8/btr1f06ttVa/XL5D7G9AeAsTt4y5KGzCS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rV3p8/btr1f06ttVa/XL5D7G9AeAsTt4y5KGzCS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rV3p8/btr1f06ttVa/XL5D7G9AeAsTt4y5KGzCS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrV3p8%2Fbtr1f06ttVa%2FXL5D7G9AeAsTt4y5KGzCS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;469&quot; height=&quot;121&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;Infinite scrolling page(무한 스크롤 페이지)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;사용자가 스크롤을 할 때마다 새로운 컨텐츠가 하단에 무한으로 보여지는 되는 UX는 요즘 어디서나 쉽게 찾아볼 수 있다.(트위터, 인스타그램 등)사용자가 스크롤을할 때 footer와의 거리를 계산해서 컨텐츠를 불러오는 온다고 생각해보자. 그렇다면 onScroll 이벤트가 계속 일어날때마다 &lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;footer와의 거리를 계산해서 api를 호출할 것인가? 이것 또한 &lt;b&gt;api호출이 무분별하게 많아질 수 있으므로 성능상이 이슈가 있을 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;3. 티켓팅 결제나 댓글을 등록할 때 등록 버튼을 여러번 연속으로 클릭한다면?&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212121;&quot;&gt;요즘은 거의 없겠지만 댓글을 등록을 할 때 등록을 여러번 클릭해 동일한 댓글이 두 번 달린 경험이 있을 것이다. 등록버튼을 클릭하고 바로 disabled하거나 막지 않으면 동일한 폼을 여러번 submit하게 되어 &lt;b&gt;의도와 다르게 여러번 게시될 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그래서 이런 문제점을 보완하고자 Throttle 과 Debounce개념이 나왔다. &lt;span&gt;이벤트가 발생할 때마다 function을 실행하게되면 &lt;b&gt;성능상이 이슈&lt;/b&gt;가 있으므로, 이벤트가 발생되는 동안&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;function이 계속 실행되지 않도록 일정한 시간을 주는 것을 말한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Throttle 과 Debounce 이란&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤 예제 하나로 아주 쉽게 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Nothing : 스크롤할 때마다 function이 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;b&gt;Throttle&lt;/b&gt; : 스크롤을 할 때 일정한 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;시간 간격을 두고&lt;span style=&quot;color: #4c4c4c;&quot;&gt; 정기적으로 function을 실행&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;b&gt;Debounce&lt;/b&gt; : 아무리 많은 스크롤해도&lt;span style=&quot;background-color: #ffffff; color: #4c4c4c;&quot;&gt; 모두 무시하고 특정 시간 사이에 어떤 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;스크롤도 발생하지 않았을 때 딱 한번만 마지막 function을 발생&lt;/span&gt;시키는 기법&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe style=&quot;width:100%; min-height:400px;&quot; src=&quot;https://codesandbox.io/embed/the-difference-between-throttling-debouncing-and-neither-gdxdwi?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Throttle 과 Debounce 원리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;loadsh에 있는 throttle과 debounce를 코드를 까보면 이런 식으로 구현되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;debounce의 경우 이벤트가 실행되었을 때 일정 시간을 기다렸다가 이벤트를 수행하도록 만들고, 일정 시간 내에 같은 이벤트가 또 들어오면 이전 요청을 취소하는 방식으로 구현한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function debounce(callback, delay) {
  let timer;
  return function () {
    clearTimeout(timer);
    timer = setTimeout(() =&amp;gt; {
      callback.apply(this, arguments);
    }, delay);
  };
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;throttle은 타이머가 없을 경우 타이머를 설정하고, 타이머가 있을 경우 아무런 동작도 하지 않도록 하여 일정 시간 이후에 이벤트가 1번 실행되도록 구현한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function throttle(callback, delay) {
  let timer;
  return function () {
    if (!timer) {
      timer = setTimeout(() =&amp;gt; {
        callback.apply(this, arguments);
      }, delay);
    }
  };
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Throttle 과 Debounce 를 언제 사용해야하나&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;throttle이나 debounce는 사실 정답이 없고 상황에 맞춰서 사용해야한다.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색창에 타이핑을 할 때 100ms 마다 자동완성검색창이 나왔으면 한다면 throttle을 사용해야하겠고, 어느정도 타이핑을 다 입력하고 이벤트가 멈췄을 때 자동완성이 보이는 걸 원한다면 debounce를 사용하면 되겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 보편적으로 쓰이고 있는 경우는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Throttle&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;u&gt;무한 스크롤 기능&lt;/u&gt; : SNS에서 자주 쓰이는 무한 스크롤 기능에서는 debounce보다 throttle이 적합하다. 유저가 스크롤링을 멈출 때까지 기다렸다가 새로운 글을 불러오는 것보다는 유저의 스크롤이 어느 정도 내려왔을 때 미리 글을 불러오는 것이 좀 더 좋은 경험을 제공할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;u&gt;브라우저 resize&lt;/u&gt;&amp;nbsp;: resize&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이벤트는 빈번하게 발생될 수 있기 때문에, 이벤트 핸들러는 DOM 수정과 같은 계산이 많이 필요한 연산을 실행하지 않아야 한다. 대신에 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b;&quot;&gt;이벤트를 스로틀(throttle) 하는것이 좋다. -&amp;gt; &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Window/resize_event#examples&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MDN 사이트&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;23a1&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Debounce&lt;/b&gt;&lt;/h3&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;- &lt;u&gt;toggle버튼&lt;/u&gt; : 알림설정 toggle 버튼은 수시로 쉽게 수정할 수 있다. 이 경우 toggle 이벤트 리스너에 어느 정도 debounce를 줘서 마지막으로 일어난 toggle의 요청만 처리하는 것이 유저에게 좋은 경험일 것이다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;- &lt;u&gt;자동완성 검색어&lt;/u&gt; : 한 글자씩 타이핑할 때마다 api를 호출하여 자동완성 검색을 하단에 바뀌도록 보여주는 것보다는 어느정도 단어가 완성된 후 자동완성을 제공해주는 것이 성능상에도 유저의 경험에도 더 적절할 것이다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;여담&lt;/h4&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;전에 회사에서 사내에서 사용하는 백오피스의 버튼에 debounce나 throttle같은 기능을 해놓지 않아 버그처럼 보였던 경험이 있다. 그리고 scroll에 따라 element의 스타일이 변경되는 작업도 해보았어서 이번 공부를 통해 이벤트 핸들링에 대해 더 예민하게 봐야할 필요가 있다는 생각이 들었다. debounce나 throttle는&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;&amp;nbsp;이벤트나 함수들의 실행되는 빈도를 줄여서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;성능상의 유리함&lt;/span&gt; 가져기도 하지만 &lt;/span&gt;&lt;span style=&quot;color: #292929; background-color: #f6e199;&quot;&gt;유저의 경험에도 직접적으로 영향&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;을 줄 수 있기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://webclub.tistory.com/607&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://webclub.tistory.com/607&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://css-tricks.com/debouncing-throttling-explained-examples/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://css-tricks.com/debouncing-throttling-explained-examples/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://pks2974.medium.com/throttle-%EC%99%80-debounce-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-2335a9c426ff&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://pks2974.medium.com/throttle-%EC%99%80-debounce-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-2335a9c426ff&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://medium.com/@kwoncharles/debounce%EC%99%80-throttle%EC%9D%98-%EC%B0%A8%EC%9D%B4-34845fbfa1ff&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/@kwoncharles/debounce%EC%99%80-throttle%EC%9D%98-%EC%B0%A8%EC%9D%B4-34845fbfa1ff&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1677765390914&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Throttle 와 Debounce 개념 정리하기&quot; data-og-description=&quot;Throttle 와 Debounce 라는 개념 을 알게 되어 이를 정리해보고자 한다.&quot; data-og-host=&quot;pks2974.medium.com&quot; data-og-source-url=&quot;https://pks2974.medium.com/throttle-%EC%99%80-debounce-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-2335a9c426ff&quot; data-og-url=&quot;https://pks2974.medium.com/throttle-%EC%99%80-debounce-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-2335a9c426ff&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pks2974.medium.com/throttle-%EC%99%80-debounce-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-2335a9c426ff&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pks2974.medium.com/throttle-%EC%99%80-debounce-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-2335a9c426ff&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Throttle 와 Debounce 개념 정리하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Throttle 와 Debounce 라는 개념 을 알게 되어 이를 정리해보고자 한다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pks2974.medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1677664661684&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Debouncing and Throttling Explained Through Examples | CSS-Tricks&quot; data-og-description=&quot;The following is a guest post by David Corbacho, a front end engineer in London. We've broached this topic before, but this time, David is going to drive the&quot; data-og-host=&quot;css-tricks.com&quot; data-og-source-url=&quot;https://css-tricks.com/debouncing-throttling-explained-examples/&quot; data-og-url=&quot;https://css-tricks.com/debouncing-throttling-explained-examples/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/XROEp/hyRNFIWdc1/04vkXiJ0jmq4M4ayO7wCi0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=106_403_149_449,https://scrap.kakaocdn.net/dn/baJAN4/hyRNHtdGwK/QxPtkKS0jU7S09vKrbF6Z0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=106_403_149_449,https://scrap.kakaocdn.net/dn/cQxECE/hyRNFIWdiJ/HHQvbhuTFK6ELgicyNVn71/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=106_403_149_449&quot;&gt;&lt;a href=&quot;https://css-tricks.com/debouncing-throttling-explained-examples/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://css-tricks.com/debouncing-throttling-explained-examples/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/XROEp/hyRNFIWdc1/04vkXiJ0jmq4M4ayO7wCi0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=106_403_149_449,https://scrap.kakaocdn.net/dn/baJAN4/hyRNHtdGwK/QxPtkKS0jU7S09vKrbF6Z0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=106_403_149_449,https://scrap.kakaocdn.net/dn/cQxECE/hyRNFIWdiJ/HHQvbhuTFK6ELgicyNVn71/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=106_403_149_449');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Debouncing and Throttling Explained Through Examples | CSS-Tricks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The following is a guest post by David Corbacho, a front end engineer in London. We've broached this topic before, but this time, David is going to drive the&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;css-tricks.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1677664640982&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;디바운스(Debounce)와 스로틀(Throttle ) 그리고 차이점&quot; data-og-description=&quot;Throttle, Debounce &amp;amp; Difference 스로틀(Throttle)과 디바운스(Debounce)란 무엇일까? 이 두 가지 방법 모두 DOM 이벤트를 기반으로 실행하는 자바스크립트를 성능상의 이유로 JS의 양적인 측면, 즉 이벤트(event)&quot; data-og-host=&quot;webclub.tistory.com&quot; data-og-source-url=&quot;https://webclub.tistory.com/607&quot; data-og-url=&quot;https://webclub.tistory.com/607&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b2fPlP/hyRNOMDQPK/fxyK2OgRVDmKaV1SK4KbyK/img.png?width=661&amp;amp;height=133&amp;amp;face=0_0_661_133,https://scrap.kakaocdn.net/dn/WRxUx/hyRNLvCpXh/Er3TYZHYUveYuW4DTdFEN1/img.png?width=661&amp;amp;height=133&amp;amp;face=0_0_661_133,https://scrap.kakaocdn.net/dn/iUMSo/hyRMnb3XqN/KVYXEa73CmPRSzC8v44of1/img.jpg?width=589&amp;amp;height=545&amp;amp;face=236_184_363_322&quot;&gt;&lt;a href=&quot;https://webclub.tistory.com/607&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://webclub.tistory.com/607&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b2fPlP/hyRNOMDQPK/fxyK2OgRVDmKaV1SK4KbyK/img.png?width=661&amp;amp;height=133&amp;amp;face=0_0_661_133,https://scrap.kakaocdn.net/dn/WRxUx/hyRNLvCpXh/Er3TYZHYUveYuW4DTdFEN1/img.png?width=661&amp;amp;height=133&amp;amp;face=0_0_661_133,https://scrap.kakaocdn.net/dn/iUMSo/hyRMnb3XqN/KVYXEa73CmPRSzC8v44of1/img.jpg?width=589&amp;amp;height=545&amp;amp;face=236_184_363_322');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;디바운스(Debounce)와 스로틀(Throttle ) 그리고 차이점&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Throttle, Debounce &amp;amp; Difference 스로틀(Throttle)과 디바운스(Debounce)란 무엇일까? 이 두 가지 방법 모두 DOM 이벤트를 기반으로 실행하는 자바스크립트를 성능상의 이유로 JS의 양적인 측면, 즉 이벤트(event)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;webclub.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Developer/Javascript</category>
      <author>jaddong</author>
      <guid isPermaLink="true">https://jaddong.tistory.com/86</guid>
      <comments>https://jaddong.tistory.com/entry/Throttle%EC%99%80-Debounce-%EA%B0%9C%EB%85%90-%EC%95%8C%EA%B3%A0-%EC%83%81%ED%99%A9%EC%97%90-%EB%A7%9E%EA%B2%8C-%EC%93%B0%EA%B8%B0#entry86comment</comments>
      <pubDate>Thu, 2 Mar 2023 22:23:56 +0900</pubDate>
    </item>
    <item>
      <title>rollup 빌드 결과 파일 cjs, esm 차이, package.json, rollup.config.js</title>
      <link>https://jaddong.tistory.com/entry/rollup-%EB%B2%88%EB%93%A4-%ED%8C%8C%EC%9D%BC-packagejson-cjs-esm-%EC%B0%A8%EC%9D%B4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 회사에서 디자인시스템을 모노레포로 만드는 작업을 하고 있다. 모노레포에 있는 각각의 라이브러리들을 rollup을 통해 번들링이되는데, 지금 모노레포와 rollup 관련해서 꽤나 삽질을 하고 있다. 그래도 배우는 것이 있어서 정리해보고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 rollup.config.js파일이다. 여기선 결과물의 경로나 format을 정할 수 있다 cjs로 선택되어 있는 상태이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675592599270&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// rollup.config.js
output: {
    dir: &quot;lib&quot;,
    format: &quot;cjs&quot;,
    exports: &quot;named&quot;,
  },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 나온 번들 결과물을 package.json에서 사용하겠다고 지정을 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;main은 이 라이브러리를 사용할 때 참조할 파일이고, types는 타입스크립트 파일이 되겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;그러면 module와 esm은 뭐고 왜 필요하지라는 궁금증이 생기게 되었다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675592717832&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// package.json
{
  ...
  &quot;main&quot;: &quot;lib/index.js&quot;,
  &quot;module&quot;: &quot;lib/index.esm.js&quot;,
  &quot;types&quot;: &quot;lib/index.d.ts&quot;,
  ...
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 우선 esm과 cjs의 차이를 알아보면 우선 코드상에서&amp;nbsp; import, export할 때의 차이가 있다는 걸 알 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리액트로 개발하는 개발자라면 esm이 눈에는 더 익숙할 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 외에도 차이가 다양하니 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://roseline.oopy.io/dev/translation-why-cjs-and-esm-cannot-get-along&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&gt; 자세히보면 되겠다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;CommonJs (CJS)&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1675592907022&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// util.js
module.exports = (x, y) =&amp;gt; x + y;

// main.js
const whateverWeWant = require('util');
console.log(whateverWeWant(2, 4));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ECMAScript Modules (ESM)&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1675592973380&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// util.js
export const sum = (x, y) =&amp;gt; x + y;

// main.js
import { sum } from 'util'
console.log(sum(2, 4));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 라이브러리의 빌드 결과로 나온 index.js 외에 index.esm.js는 왜 필요한 것인가?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;main&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;라이브러리 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;빌드 결과의 메인 파일&lt;/span&gt;로 어떤 프로젝트에서 이 라이브러리를 설치하게되면 index.js 파일을 참조하게된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;module&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; background-color: #f6e199;&quot;&gt;이 라이브러리를 설치한 프로젝트를 빌드할 때, index.esm.js를 참조해 빌드하게된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ems는 트리쉐이킹이 가능해 JavaScript 번들의 사이즈를 줄여서 렌더링이 중단되는 시간을 최소화는 효과가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 설명보다 자세히 예시를 들어보겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로젝트에서 아래와 같이 libraryA라는 라이브러리를 설치했다고하자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1675593659957&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Button } from &quot;libraryA&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;로컬에서 개발할 때 command나 ctrl을 누르상태로&amp;nbsp;libraryA 부분을 클릭하면 node_modules에 해당 라이브러리의 index.js파일을 가리키는 것을 확인할 수 있다. main에서 지정한 데로 index.js 파일에서 import가 된다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 import로 불러오든 require로 불러오든 상관이 없다! 자세한 건 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://pencilflip.medium.com/using-es-modules-with-commonjs-modules-in-node-js-1015786dab03&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&gt; 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1206&quot;&gt;&lt;a href=&quot;https://pencilflip.medium.com/using-es-modules-with-commonjs-modules-in-node-js-1015786dab03&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UMDBX/btrYdiHZTuB/HBhAviYk4yB7Wgnaf0II91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUMDBX%2FbtrYdiHZTuB%2FHBhAviYk4yB7Wgnaf0II91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;425&quot; height=&quot;366&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1206&quot;/&gt;&lt;/a&gt;&lt;figcaption&gt;This diagram summarizes the above rules.&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;span style=&quot;color: #000000;&quot;&gt;그런데 module에서 선택한 &lt;b&gt;esm의 경우는 개발할 때가 아닌 번들링할 때 참조가 이뤄진다.&lt;/b&gt; 프로젝트 완성 후, 완성한 프로젝트를 번들러로 빌드를 할때 libraryA의 package.json의 module에서 지정한 esm형식을 참고해 빌드가 이뤄진다. 그리고 esm은 트리쉐이킹 가능해서 빌드할 때 프로젝트에서 libraryA에서 사용한 것만 긁어서 빌드가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 경우 Button만 import했다면 Button만 골라서 빌드가 된다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;일반적으로 CommonJS형태는 트리쉐이킹을 할 수 없다. lodash를 예로 들면, lodash는 CommonJS 형태로 번들링되어 배포되기 때문에 webpack의 기본설정으로는 lodash를 트리쉐이킹 할 수 없다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;트리쉐이킹을 위해서는 babel-plugin-lodash을 사용하거나 lodash-es를 사용하는 것을 추천한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;i&gt;그래서 결론은&amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;라이브러리를 만들 때 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;cjs와 esm을 둘다 제공해주는 것이 좋고 &lt;/span&gt;&lt;i&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;esm버전이 프로젝트의 번들사이즈를 줄이는 데 도움을 줄 수 있다는 것이다.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;참고&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://roseline.oopy.io/dev/translation-why-cjs-and-esm-cannot-get-along&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://roseline.oopy.io/dev/translation-why-cjs-and-esm-cannot-get-along&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://pencilflip.medium.com/using-es-modules-with-commonjs-modules-in-node-js-1015786dab03&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://pencilflip.medium.com/using-es-modules-with-commonjs-modules-in-node-js-1015786dab03&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://maeng2418.github.io/development/library_deploy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://maeng2418.github.io/development/library_deploy/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://toss.tech/article/commonjs-esm-exports-field&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://toss.tech/article/commonjs-esm-exports-field&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675594869814&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;CommonJS와 ESM에 모두 대응하는 라이브러리 개발하기: exports field&quot; data-og-description=&quot;Node.js에는 두 가지 Module System이 존재합니다. 토스 프론트엔드 챕터에서 운영하는 100개가 넘는 라이브러리들은 그것에 어떻게 대응하고 있을까요?&quot; data-og-host=&quot;toss.tech&quot; data-og-source-url=&quot;https://toss.tech/article/commonjs-esm-exports-field&quot; data-og-url=&quot;https://toss.tech/article/commonjs-esm-exports-field&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5wJhr/hyRwvM7mxm/t2AOlN7oKBkvYvs2i26Be1/img.png?width=1500&amp;amp;height=1500&amp;amp;face=0_0_1500_1500&quot;&gt;&lt;a href=&quot;https://toss.tech/article/commonjs-esm-exports-field&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://toss.tech/article/commonjs-esm-exports-field&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5wJhr/hyRwvM7mxm/t2AOlN7oKBkvYvs2i26Be1/img.png?width=1500&amp;amp;height=1500&amp;amp;face=0_0_1500_1500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CommonJS와 ESM에 모두 대응하는 라이브러리 개발하기: exports field&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Node.js에는 두 가지 Module System이 존재합니다. 토스 프론트엔드 챕터에서 운영하는 100개가 넘는 라이브러리들은 그것에 어떻게 대응하고 있을까요?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;toss.tech&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1675594855273&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;라이브러리 배포 cjs? ejs?&quot; data-og-description=&quot;개요 언젠가 한번 나만의 라이브러리를 만들어서 배포해보고 싶다는 생각을 갖구있다가 이번에 디자인시스템 개발을 도전하게 되었다. 리액트 컴포넌트 기반으로 디자인시스템 개발에 대한 자&quot; data-og-host=&quot;maeng2418.github.io&quot; data-og-source-url=&quot;https://maeng2418.github.io/development/library_deploy/&quot; data-og-url=&quot;https://maeng2418.github.io/development/library_deploy/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bnHY6D/hyRwIlmqU1/XVUI10QKRWTswbjJaKf080/img.png?width=1200&amp;amp;height=365&amp;amp;face=0_0_1200_365,https://scrap.kakaocdn.net/dn/b4fA4y/hyRwDxAlhK/nPm6gbq52M6D7Vr39rzwg1/img.png?width=1032&amp;amp;height=420&amp;amp;face=0_0_1032_420,https://scrap.kakaocdn.net/dn/bY6HtP/hyRwBGxdf8/cUpumFRTkRxEvpVdCEdYE1/img.png?width=700&amp;amp;height=603&amp;amp;face=0_0_700_603&quot;&gt;&lt;a href=&quot;https://maeng2418.github.io/development/library_deploy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://maeng2418.github.io/development/library_deploy/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bnHY6D/hyRwIlmqU1/XVUI10QKRWTswbjJaKf080/img.png?width=1200&amp;amp;height=365&amp;amp;face=0_0_1200_365,https://scrap.kakaocdn.net/dn/b4fA4y/hyRwDxAlhK/nPm6gbq52M6D7Vr39rzwg1/img.png?width=1032&amp;amp;height=420&amp;amp;face=0_0_1032_420,https://scrap.kakaocdn.net/dn/bY6HtP/hyRwBGxdf8/cUpumFRTkRxEvpVdCEdYE1/img.png?width=700&amp;amp;height=603&amp;amp;face=0_0_700_603');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;라이브러리 배포 cjs? ejs?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요 언젠가 한번 나만의 라이브러리를 만들어서 배포해보고 싶다는 생각을 갖구있다가 이번에 디자인시스템 개발을 도전하게 되었다. 리액트 컴포넌트 기반으로 디자인시스템 개발에 대한 자&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;maeng2418.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1675594846790&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Using ES modules with CommonJS modules in Node.js&quot; data-og-description=&quot;This is not meant to be a comprehensive overview of ES modules and CommonJS modules &amp;mdash; I do not go into depth about what modules are and&amp;hellip;&quot; data-og-host=&quot;pencilflip.medium.com&quot; data-og-source-url=&quot;https://pencilflip.medium.com/using-es-modules-with-commonjs-modules-in-node-js-1015786dab03&quot; data-og-url=&quot;https://pencilflip.medium.com/using-es-modules-with-commonjs-modules-in-node-js-1015786dab03&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DZ9OS/hyRvbbCUJq/yGuFIBQsFuqhbJB3Wd5aI1/img.png?width=1200&amp;amp;height=734&amp;amp;face=0_0_1200_734&quot;&gt;&lt;a href=&quot;https://pencilflip.medium.com/using-es-modules-with-commonjs-modules-in-node-js-1015786dab03&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pencilflip.medium.com/using-es-modules-with-commonjs-modules-in-node-js-1015786dab03&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DZ9OS/hyRvbbCUJq/yGuFIBQsFuqhbJB3Wd5aI1/img.png?width=1200&amp;amp;height=734&amp;amp;face=0_0_1200_734');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Using ES modules with CommonJS modules in Node.js&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This is not meant to be a comprehensive overview of ES modules and CommonJS modules &amp;mdash; I do not go into depth about what modules are and&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pencilflip.medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1675594833371&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;[번역] 왜 CommonJS와 ES Modules는 함께 쓸 수 없는가 | 내 라이브러리에서 esm과 cjs를 동시에 지원하는&quot; data-og-description=&quot;Dan Fabulich의 Node Modules at War: Why CommonJS and ES Modules Can&amp;rsquo;t Get Along 을 번역한 글입니다. 이미지나 설명이 필요한 부분은 추가로 덧붙여 놓았습니다.&quot; data-og-host=&quot;roseline.oopy.io&quot; data-og-source-url=&quot;https://roseline.oopy.io/dev/translation-why-cjs-and-esm-cannot-get-along&quot; data-og-url=&quot;https://roseline.oopy.io/dev/translation-why-cjs-and-esm-cannot-get-along&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DEjyo/hyRvesDJBQ/wIk7aUIXV7SXFlBwdGWLn0/img.jpg?width=2400&amp;amp;height=1597&amp;amp;face=0_0_2400_1597,https://scrap.kakaocdn.net/dn/c0I7EI/hyRvgxe60D/gch55QSbPckfp80BIOzOd1/img.jpg?width=2400&amp;amp;height=1597&amp;amp;face=0_0_2400_1597,https://scrap.kakaocdn.net/dn/oX4m5/hyRwIr7wqm/ClofkoneDKAPlAdyRjnhJ0/img.jpg?width=3600&amp;amp;height=2396&amp;amp;face=0_0_3600_2396&quot;&gt;&lt;a href=&quot;https://roseline.oopy.io/dev/translation-why-cjs-and-esm-cannot-get-along&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://roseline.oopy.io/dev/translation-why-cjs-and-esm-cannot-get-along&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DEjyo/hyRvesDJBQ/wIk7aUIXV7SXFlBwdGWLn0/img.jpg?width=2400&amp;amp;height=1597&amp;amp;face=0_0_2400_1597,https://scrap.kakaocdn.net/dn/c0I7EI/hyRvgxe60D/gch55QSbPckfp80BIOzOd1/img.jpg?width=2400&amp;amp;height=1597&amp;amp;face=0_0_2400_1597,https://scrap.kakaocdn.net/dn/oX4m5/hyRwIr7wqm/ClofkoneDKAPlAdyRjnhJ0/img.jpg?width=3600&amp;amp;height=2396&amp;amp;face=0_0_3600_2396');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[번역] 왜 CommonJS와 ES Modules는 함께 쓸 수 없는가 | 내 라이브러리에서 esm과 cjs를 동시에 지원하는&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Dan Fabulich의 Node Modules at War: Why CommonJS and ES Modules Can&amp;rsquo;t Get Along 을 번역한 글입니다. 이미지나 설명이 필요한 부분은 추가로 덧붙여 놓았습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;roseline.oopy.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Developer/Javascript</category>
      <category>cjs</category>
      <category>CommonJS</category>
      <category>ESM</category>
      <category>javascript</category>
      <category>ROLLUP</category>
      <category>번들러</category>
      <author>jaddong</author>
      <guid isPermaLink="true">https://jaddong.tistory.com/85</guid>
      <comments>https://jaddong.tistory.com/entry/rollup-%EB%B2%88%EB%93%A4-%ED%8C%8C%EC%9D%BC-packagejson-cjs-esm-%EC%B0%A8%EC%9D%B4#entry85comment</comments>
      <pubDate>Sun, 5 Feb 2023 19:58:17 +0900</pubDate>
    </item>
    <item>
      <title>Multiple Pointers - countUniqueValues(고유값세기 - 다중포인터)</title>
      <link>https://jaddong.tistory.com/entry/Multiple-Pointers-countUniqueValues%EA%B3%A0%EC%9C%A0%EA%B0%92%EC%84%B8%EA%B8%B0-%EB%8B%A4%EC%A4%91%ED%8F%AC%EC%9D%B8%ED%84%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;div data-purpose=&quot;safely-set-inner-html:rich-text-viewer:html&quot;&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Implement a function called countUniqueValues, which accepts a sorted array, and counts the unique values in the array. There can be negative numbers in the array, but it will always be sorted.&lt;br /&gt;정렬이 되어있는 배열에서 고유의 값들의 갯수를 세는 함수를 실행하라. 배열에는 음수가 있을 수 있으나 항상 정렬이 되어있다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Examples:&lt;/b&gt;&lt;br /&gt;countUniqueValues([1,1,1,1,1,2]) // 2&lt;br /&gt;countUniqueValues([1,2,3,4,4,4,7,7,12,12,13]) // 7&lt;br /&gt;countUniqueValues([]) // 0&lt;br /&gt;countUniqueValues([-2,-1,-1,0,1]) // 4&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note:&lt;br /&gt;&lt;/b&gt;&lt;b&gt;Time Complexity - O(n)&lt;br /&gt;&lt;/b&gt;&lt;b&gt;Space Complexity - O(n)&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;나의 코드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간복잡도는 O(n)&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이어야 하므로 for 문이 이중이면 안된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;이중포인터(i,j)를 가지고 값을 비교해가며 두 값이 다를 경우에 j값을 i+1값에 넣어가며 전진한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674999123232&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function countUniqueValues(arr){
    if(arr.length === 0){
        return 0;
    }
    
    var i = 0;
    for(var j= i+1; j&amp;lt;arr.length; j++){
        if(arr[i] !== arr[j]){
            arr[i+1] = arr[j];
            i++;
        }
        if(j === arr.length -1){
            return i+1;
        }
    }
   

}

console.log(countUniqueValues([1,1,1,1,1,2]))&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;강의에서의 정답&lt;/h2&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;그러나 for문 안에서 j와 arr의 길이를 비교하는 조건문은 필요가 없었다. 어짜피 j는 arr끝까지 가야 탐색이 끝나는 것이기때문에 필요없는 구문을 넣었다는 점이 아쉬웠다.&lt;/li&gt;
&lt;li&gt;그리고 for문 안에서 i도 ++을 먼저했으면 되는데, i값을 증가시키는 연산을 두번이나 한 셈이어서 이것도 수정하는 것이 좋겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1674999167095&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function countUniqueValues(arr){
    if(arr.length === 0) return 0;
    var i = 0;
    for(var j = 1; j &amp;lt; arr.length; j++){
        if(arr[i] !== arr[j]){
            i++;
            arr[i] = arr[j]
        }
    }
    return i + 1;
}
countUniqueValues([1,2,2,5,7,7,99])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;강의 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.udemy.com/share/106X8E3@Lt-njJ7uYB8WJ1Fa57QDkC-Jt8Jccl8YSfNg-Ss97_zbUJPT4bPNz4Cynqye826R/&quot;&gt;https://www.udemy.com/share/106X8E3@Lt-njJ7uYB8WJ1Fa57QDkC-Jt8Jccl8YSfNg-Ss97_zbUJPT4bPNz4Cynqye826R/&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Developer/알고리즘,자료구조</category>
      <category>javascript</category>
      <category>Multiple Pointers</category>
      <category>다중포인터</category>
      <category>알고리즘</category>
      <category>자료구조</category>
      <category>자바스크립트</category>
      <author>jaddong</author>
      <guid isPermaLink="true">https://jaddong.tistory.com/83</guid>
      <comments>https://jaddong.tistory.com/entry/Multiple-Pointers-countUniqueValues%EA%B3%A0%EC%9C%A0%EA%B0%92%EC%84%B8%EA%B8%B0-%EB%8B%A4%EC%A4%91%ED%8F%AC%EC%9D%B8%ED%84%B0#entry83comment</comments>
      <pubDate>Sun, 29 Jan 2023 22:41:20 +0900</pubDate>
    </item>
  </channel>
</rss>