<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>nana_log</title>
    <link>https://anneslab.tistory.com/</link>
    <description>좋아하는 걸 좋아하는 나나  </description>
    <language>ko</language>
    <pubDate>Fri, 15 May 2026 11:41:15 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>나나 (nykim)</managingEditor>
    <image>
      <title>nana_log</title>
      <url>https://tistory1.daumcdn.net/tistory/3014653/attach/0013e76e78f7462fb4dd1a93a92c7744</url>
      <link>https://anneslab.tistory.com</link>
    </image>
    <item>
      <title>[웹 성능 최적화] #1 브라우저는 어떻게 렌더링을 할까</title>
      <link>https://anneslab.tistory.com/118</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;thumb.png&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;2048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OYFDd/btsp8b3BLUo/URKMxxdCAMPxEJJ0psOHy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OYFDd/btsp8b3BLUo/URKMxxdCAMPxEJJ0psOHy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OYFDd/btsp8b3BLUo/URKMxxdCAMPxEJJ0psOHy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOYFDd%2Fbtsp8b3BLUo%2FURKMxxdCAMPxEJJ0psOHy0%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;860&quot; height=&quot;612&quot; data-filename=&quot;thumb.png&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;2048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;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;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;면접관: &amp;ldquo;웹 사이트 성능 최적화에 대해 아는 대로 얘기해 보세요.&amp;rdquo;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;???: &amp;ldquo;ㅈ..잘...?  &amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&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;b&gt;웹 사이트를 사용자에게 빠르고 쾌적하게 보여주고 싶은 마음&lt;/b&gt;을 다들 한켠에 품고 있으실 거라 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 항상 사용자에게 좋은 UX를 남겨주고 싶으니까요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언젠가 정리해야지 정리해야지 미루다가 더 이상 미룰 수 없다! 해서 써보는 &lt;b&gt;웹 성능 최적화 글&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용이 워낙 방대해서 중구난방 시리즈가 될 거 같은데 쫌쫌따리 써보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저와 의식의 흐름을 함께하며 최적화를 알아보고 싶을 때 읽어주세요!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #ffffff; background-color: #f3c000;&quot;&gt;&lt;b&gt;웹 성능 최적화 시리즈&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;b&gt;#1 브라우저는 어떻게 렌더링을 할까&lt;/b&gt;&lt;span&gt; &lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;◀ Here!&lt;br /&gt;&lt;/i&gt;&lt;span style=&quot;color: #dddddd;&quot;&gt;#2 CRP 최적화하기&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #dddddd;&quot;&gt;#3 Core Web Vitals&lt;br /&gt;etc...&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;저처럼 UI 개발을 하시는 분들은 사용자에게 보여질 View를 자주 다루실 텐데, 저는 View가 어떻게 그려지는지 늘 궁금했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 최적화 방법에 대해 알아보기 전에 먼저 렌더링 과정을 살펴보며 브라우저와 친해져보려고요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 맞지 않는 내용이 있다면 언제든 말씀해 주세요. 미리 감사의 큰절 드립니다  &amp;zwj;♀️&lt;/p&gt;
&lt;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;browse: 둘러보다[훑어보다], 대강 읽다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 브라우저는 웹의 다양한 콘텐츠를 탐색할 수 있게 만들어주는 프로그램입니다. 여기에는 크롬이나 엣지 같은 익숙한 이름부터, 브레이브나 토어 같이 다소 낯선 이름들까지, 정말 다양한 브라우저가 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;웹 브라우저 인기 랭킹 in 한국 (2023.7)  &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;본격적인 내용에 들어가기 앞서, 재미 겸 직무 상식으로 알아보는 웹 브라우저 인기 순위!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;statecounter에 따르면 국내 브라우저의 시장 점유율 순위는 아래와 같습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://gs.statcounter.com/browser-market-share/all/south-korea&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(출처)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2023-08-06 at 3.59.06 PM.png&quot; data-origin-width=&quot;2412&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t8HbW/btsp7UVgc76/IbGUpvjdWDEDZBwRhYY6Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t8HbW/btsp7UVgc76/IbGUpvjdWDEDZBwRhYY6Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t8HbW/btsp7UVgc76/IbGUpvjdWDEDZBwRhYY6Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft8HbW%2Fbtsp7UVgc76%2FIbGUpvjdWDEDZBwRhYY6Jk%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;860&quot; height=&quot;346&quot; data-filename=&quot;Screen Shot 2023-08-06 at 3.59.06 PM.png&quot; data-origin-width=&quot;2412&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 크롬 Chrome (49%)&lt;br /&gt;2. 사파리 Safari (18%)&lt;br /&gt;3. 삼성 인터넷 Samsung Internet (16%)&lt;br /&gt;4. 웨일 Whale (8%)&lt;br /&gt;5. 엣지 Edge (6%)&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;예상했던 대로 크롬이 탑을 달리고, 사파리가 그 다음입니다. 삼성 인터넷도 꽤나 비중이 크네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;재미있는 점은 차트 기간을 5~6년 전으로만 돌려도 악명 높은 그녀석(?)을 만날 수 있습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Screen Shot 2023-08-06 at 4.00.10 PM.png&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;1104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X0NjB/btsp8AhUTuK/VU8oAkLjWh1BmgZqJ1QFk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X0NjB/btsp8AhUTuK/VU8oAkLjWh1BmgZqJ1QFk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X0NjB/btsp8AhUTuK/VU8oAkLjWh1BmgZqJ1QFk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX0NjB%2Fbtsp8AhUTuK%2FVU8oAkLjWh1BmgZqJ1QFk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;353&quot; data-filename=&quot;edited_Screen Shot 2023-08-06 at 4.00.10 PM.png&quot; data-origin-width=&quot;1564&quot; data-origin-height=&quot;1104&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;2018년까지만 해도 사용률 2위가 무려 'IE' 입니다. 그만큼 국내에서는 IE의 비중이 굉장히 높았습니다. 그래서 신경을 쓰지 않을 수 없었던 참 얄미운 녀석이었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 모바일 시장의 확대, 액티브엑스 탈피 등의 시장 변화와 함께 마침내 2022년 IE가 영면에 잠들면서&lt;span style=&quot;color: #dddddd;&quot;&gt;~R.I.P~&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;h3 data-ke-size=&quot;size23&quot;&gt;웹 브라우저의 동작&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 브라우저에 접속해 주소창에 도메인을 입력하면 아주 짧은 순간에 많은 일이 일어납니다. DNS를 통해 IP 주소를 획득하고, TCP를 통해 연결하고, HTTP 요청을 보내 응답을 받습니다. 이 과정에는 캐싱이 포함됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 일련의 과정들을 알아보기엔 블로그 여백이 부족&amp;hellip;하지 않지만 저의 뇌내 메모리가 부족하므로 생략하고 다음에 더 알아보겠습니다. 아무튼 안전한 HTML 파일을 잘 받아왔다고 가정합니다  &lt;/p&gt;
&lt;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;h4 data-ke-size=&quot;size20&quot;&gt;웹 브라우저의 구조&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 브라우저는 이러한 구조로 이루어져 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Frame 1.png&quot; data-origin-width=&quot;2008&quot; data-origin-height=&quot;1470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xxk51/btsqj2xC6Sa/xhNZUdSc44KauK0uDquPL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xxk51/btsqj2xC6Sa/xhNZUdSc44KauK0uDquPL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xxk51/btsqj2xC6Sa/xhNZUdSc44KauK0uDquPL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxxk51%2Fbtsqj2xC6Sa%2FxhNZUdSc44KauK0uDquPL0%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;860&quot; height=&quot;630&quot; data-filename=&quot;Frame 1.png&quot; data-origin-width=&quot;2008&quot; data-origin-height=&quot;1470&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;lsquo;렌더링 엔진&amp;rsquo;으로, 우리가 만든 마크업과 스타일을 해석해서 그려내는 역할을 합니다. &lt;br /&gt;물론 두루두루 친해지고 싶지만 일단 여기선 렌더링에 집중해 봅시다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;렌더링 엔진의 종류&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 렌더링 엔진을 쓰는지는 웹 브라우저마다 조금씩 달라요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Webkit (Safari)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple에서 개발, 컨커러 브라우저의 KHTML을 기반으로 만든 엔진&lt;/li&gt;
&lt;li&gt;Safari, iOS 브라우저에서 사용하며, 크롬도 Blink 탑재 전에는 해당 엔진을 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Blink (Chrome, Whale, Samsung Internet, Edge)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Google에서 개발, WebKit에서 파생된 엔진 (Chromium 28 버전부터 적용)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Trident (Internet Explorer)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Microsoft에서 개발, Internet Explorer에서 사용되었지만 웹 표준 미준수+각종 버그+보안 문제 등으로 이젠 안-녕&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EdgeHTML (Edge)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Microsoft에서 개발, Trident에서 벗어나 Edge에서 사용되던 엔진이었지만, 2019년부터 Edge 브라우저를 Chromium 기반으로 재구축하면서 이젠 안-녕(2)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Gecko (Firefox)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mozilla에서 개발&lt;/li&gt;
&lt;/ul&gt;
&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;사실 웹 브라우저를 만드는 비법(?)은 제조사마다 다르고, 각 엔진도 저마다 고유한 개성이 있어 동작 과정이 조금씩 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크롬(그리고 아마 대부분의 최신 브라우저)의 경우 각 탭마다 별도의 렌더러 프로세스가 돌아가고 있어서, 여러 개의 탭을 열면 각각 렌더링 엔진이 동작해 해당 탭의 내용을 표시합니다. 그래서 하나의 탭이 X_X 하고 죽어도 다른 탭은 멀쩡히 살아남을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;그렇다면 HTML, CSS, JavaScript를 어쩜 그리 찰떡같이 해석해서 화면을 그려내는 것인가! 가 이 글의 제일 궁금한 포인트이기에 그 일련의 흐름을 알아봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;* 아래 내용은 Chrome의 아티클을 기반으로 하고 있으므로 Chromium이나 Blink에만 해당하는 내용이 있을 수 있습니다. 보완이 필요한 부분은 말씀해 주세요!&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;h3 data-ke-size=&quot;size23&quot;&gt;(1) 삐빅, HTML 읽는 중&amp;hellip;  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 문서를 받기 시작하면 우선 이를 &lt;b&gt;파싱(Parsing)하는 작업&lt;/b&gt;을 시작합니다.&lt;/p&gt;
&lt;p id=&quot;d717e924-d0ff-444d-b51d-11ae5e41e789&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;파싱이란 문서를 해석하고 구조화된 데이터로 바꾸는 것으로, 말하자면 마크업을 렌더링하기 위한 형태로 바꾸는 단계입니다.&lt;/p&gt;
&lt;p id=&quot;56684f47-023d-42e2-aeb6-96c33163cc38&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;아아 하나요. 샷추도 해주세요.&amp;rdquo; &amp;rArr; &amp;ldquo;아이스 / 아메리카노 / 1잔 / 샷 추가&amp;rdquo; 처럼 바꾸는 느낌이죠.&lt;/p&gt;
&lt;p id=&quot;94305c3e-e62a-4a70-ad33-b3ae27cfb211&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;307d0b1a-43e1-4447-b7c8-1e683b4139fc&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;렌더러 프로세스의 메인 스레드는 HTML 문서의 원시 바이트를 읽은 다음, 정해진 파일 인코딩에 맞춰 각각을 문자로 전환합니다. 그러면 아래와 같이 꺽쇄로 묶인 문자열을 읽게 됩니다.&lt;/p&gt;
&lt;p id=&quot;101715c1-ae00-402b-a174-cc718aea2d9a&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;4fdb0f65-3c7f-421e-8bb4-700a586c5152&quot; class=&quot;code xml&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;meta charset=&quot;utf-8&quot;/&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;Hello&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;af5090d7-a176-4ec9-9dff-6561690926bb&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;b32e90a5-63fc-40f7-88da-4061b2e0b49c&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;이나 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;는 W3C HTML5 표준에 규정된 문자열이므로 각각의 의미와 규칙을 이해할 수 있습니다. 브라우저는 이것들을 쪼개서 &amp;lsquo;토큰(Token)&amp;rsquo;으로 만듭니다.&lt;/p&gt;
&lt;p id=&quot;e1fd75e3-f684-4d22-9c7e-d5af16244f47&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어, &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;을 보면 html 시작 토큰을 만들고, &lt;code&gt;&amp;lt;/html&amp;gt;&lt;/code&gt;를 보면 html 종료 토큰을 만들어 냅니다. 여기에는 태그 뿐만 아니라 속성(&lt;code&gt;class&lt;/code&gt; 등)이나, 텍스트 같은 HTML 요소가 포함돼요.&lt;/p&gt;
&lt;p id=&quot;643a0e9c-5e0b-4bbe-a07d-49797ce1931a&quot; class=&quot;&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;1280&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpTenl/btsp7qmES05/tzD85duKBjnIgqplkakfi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpTenl/btsp7qmES05/tzD85duKBjnIgqplkakfi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpTenl/btsp7qmES05/tzD85duKBjnIgqplkakfi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpTenl%2Fbtsp7qmES05%2FtzD85duKBjnIgqplkakfi1%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;400&quot; height=&quot;137&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p id=&quot;913af206-c413-42d3-b088-4face5e37cd6&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;cdc560f6-aacc-4e9a-a4a3-38e072552f6d&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 문서를 토큰화했으면, 만든 토큰을 가지고 구조화시킵니다.&lt;/p&gt;
&lt;p id=&quot;a6c272b3-bd8f-4f7a-8f4a-56e77a27c505&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;/html&amp;gt;&lt;/code&gt;에서 head 종료 토큰은 html 종료 토큰 전에 존재합니다. 따라서 head는 html의 자식이구나! 하고 판단할 수 있어요.&lt;/p&gt;
&lt;p id=&quot;4c8f1ff5-376f-474d-b532-fe205300ca3f&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;cfc0075c-1feb-4461-9952-f58073b3f4d2&quot; class=&quot;&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;Untitled2.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUHpFs/btsp9qF7jO0/Bh9MwcuoaIMkspDnmQkMK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUHpFs/btsp9qF7jO0/Bh9MwcuoaIMkspDnmQkMK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUHpFs/btsp9qF7jO0/Bh9MwcuoaIMkspDnmQkMK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUHpFs%2Fbtsp9qF7jO0%2FBh9MwcuoaIMkspDnmQkMK1%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;400&quot; height=&quot;120&quot; data-filename=&quot;Untitled2.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;86186246-b6aa-45b9-b61d-0d706afe913a&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;토큰을 파싱해 구조를 만들었으면 각 토큰은 해당하는 &amp;lsquo;노드(Node)&amp;rsquo;로 변환됩니다. 노드는 부모-자식이라는 계층적 관계를 통해 문서의 구조를 표현해내는 요소입니다.&lt;/p&gt;
&lt;p id=&quot;f1cbea64-21b1-4c68-af61-59d45d289f52&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;사실 HTML 문서의 모든 것은 노드가 되어 구조를 형성합니다. 태그는 요소 노드(Element Node)가 되고, 그 안에 적은 건 텍스트 노드가 되죠. 속성은 물론, 독타입 선언이나 주석도 모두 노드가 됩니다.&lt;/p&gt;
&lt;p id=&quot;ebbb7d24-1855-462d-8fc0-9e1370ab79a4&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;b89cf038-bc0b-4126-a9f3-72b02ebac88e&quot; class=&quot;&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;Untitled3.png&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;1378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgFJko/btsqd4vQob4/PO6ghfUvijIHT9FB3CZekk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgFJko/btsqd4vQob4/PO6ghfUvijIHT9FB3CZekk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgFJko/btsqd4vQob4/PO6ghfUvijIHT9FB3CZekk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgFJko%2Fbtsqd4vQob4%2FPO6ghfUvijIHT9FB3CZekk%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;400&quot; height=&quot;547&quot; data-filename=&quot;Untitled3.png&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;1378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;아무튼 주요 포인트는 &lt;b&gt;HTML 문서를 읽어 노드로 만들고 계층 형태로 구조화한다는 것!&lt;/b&gt;&lt;/p&gt;
&lt;p id=&quot;7ac6aaf5-2055-4ea8-b3fc-fffdfe93e8e0&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이걸 트리 구조라고도 하는데 말 그대로 나무처럼 생겼어요. 뿌리가 있고 가지가 있으며 거기서 잎이 돋아난 나무를 거꾸로 배치한 것이 트리 구조입니다.&lt;/p&gt;
&lt;p id=&quot;b52f171d-4883-4cc4-930f-af25449f18c6&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 구성된 노드 트리를 &lt;b&gt;DOM(Document Object Model) 트리&lt;/b&gt;라고도 불러요.&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;e0613986-95b0-475a-93fe-71a40255d0ee&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그중에서도 처음으로 생성되는, 가장 상위에 있는 노드인 &lt;code&gt;document&lt;/code&gt;를 루트(Root)라고 부릅니다. 루트 = 뿌리, 노드 = 가지라는 걸 생각하면 재미있는 이름이죠! 참고로 텍스트 노드처럼 자식을 가질 수 없는 노드는 리프(leaf)라고 부릅니다  &lt;/p&gt;
&lt;p id=&quot;ec10d71f-9bdb-4a9f-a4fe-cce02f8476fb&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;개발자들은 뿌리(document node)를 진입점으로 문서 내 다른 DOM 요소들을 탐색하고 조작할 수 있습니다.&lt;/p&gt;
&lt;p id=&quot;ccfb75e1-4bec-4a76-84e8-08c1c4353c81&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;6c494928-075d-41c5-9a8f-7b0ea422786e&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;9720a4b8-a271-4450-bcc9-83a5787620a0&quot; data-ke-size=&quot;size20&quot;&gt;DOM? 먹는 건가요? (아님)&lt;/h4&gt;
&lt;p id=&quot;ae0fc12e-8f6d-4bbf-9c83-c51f3014c46c&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;아까부터 DOM이라는 단어가 몇 번 언급됐는데, DOM은 말그대로, 문서(Document)를 객체(Object) 기반으로 표현한 모델(Model)입니다.&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;f6ce7f3b-b4a1-4267-b7ff-558bb053166a&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이 모델은 HTML, XML 등의 문서 내 요소를 객체로 표현해 프로그래밍 언어에서 조작할 수 있게 하는 인터페이스를 제공합니다. 우리는 DOM API를 통해 DOM 객체를 조작할 수 있어요. 만약 span 텍스트를 바꾸고 싶다면 &lt;code&gt;document.body.getElementsByTagName('span')[0].innerText = 'Hello'&lt;/code&gt;로 접근해 조작하면 됩니다.&lt;/p&gt;
&lt;p id=&quot;c5834bbe-4f91-4738-b3e7-32bfbebea537&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;09dd05fa-05e1-42f6-8150-83ab2c2f3570&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;사실 여기까지 쓰다가 저도 점점 헷갈려서 용어를 정리해보았습니다. (잘못된 부분은 말씀해 주시면 감사합니다)&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol id=&quot;70948f0b-7d31-4af2-8921-832af17f2a47&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;1&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;DOM Object&lt;br /&gt;&lt;/b&gt;DOM에서 문서와 관련된 객체를 뜻하며, 접근하고 조작하기 위한 인터페이스를 제공합니다. &lt;br /&gt;프로그래밍적으로 접근하고 &lt;s&gt;&lt;i&gt;조질 수.. &lt;/i&gt;&lt;/s&gt;조작할 수 있는 객체의 개념입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol id=&quot;33a626d9-4671-4a37-89c8-a6ca8f03c10e&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;DOM Node&lt;br /&gt;&lt;/b&gt;DOM Tree(=Node Tree)를 구성하는 온갖 노드들을 말합니다. &lt;br /&gt;트리 구조를 통해 서로 간의 계층적 관계를 나타내요. 문서를 표현하는 각 콘텐츠를 지칭하는 개념입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol id=&quot;d617e2db-d371-470b-bb3d-530b05a9067f&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;DOM Element&lt;br /&gt;&lt;/b&gt;DOM Node 중에서도 태그로 표현되는 요소들(&lt;code&gt;div&lt;/code&gt;등)을 Element Node라고 합니다.&lt;br /&gt;이들을 부르는 다른 말이 DOM Element에요.&lt;/li&gt;
&lt;/ol&gt;
&lt;p id=&quot;c62baa92-d83f-4601-9867-7ecc2b432bc8&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;3cbd8cf7-2fc7-4d3c-92d5-b397400c0923&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;1f3c5072-da87-4f4a-bfef-dfca2cda29ce&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이렇듯 DOM은 트리 구조로 웹 페이지를 표현하며, 이 DOM 트리를 이루는 구성요소가 바로 노드입니다.&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&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;0.png&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tazyK/btsqd4o4EYK/I6QmmgrmLPrYnGgUDlcEok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tazyK/btsqd4o4EYK/I6QmmgrmLPrYnGgUDlcEok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tazyK/btsqd4o4EYK/I6QmmgrmLPrYnGgUDlcEok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtazyK%2Fbtsqd4o4EYK%2FI6QmmgrmLPrYnGgUDlcEok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;110&quot; data-filename=&quot;0.png&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p id=&quot;4b964a96-4844-424e-9040-cef7a0b0a62a&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;4b4549a8-c704-4b2c-8b75-24203c38c3aa&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;DOM은 후술할 CSSOM이라는 것과 결합해 렌더(Render) 트리를 만들며, 브라우저는 이걸 통해 화면을 그려냅니다.&lt;/p&gt;
&lt;p id=&quot;dfb4fe3e-5444-4aa6-afcb-25d0c4291752&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 &lt;b&gt;DOM은 실제로 화면에 보여지는 것과 일치하지 않다&lt;/b&gt;고 할 수 있습니다. 예를 들어, &lt;code&gt;display:none&lt;/code&gt; 처리한 요소는 화면에 보이지 않지만 DOM에는 존재하며, &lt;code&gt;:after&lt;/code&gt; 같은 가상요소는 렌더링되었지만 DOM 객체가 아니므로 자바스크립트로 조작하는 것이 불가합니다.&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;cf8d89e2-ac33-4d81-99f8-4c3905c0dbf0&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;또한, HTML 문서를 보고 만들긴 했지만 DOM이 HTML과 완전히 일치한다고 할 수도 없습니다. 브라우저는 우리가 닫는 태그를 빼먹고 썼더라도 친절하게 DOM을 구성해주니까요.&lt;/p&gt;
&lt;p id=&quot;4b7ba150-024f-436e-bf87-64d5ff9771e7&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;25e32686-937b-464b-9ef9-83595f6c8bd7&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;한편, DOM은 자바스크립트의 영향을 받을 수 있습니다. 예를 들면 &lt;code&gt;appendChild()&lt;/code&gt; 메서드로 돔을 추가하는 것도 가능하죠! 그래서 자바스크립트에 의해 DOM 파싱은 &amp;lsquo;방해받을 수&amp;rsquo; 있습니다. 이에 대한 대한 내용은 다음 글에서 좀 더 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(2) 삐빅, CSS 읽는 중&amp;hellip;  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSSOM은 이름에서 유추할 수 있듯, CSS Object Model의 약자로 CSS 문서를 객체로 나타내고 조작하기 위한 인터페이스를 제공하는 웹 표준입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링 엔진이 HTML을 쫙- 스캔할 때, 아마 대부분 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;에서 스타일시트를 만나게 될 거에요. 렌더링에 이 CSS가 필요하다고 생각되면 요청해서 스타일시트 내용을 받아오게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS도 파싱 과정을 거쳐서 Bytes &amp;rarr; Characters &amp;rarr; Tokens &amp;rarr; Nodes &amp;rarr; Object Model로 만들어집니다. 이걸 CSSOM이라고 부르고요. 마찬가지로 트리 형태로 구조화되어 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS의 C는 캐스케이딩이죠. 즉, 스타일 규칙은 계단식으로 상속됩니다. 브라우저는 트리 구조를 따라 각 노드에 적용할 수 있는 가장 일반적인 규칙부터 시작해 추가적인 스타일을 적용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;body { font-size: 16px; }
p { font-weight: bold; }
span { display: none; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면, &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;는 &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;의 자식이니까 &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;에 적용된 스타일인 &lt;code&gt;font-size:16px&lt;/code&gt;가 적용됩니다. 그 다음에는 자기 자신의 스타일인 &lt;code&gt;font-weight:bold;&lt;/code&gt;가 추가로 적용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;css1.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xV9pw/btsqh4bfdhG/zo2isgD9kDmyXjuCDEMWp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xV9pw/btsqh4bfdhG/zo2isgD9kDmyXjuCDEMWp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xV9pw/btsqh4bfdhG/zo2isgD9kDmyXjuCDEMWp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxV9pw%2Fbtsqh4bfdhG%2Fzo2isgD9kDmyXjuCDEMWp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;222&quot; data-filename=&quot;css1.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;600&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;다만 아직 위의 트리는 완전한 CSSOM 트리가 아니에요! 사실 브라우저들은 저마다 기본으로 제공하는 스타일이 있습니다. 개발자 도구를 열어보면 우리가 정의하지 않은 속성이 user agent stylesheet에서 적용된 걸 볼 수 있어요.&lt;/p&gt;
&lt;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;css2.png&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OpsPc/btsqh0mob2S/J8J2TJRKipI6NvgKzIOy70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OpsPc/btsqh0mob2S/J8J2TJRKipI6NvgKzIOy70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OpsPc/btsqh0mob2S/J8J2TJRKipI6NvgKzIOy70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOpsPc%2Fbtsqh0mob2S%2FJ8J2TJRKipI6NvgKzIOy70%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;650&quot; height=&quot;68&quot; data-filename=&quot;css2.png&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;164&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;이러한 기본 스타일은 특이성(&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Specificity&lt;/a&gt;, 적용되는 CSS 우선순위)이 낮기 때문에 &lt;a href=&quot;https://andy-bell.co.uk/a-modern-css-reset/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;reset&lt;/a&gt; 등으로 재정의하여 쓰기 편하게 바꾸곤 하죠. 아무튼 브라우저는 CSSOM 트리를 구성할 때 이러한 내장 속성도 고려한다는 점!&lt;/p&gt;
&lt;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;h4 data-ke-size=&quot;size20&quot;&gt;CSS 속독 비법: 오른쪽부터 읽기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참, 브라우저는 &lt;b&gt;CSS를 빠르게 파싱하기 위해 오른쪽에서 왼쪽으로 읽어요!&lt;/b&gt; RTL(right-to-left) 파싱 또는 상향식 파싱이라고 하며 말 그대로 셀렉터의 오른쪽(자식)부터 왼쪽(부모)으로 거슬러 올라가며 읽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;header div h1 p span { color: red; }
header span.text { color: red; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 두 개의 규칙이 있습니다. 스타일을 계산할 때 어느 쪽이 더 빠를까요? 사실 웬만한 모던 브라우저는 CSS 선택자 매칭 최적화가 되어 있어 이 정도로 큰 차이는 없겠지만 일단 이론상으로는 후자가 좀 더 빠릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전자는 span부터 시작해, p, h1 등을 거슬러 올라가 header까지 일치하는지 확인해야 합니다. 브라우저가 DOM 트리를 순회하며 요소를 확인해야 하는데, DOM 트리가 크거나 복잡한 경우 속도가 느려질 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후자는 클래스 셀렉터를 쓰고 있는데, 이렇게 하면 DOM 트리를 탐색할 필요 없이 렌더링 엔진이 알아서 빠르게 인덱싱을 하기 때문에 상대적으로 더 빠릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 오른쪽부터 읽는 이유는 스타일을 계산해야 하는 요소는 하나인데 읽어야 하는 규칙은 산더미처럼 많기 때문입니다. 가장 오른쪽부터 시작해서 일치하는지 않는지 먼저 빠르게 판단하면 나머지 셀렉터는 읽지 않고 다음 판단으로 넘어갈 수 있습니다. 왼쪽부터 읽어야 한다면 DOM 트리 탐색을 위해 전체 HTML을 스캔해서 갖고 있어야 할 수도 있겠죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, 각 브라우저별 렌더링 엔진이 가진 최적화 기술도 RTL 파싱에 적합한 매커니즘을 갖고 있다고 합니다. 하지만 이제 모던 브라우저는 성능이 짱짱하기에 셀렉터 최적화에 너무 매달릴 필요는 없을 것 같네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(3) 삐빅, 렌더 트리 구성하는 중&amp;hellip;  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML과 CSS를 읽어 DOM 트리와 CSSOM 트리를 구성했으면, 이제 둘을 결합시켜 렌더 트리를 만들 차례입니다. 렌더 트리는 화면에 실제로 표시되는 시각적 요소를 계층적으로 나타낸 것으로, 얘도 ROM(Render Object Model)이라 부르기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 아래와 같은 단계를 거쳐 렌더 트리를 구성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DOM 트리의 루트(document)에서 시작하여 렌더링할 노드를 찾아냅니다.&lt;/li&gt;
&lt;li&gt;각 노드에 일치하는 CSSOM 규칙을 찾아 적용합니다.&lt;/li&gt;
&lt;li&gt;이제 콘텐츠와 스타일을 결합해 가시적인(visible) 노드를 구성합니다.&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQfwGF/btsp35wuPvT/bwrbj4ux3n3dKxHndQ3K70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQfwGF/btsp35wuPvT/bwrbj4ux3n3dKxHndQ3K70/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;965&quot; data-filename=&quot;download.png&quot; style=&quot;width: 54.0927%; margin-right: 10px;&quot; data-widthpercent=&quot;54.73&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQfwGF/btsp35wuPvT/bwrbj4ux3n3dKxHndQ3K70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQfwGF%2Fbtsp35wuPvT%2Fbwrbj4ux3n3dKxHndQ3K70%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;1280&quot; height=&quot;965&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bspNem/btsp7TWmDBS/lya5muG6lcxxnpN0MZoFm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bspNem/btsp7TWmDBS/lya5muG6lcxxnpN0MZoFm0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;1070&quot; data-filename=&quot;download (1).png&quot; style=&quot;width: 44.7445%;&quot; data-widthpercent=&quot;45.27&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bspNem/btsp7TWmDBS/lya5muG6lcxxnpN0MZoFm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbspNem%2Fbtsp7TWmDBS%2Flya5muG6lcxxnpN0MZoFm0%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;1174&quot; height=&quot;1070&quot;/&gt;&lt;/span&gt;&lt;/div&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-filename=&quot;download (2).png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;1026&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S6QXm/btsp6p9mYWA/9qLh6vBef9AhdPwGO28x50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S6QXm/btsp6p9mYWA/9qLh6vBef9AhdPwGO28x50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S6QXm/btsp6p9mYWA/9qLh6vBef9AhdPwGO28x50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS6QXm%2Fbtsp6p9mYWA%2F9qLh6vBef9AhdPwGO28x50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;450&quot; data-filename=&quot;download (2).png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;1026&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 id=&quot;fa0886f0-ac66-42b8-b2f5-6f98a2f0d802&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심은 눈에 보이는 시각적 요소만 포함한다는 점&lt;/b&gt;입니다. 예를 들어, &lt;code&gt;display:none&lt;/code&gt;이 적용된 DOM 요소는 화면에 렌더링되지 않으므로 렌더 트리에 포함되지 않습니다. 마찬가지로 &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;이나 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;도 시각적인 내용이 없기 때문에 생략됩니다.&lt;/p&gt;
&lt;p id=&quot;10ee713a-dcc5-4414-b316-21824ef4b6ae&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;위 이미지에서도 &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;은 &lt;code&gt;display:none&lt;/code&gt;이 적용됐기에 자식인 텍스트 노드(&amp;rdquo;World&amp;rdquo;)는 렌더 트리에서 제외됐어요. 마찬가지로 &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;이나 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;도 시각적인 내용이 없기 때문에 생략된 걸 볼 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;15698765-3e1a-47b6-8299-bfc0d3ceb463&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;덧붙여 &lt;code&gt;:before&lt;/code&gt; 및 &lt;code&gt;:after&lt;/code&gt;로 만든 가상 요소는 DOM 요소는 아니지만 화면에 렌더링되므로 렌더 트리에 포함됩니다.&lt;/p&gt;
&lt;p id=&quot;be9d43ac-ee86-4147-8931-98a49406e31c&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;92936171-657f-4f02-be9b-9c2e210d98b9&quot; data-ke-size=&quot;size23&quot;&gt;(4) 삐빅, 설계도 만드는 중&amp;hellip;  &lt;/h3&gt;
&lt;p id=&quot;c7ac1098-caff-4a95-bfd5-43f73744efd0&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;bd6761f4-b89b-4f35-be65-c080f4ff9564&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지의 흐름을 거쳐 &amp;lsquo;그려내야 할 것&amp;rsquo;을 알아냈습니다. 그럼 이제 이대로 화면에 그리면 되는 걸까요? 사실&amp;hellip; 할 일이 더 있습니다. 구체적으로 어디에 얼마만큼 그려야 하는지 아직 모르기 때문입니다.&lt;/p&gt;
&lt;p id=&quot;9869ab9d-6c40-4556-a419-a1abcb982742&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;width:50%&lt;/code&gt;는 어떤 부모를 기준으로 한 건지, &lt;code&gt;font-size: 4em&lt;/code&gt;은 어디서부터 계산해야 하는지 아직은 알 수 없어요. 그래서 브라우저는 렌더 트리의 루트부터 시작해 각각의 정확한 위치와 크기를 알아내야 합니다. &amp;ldquo;width: 50%는 250px이고, font-size: 4em은 22px이군&amp;rdquo;하고 정확하게 파악하는 거죠.&lt;/p&gt;
&lt;p id=&quot;bf940dc4-2054-4446-9ef5-95220b7d18d8&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이 과정을 &lt;b&gt;&amp;lsquo;Layout(레이아웃)&amp;rsquo; 또는 &amp;lsquo;Reflow(리플로우)&amp;rsquo;&lt;/b&gt;라고 부릅니다. 최종적인 설계도를 그리는 단계라 할 수 있겠네요!&lt;/p&gt;
&lt;p id=&quot;3c3f6e20-dd4b-4803-b435-58ae9ea6709c&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;a2a4dadb-42c3-49c1-9b0d-6e3a336f5fc2&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;세부 구현 사항은 거의 심연 레벨이여서 생략하겠습니다  &lt;/p&gt;
&lt;p id=&quot;c7baa3f4-8d36-4b42-894c-c1e89c1ed195&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;아무튼 브라우저는 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;박스 모델&lt;/a&gt;에 따라 페이지에서 노드가 차지하는 박스를 만들고, 이를 트리 형태로 구조화합니다. 그리고 각 박스의 크기와 위치를 계산해서 페이지의 레이아웃을 구성합니다.&lt;/p&gt;
&lt;p id=&quot;c1a291c9-5c0b-4590-ba49-d4899bfc7718&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;67b476c4-80f1-4a46-a3f1-92f386f50f57&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;bca15673-9370-4117-b343-f2f468d0de70&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;95dc381a-4aae-40ec-9180-c1e1c30a86d5&quot; data-ke-size=&quot;size23&quot;&gt;(5) 삐빅, 화면을 그리는 중&amp;hellip;  &lt;/h3&gt;
&lt;p id=&quot;d3eb579b-5bb0-4829-8588-b063770457d7&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;f72e84b9-5498-4dd1-9745-2442dd467c7e&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;드디어 웹 페이지에 화면을 그릴 수 있습니다! (마참내!)&lt;/p&gt;
&lt;p id=&quot;7f479f42-cb2a-4e1f-be1d-95e54433b31b&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;레이아웃 단계가 끝나면 &lt;b&gt;렌더링 트리를 화면의 픽셀로 변환하는 &amp;lsquo;페인트(Paint)&amp;rsquo; 이벤트가 실행됩니다.&lt;/b&gt; 여기서도 브라우저는 화면에 그려낼 요소를 트리 형태로 구조화하며, 이를 페인트 트리라고 부릅니다.&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;614ca3b8-5219-4eef-98c2-9d45a554e907&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;1736a699-379e-40f1-bc96-10256e8874e9&quot; data-ke-size=&quot;size20&quot;&gt;합성 단계&lt;/h4&gt;
&lt;p id=&quot;50352185-cfcd-4a22-90f9-1b16a48582e7&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;화면에 그려내야 하는 단계인 만큼, &amp;lsquo;페인트&amp;rsquo; 단계는 렌더링 엔진이 가장 바쁜 구간입니다. 특히 그림자나 그라디언트 같은 렌더링은 페인팅하는 데 시간이 걸리는 비싼 작업이죠.&lt;/p&gt;
&lt;p id=&quot;918848f5-35b3-488b-a40f-e1f2b8d462eb&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 이때 보다 &lt;b&gt;효율적인 렌더링을 위해 &amp;lsquo;합성(Composite)&amp;rsquo; 이라는 과정&lt;/b&gt;을 거칩니다.&lt;/p&gt;
&lt;p id=&quot;4178cc59-e8e4-49e7-8a70-8440d0941238&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;03fe47c0-0222-4b18-8664-ce89c5e30553&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;합성은 렌더링 파이프라인의 레이아웃 및 페인팅 단계 후에 발생합니다. 최종적으로 보여질 화면을 위해 웹 페이지의 여러 요소를 결합하거나 투명도 효과를 적용합니다. &lt;code&gt;z-index&lt;/code&gt;, &lt;code&gt;opacity&lt;/code&gt; 같은 값을 렌더링에 적용하는 거죠. 크롬의 경우, 컴포지터 스레드(compositor thread)라는 별도 스레드가 합성을 담당합니다.&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;70323995-6ff6-4dc2-8acf-3096b367b24f&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;4897b666-ffb9-4b9e-9fa3-031b84c44fdb&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;e73435c9-c6af-4e07-9574-2baa31bfca2e&quot; data-ke-size=&quot;size20&quot;&gt;레이어로 나누면 편하니까&lt;/h4&gt;
&lt;p id=&quot;852ac053-6fbd-4190-bce2-f8de42a53072&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이때 요소를 &lt;b&gt;&amp;lsquo;레이어&amp;rsquo;로 분리하여 관리하기 위해 레이어 트리(Layer Tree)를 생성&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p class=&quot;&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;Screen Shot 2023-08-06 at 5.01.24 PM.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFydd0/btsp8XjKyKw/h4aEwqh7hn3s7HapHCvNt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFydd0/btsp8XjKyKw/h4aEwqh7hn3s7HapHCvNt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFydd0/btsp8XjKyKw/h4aEwqh7hn3s7HapHCvNt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFydd0%2Fbtsp8XjKyKw%2Fh4aEwqh7hn3s7HapHCvNt1%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;400&quot; height=&quot;310&quot; data-filename=&quot;Screen Shot 2023-08-06 at 5.01.24 PM.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;460&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;3e759568-db40-4609-90dd-949986066d8c&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;어도비 포토샵을 써보셨으면 레이어 패널에서 눈을 끄거나 켜면서 레이어를 관리한 경험이 있으실 텐데 그것과 비슷해요! 예를 들어 사이드 네비게이션 바가 열린 화면을 그려야 하는 경우를 생각해 봅시다. 기존 메인 화면 위에 그려도 되지만, 레이어 위에 네비게이션 바를 그려서 합성하는 게 훨씬 효율적입니다. 네비게이션을 닫는다고 해도 해당 레이어에만 변화가 일어나니 전체를 다시 렌더링 할 필요도 없죠!&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;98d04b9e-284f-4199-aed0-54e3c078f32b&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 웹 페이지를 몇 개의 레이어로 나눈 뒤 컴포지터 스레드에서 각각을 칠하고 합성하면 빠르고 효율적으로 작업을 끝낼 수 있습니다.&lt;/p&gt;
&lt;p id=&quot;72137331-e876-44d0-986d-1c995cae2dea&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;레이어 분리 상황을 보고 싶다면 크롬 개발자 도구의 Layers 탭에서 확인할 수 있습니다. 세부사항을 보면 합성 원인도 확인할 수 있어요.&lt;/p&gt;
&lt;p id=&quot;605d247d-5abc-4ded-adb7-c176fd0f37e5&quot; data-ke-size=&quot;size16&quot;&gt;만약 해당 패널이 보이지 않는다면, &lt;code&gt;Cmd(Ctl)+P&lt;/code&gt;로 검색 창을 띄운 뒤 &lt;code&gt;&amp;gt;layers&lt;/code&gt;를 입력하세요!&lt;/p&gt;
&lt;p id=&quot;09c0ef0a-c125-46f1-a2e1-075ad490b6d2&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&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;222.gif&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buCyJc/btsp7pnL10w/9WsS3lgYmeUSPxMvVTAMA1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buCyJc/btsp7pnL10w/9WsS3lgYmeUSPxMvVTAMA1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buCyJc/btsp7pnL10w/9WsS3lgYmeUSPxMvVTAMA1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/buCyJc/btsp7pnL10w/9WsS3lgYmeUSPxMvVTAMA1/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;800&quot; height=&quot;663&quot; data-filename=&quot;222.gif&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;3317ab33-b43d-453e-81c8-1b109d238ed8&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;23da1763-4d43-451d-9acc-9aca1bf72d99&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;cedccadd-1f4d-446e-a987-f51b8d0ec7d8&quot; data-ke-size=&quot;size20&quot;&gt;하드웨어&amp;hellip; 가속!!!&lt;/h4&gt;
&lt;p id=&quot;ff908d50-11ec-4099-bd8b-9ca074931446&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;혹시 하드웨어 가속에 대해 들어보셨나요? 여기서는 CPU가 처리하기 어려운 복잡한 작업을 &lt;b&gt;GPU에서 처리하도록 하여 성능 향상을 끌어내는 것&lt;/b&gt;을 말합니다. GPU는 그래픽 렌더링과 병렬 처리에 최적화되어 있기 때문에 복잡한 그래픽 작업을 보다 효율적으로 처리합니다.&lt;/p&gt;
&lt;p id=&quot;cdfdade8-804a-49f9-a49f-5fa9c6b00a4c&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CSS 속성으로 &lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;opacity&lt;/code&gt; 속성을 사용하면 GPU에서 처리되어 더 부드럽게 애니메이션을 표현할 수 있습니다.&lt;/b&gt; 그 외에 canvas나 WebGL도 하드웨어 가속으로 처리됩니다.&lt;/p&gt;
&lt;p id=&quot;505d6559-c24c-48c7-a6d0-082a5b94641a&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;3d833d77-3ebf-48a7-b30d-ff996ed96ead&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;사실 레이어도 세분화해서 나뉘어지지만, 여기서는 하드웨어 가속을 사용하여 처리하는 그래픽 레이어(Graphics Layer)를 가리킵니다. 레이어가 생성되면 일반적으로 하드웨어 가속이 발생하고, GPU에서 레이어들을 합성하여 최종 이미지를 생성합니다.&lt;/p&gt;
&lt;p id=&quot;4dbb626e-da80-4043-b14f-6ab3ef3c2947&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 &lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;opacity&lt;/code&gt; 속성을 쓴다고 항상 레이어가 생성되어 하드웨어 가속이 발생하는 것은 아닙니다. 레이어를 관리하는 데는 비용이 들기 때문에 브라우저는 자기 마음대로(=최적화 상황에 따라) 레이어를 만들지 않거나 서로 합치기도 합니다. 하지만 대부분의 경우는 렌더링 성능을 향상시킬 수 있기에 최대한 하드웨어 가속 속성을 써서 전환 효과를 만드는 것이 좋습니다.&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1691307545331&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.slide {
  will-change: transform;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;93047d47-217a-471f-b85b-453eb78a4cb1&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;만약 필요하다면 &lt;code&gt;will-change&lt;/code&gt;라는 속성을 써서 브라우저에게 해당 요소를 레이어로 승격시키고 하드웨어 가속을 쓰고 싶다고 말할 수도 있습니다. 하지만 이 경우에도 적용할지 여부는 브라우저 마음이라는 점!&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;5c3e8f04-9217-4f8e-ad56-873a8ef9f83f&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;9445f1ba-9558-4ac8-b913-cc6413012828&quot; data-ke-size=&quot;size20&quot;&gt;최적화 = 일 덜 시키기&lt;/h4&gt;
&lt;p id=&quot;3ddb456d-0d67-42c3-b3af-62b4e71ce9b6&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;화면을 그리기 위해 렌더링 엔진은 Layout &amp;rArr; Paint &amp;rArr; Composite 이라는 과정을 거친다는 걸 알았습니다. 그렇다는 건 화면에 변화가 일어났을 때 해당 영역에서 저 과정을 반복한다는 셈이죠.&lt;/p&gt;
&lt;p id=&quot;f0bdb5ab-887e-47be-b787-be888ec78478&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;성능 최적화를 위해서는 레이아웃이나 페인트를 발생시키는 상황을 줄이는 것이 좋습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이른바 리플로우/리페인트라고 부르는, &amp;lsquo;다시 그리는 비용&amp;rsquo;을 줄이려면 위 파이프라인에서 합성만 발생하는 것이 가장 좋고요! 즉, 렌더링 엔진에게 일을 덜 시키면 됩니다. 요 내용은 나중에 프레임 최적화를 배울 때 더 살펴보겠습니다.&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p id=&quot;0e0348e2-bf3b-4746-8ff0-3b08fe1c0732&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;5c773868-3994-4e83-903f-16e9600b809d&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;ed7faa2e-c179-4603-9f7b-0c6f4317fcb9&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;36961dfe-622c-4b37-a092-0fbb87ebbfff&quot; data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;blockquote id=&quot;531ae911-5e6e-46cf-9b3f-08caf69fb037&quot; data-ke-style=&quot;style2&quot;&gt;- 국내 브라우저 탑쓰리: 1) 크롬 2) 사파리 3) 삼성 인터넷&lt;br /&gt;- 브라우저의 렌더링 엔진이 HTML과 CSS를 해석해서 화면을 그려준다.&lt;br /&gt;&amp;nbsp; &amp;nbsp;- 크롬은 Blink, 사파리는 Webkit, 파이어폭스는 Gecko 엔진을 사용한다.&lt;br /&gt;- 렌더링 과정&lt;br /&gt;&amp;nbsp; &amp;nbsp;(1) HTML 해석 후 DOM 트리 구성&lt;br /&gt;&amp;nbsp; &amp;nbsp;(2) CSS 해석 후 CSSOM 트리 구성&lt;br /&gt;&amp;nbsp; &amp;nbsp;(3) DOM + CSSOM 결합하여 렌더 트리 구성&lt;br /&gt;&amp;nbsp; &amp;nbsp;(4) 레이아웃 계산&lt;br /&gt;&amp;nbsp; &amp;nbsp;(5) 페인트 및 레이어 합성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;d1325172-d686-4710-80d8-89cbbfe1ce2d&quot; data-ke-size=&quot;size26&quot;&gt;에필로그&lt;/h2&gt;
&lt;p id=&quot;82e203ea-dcaa-423e-884e-324570a911b6&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저가 웹 페이지를 표시하려면 (우리 눈엔 순식간이지만) 제법 긴 과정을 거쳐야 한다는 걸 알았습니다.&lt;/p&gt;
&lt;blockquote id=&quot;80278078-5313-4ea3-80d9-c98f3c94867c&quot; data-ke-style=&quot;style3&quot;&gt;① 리소스 가져오기 &lt;br /&gt;② 파싱 &lt;br /&gt;③ DOM 트리 &amp;amp; CSSOM 트리 구성 &lt;br /&gt;④ 렌더 트리 구성 &lt;br /&gt;⑤ 레이아웃 계산 &lt;br /&gt;⑥ 페인팅 &amp;amp; 합성 &lt;br /&gt;⑦ 렌더링&amp;nbsp;  &lt;/blockquote&gt;
&lt;p id=&quot;a2283151-c5a9-46a5-8b31-ab7d97635acb&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;c6effa32-22f1-4fa7-9624-9439b46e2bd8&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;&amp;lsquo;주요 렌더링 경로(CRP; Critical Rendering Path)&amp;rsquo;&lt;/b&gt;라고도 부릅니다. 말그대로 사용자에게 꼭 필요한 화면을 렌더링하는 데 필요한 흐름입니다.&lt;/p&gt;
&lt;p id=&quot;8e2131f5-c08a-47a8-a653-d70671458318&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;40c93a4d-b60d-4276-b757-a137a97a1607&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 이쯤 드는 생각: &lt;br /&gt;&amp;ldquo;브라우저가 렌더링을 하는 방법을 알았으니, 이제 어떻게 렌더링 최적화를 할 수 있을지 궁금한데&amp;hellip;  &amp;rdquo;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;b024e674-feff-4a22-9354-6e909e279a0d&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 CRP를 최적화하는 것을 포함해 브라우저가 이미지, 애니메이션 등 리소스를 표현할 때 어떻게 성능 최적화를 할 수 있을지 이제부터 알아봅니다! (빠밤)&lt;/p&gt;
&lt;p id=&quot;e19722d8-db6e-4671-ad02-b1c747869571&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;결국 우리의 목적은 웹 페이지를 빠르게 그려내고 상호 작용할 수 있도록 해서 유저의 체감 성능을 높이는 거니까요.&lt;/p&gt;
&lt;p id=&quot;ea74d136-fb16-47fc-90ca-199aeddbe6a4&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;d337593d-a04f-48e6-8e07-874860b8b038&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;058678d8-0481-4947-9dde-962ac4975cc3&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;좋은 자료가 워낙 많아 구체적인 내용은 다음 글에 이어 작성해보겠습니다.&lt;/p&gt;
&lt;p id=&quot;e9a54f9a-8c4b-4c74-8e4f-e71527acf797&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;여기까지 스크롤의 여정에 함께해주신 분들께 감사의 박수를 보냅니다  &lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; 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;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://web.dev/critical-rendering-path-render-tree-construction/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[web.dev] Render-tree Construction, Layout, and Paint&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://www.udacity.com/course/website-performance-optimization--ud884&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Undacity] Website Performance Optimization&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/Performance/Critical_rendering_path&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[MDN] Critical&amp;nbsp;rendering&amp;nbsp;path&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://viethung.space/blog/2021/09/26/Browser-from-Scratch-Layout/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[viethung.space] Browser from Scratch: Layout&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://developer.chrome.com/blog/inside-browser-part3/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Chrome Developers]&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;Inside look at modern web browser&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Blog/etc.</category>
      <category>렌더링</category>
      <category>웹브라우저</category>
      <category>웹성능최적화</category>
      <category>최적화</category>
      <author>나나 (nykim)</author>
      <guid isPermaLink="true">https://anneslab.tistory.com/118</guid>
      <comments>https://anneslab.tistory.com/118#entry118comment</comments>
      <pubDate>Sun, 6 Aug 2023 16:55:24 +0900</pubDate>
    </item>
    <item>
      <title>2022년 짤막한 회고  </title>
      <link>https://anneslab.tistory.com/117</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;diary-cover.jpg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfyxhL/btrX8hvvH3v/BYEVA56fIcx9ZQBuEXvdjK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfyxhL/btrX8hvvH3v/BYEVA56fIcx9ZQBuEXvdjK/img.jpg&quot; data-alt=&quot;128mm x 182mm, 뉴플러스 백색, 185p&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfyxhL/btrX8hvvH3v/BYEVA56fIcx9ZQBuEXvdjK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfyxhL%2FbtrX8hvvH3v%2FBYEVA56fIcx9ZQBuEXvdjK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;643&quot; data-filename=&quot;diary-cover.jpg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1197&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;128mm x 182mm, 뉴플러스 백색, 185p&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;diary3-2.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1199&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEcBKG/btrX2XZE58r/BEi8kf2Ow8bup8tWBqlzzK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEcBKG/btrX2XZE58r/BEi8kf2Ow8bup8tWBqlzzK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEcBKG/btrX2XZE58r/BEi8kf2Ow8bup8tWBqlzzK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEcBKG%2FbtrX2XZE58r%2FBEi8kf2Ow8bup8tWBqlzzK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;1199&quot; data-filename=&quot;diary3-2.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1199&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;diary4-2.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1199&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDqx3H/btrX3s6cSrz/kG8sTpswiQ6CJkvdAa1L51/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDqx3H/btrX3s6cSrz/kG8sTpswiQ6CJkvdAa1L51/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDqx3H/btrX3s6cSrz/kG8sTpswiQ6CJkvdAa1L51/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDqx3H%2FbtrX3s6cSrz%2FkG8sTpswiQ6CJkvdAa1L51%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;644&quot; data-filename=&quot;diary4-2.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1199&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;[일기 모아 제본하기]도 이걸로 어느덧 네 번째!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 익숙해질 때도 됐는데 여전히 우당탕 헤매면서 마무리했다 (그리고 꼭 끝나야 보이는 오타...  )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해 제본 컨셉은 뭘로 할까 고민하다가 우주로 정했다.  ✨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매달 회고를 할 때마다 이번 달의 한마디를 적어두는데 그때 적었던 문구 중 하나에 꽂혀서 슥 가져와 넣었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주변에 보여주면 '뭔 유니버스?' 물어보는데 마블 영화 시리즈에서 영감을 받은 문구가 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년에 개봉했던 마블 시리즈 작품 중 닥터 스트레인지: 대환장&amp;hellip; 아니 대혼돈의 멀티버스란 작품이 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용을 다 떠나서 영화에서 말하는 멀티버스라는 개념이 무척 신선했던 게 기억이 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;그렇담 수많은 유니버스가 존재하고 내가 있는 곳도 그중 하나의 유니버스?!&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 생각하면 꽤 흥미롭다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세상 어딘가에는 다른 유니버스가 존재할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 갖추지 못한 것, 내가 택하지 않은 것이 있는 유니버스.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어딘가의 나는 지금의 내가 갖추지 못한 재능을 갖고 태어났을 수도, 어딘가의 나는 지금의 내가 선택하지 않은 길을 걷고 있을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그것은 지금 내가 있는 유니버스가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 나를 둘러싼 환경이 더 좋았더라면, 만약 내가 가진 능력이 더 뛰어났더라면, 만약 내가 그때 그 선택을 받아들였다면.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 수많은 &amp;lsquo;만약&amp;rsquo;의 유니버스는 존재할지도 모르지만 그건 여기가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기, 지금, 내가 있는 곳은 내가 만들어낸 유니버스다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함께 하고 싶다고 생각했기에 그 사람들을 곁에 두었고, 좋아하기에 내가 가진 능력으로 최선을 다했으며, 하고 싶다고 갈망하여 그런 선택을 내렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 선택으로 내 우주를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러니 내가 갈 수도 없는 유니버스를 꿈꾸는 대신, 내가 여기 서있는 이 유니버스에 최선을 다하는 것이 맞다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 것에만 영향을 받는 어린 시절의 시기는 지났고 이제 나는 내가 나에게 어떤 영향을 줄 수 있을지 선택할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우주는 끊임없이 변화한다. 무한한 가능성이 이곳에 있다.&lt;/p&gt;
&lt;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;2022년에는 이 사실을 깨달았다는 것만으로 큰 변화가 있었고 충분했다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;갖지 못한 것을 한탄하고 내가 이제껏 이뤄낸 걸 인정하지 않는 건, 여기가 아닌 다른 유니버스 가고 싶다고 소리치는 거나 마찬가지니까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우주를 넘나드는 마법사조차 자기 유니버스에서 살기로 마음 먹는데 나도 내 유니버스를 사랑해야지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 제목을 '여기가 나의 유니버스'로 지었다 땅땅  &amp;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정신차려보니 어느새 2월이었다...! 그래서 까먹기 전에 간단! 짤막! 회고하기.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어릴 때는 &amp;lsquo;자기 나이만큼 세월이 빠르게 흐른다&amp;rsquo;는 말을 믿지 않았는데 이제는 공감하다 못해 눈물이 찔끔 나올 만큼 절절하게 느낀다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 회사에서는 낯선 경험을 많이 했고 좋은 사람들에게 다양한 영감을 얻을 수 있었다. 부족하지만 그래서 더 애썼던 것도 사실이고, 몰랐던 걸 알아낼 때는 횃불을 밝히는 느낌 같아서 좋았다. 물론 아직 많이 어두워서 횃불을 잔뜩 밝히긴 해야겠지만! 그래도 주어진 기회는 감사하고 많이 써먹어야지 ;9&lt;/li&gt;
&lt;li&gt;기록은 나름대로 꼬박꼬박 했&amp;hellip;나?! 일기는 확실히 작년보다 줄었지만 매달 회고를 남겨서 되돌아보는 재미가 있었다. 무엇보다 블로그에 방문자 수에 100만 명이 찍혀 있어서 놀랐다! 세상에마상에 정말 많은 분들이 들러주셨고 게다가 댓글로 좋은 말씀만 남겨주심&amp;hellip;! 자유롭게 댓글을 남길 수 있도록 완전 익명으로 열어두는 게 좀 걱정스럽긴 했는데 그럴 필요가 없었다. 역시 세상엔 천사가 더 많지요. 이 문단을 빌려 수많은 엔젤분들께 감사 인사를 드립니다  ❤️&lt;/li&gt;
&lt;li&gt;처음으로 시작한 웨이트 운동은 할말하않. 몸살났을 땐 충격이었지만 다들 이렇게 운동하는 건가?! 하지만 몰랐던 분야에 발을 들이는 건 새 스킬을 얻는 감각이다. 레벨업이 너무 어려울 뿐 후후훟,,,&lt;/li&gt;
&lt;li&gt;코로나는 여전하지만 그래도 3년 만에 국제선을 타서 좋았다! 정말 오랜만인데 일본어를 안 까먹었다는 게 신기했다. 아니 이러면 욕심이 나잖아..! 다시 공부하고 싶을지도?! 낯선 경험, 새로운 자극이 인생에는 필요하다는 것도 깨달았다.&lt;/li&gt;
&lt;li&gt;그래도 제일 신경썼던 건 멘탈 케어가 아니었을까. 약해져야 알 수 있던 것이 있었다고 재작년에 썼었는데, 그래서 더 자신에게 귀를 기울이려고 애썼다. 일 년만에&lt;span&gt;&amp;nbsp;멘탈짱짱이 될 수는 없었지만&lt;/span&gt; 그래도 내가 힘들 때 &amp;lsquo;잠깐&amp;rsquo;이라고 외칠 수 있게 됐다. 아아아주 큰 발전이다  &lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Asset 1@2x.png&quot; data-origin-width=&quot;93&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4MRnr/btrX6ihbvXF/fNxoelQYHaq1X5X43jtivk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4MRnr/btrX6ihbvXF/fNxoelQYHaq1X5X43jtivk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4MRnr/btrX6ihbvXF/fNxoelQYHaq1X5X43jtivk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4MRnr%2FbtrX6ihbvXF%2FfNxoelQYHaq1X5X43jtivk%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;50&quot; height=&quot;50&quot; data-filename=&quot;Asset 1@2x.png&quot; data-origin-width=&quot;93&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉬운 이별도, 새로운 만남도 많았던 2022년. 결국 지나고 나면 &amp;lsquo;멋진&amp;rsquo;이란 수식어가 붙게 되는 거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023년에도 멋진 유니버스를 만들 수 있길!&lt;/p&gt;
&lt;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>Blog/Loves</category>
      <author>나나 (nykim)</author>
      <guid isPermaLink="true">https://anneslab.tistory.com/117</guid>
      <comments>https://anneslab.tistory.com/117#entry117comment</comments>
      <pubDate>Sat, 4 Feb 2023 13:48:33 +0900</pubDate>
    </item>
    <item>
      <title>[React] Gatsby 찍먹해보기 - 설치부터 배포까지</title>
      <link>https://anneslab.tistory.com/116</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;gatsby.png&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cu7qOq/btrTXLWYJOd/0rw8k8R2Boqgkg2g7m1wi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu7qOq/btrTXLWYJOd/0rw8k8R2Boqgkg2g7m1wi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu7qOq/btrTXLWYJOd/0rw8k8R2Boqgkg2g7m1wi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu7qOq%2FbtrTXLWYJOd%2F0rw8k8R2Boqgkg2g7m1wi0%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;860&quot; height=&quot;484&quot; data-filename=&quot;gatsby.png&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1440&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 종종 로컬에서 시작해 로컬에서 끝나는 마크업 작업(aka 이메일 템플릿)을 진행하는데요,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 파일을 찾으려면 하나씩 파일을 열고 닫아야 하는 수고스러움이 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;로컬에 흩어진 파일을 한 화면에서 볼 수 있게 정리하고 싶어! 하지만 어떻게?!!!&quot;하고 고민하던 중...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그레잇한 개츠비를 알게 되어 찍먹해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면 (삽질도 많았지만) 좋은 경험이었어요!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 사이트를 만들 생각인데 바닐라는 좀 그렇고 리액트 기반에서 뚝딱 만들어보고 싶다 하시는 분에게 맞을 것 같아요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;까먹을 미래를 위해 가볍게 정리해봅니다. 팁은 공식문서가 더 자세하니 이쪽으로 &amp;rarr; &lt;a href=&quot;https://www.gatsbyjs.com/docs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Gatsby Documentation&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;What&amp;rsquo;s Gatsby?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;React 기반의 정적 사이트 생성 프레임워크 (JAM Stack 기반)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JAM ...?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jamstack.org/what-is-jamstack/&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Jamstack&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is an architecture designed to make the web faster, more secure, and easier to scale.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;잼은 &lt;code&gt;Javascript&lt;/code&gt;, &lt;code&gt;Api&lt;/code&gt;, &lt;code&gt;Markup&lt;/code&gt; 의 앞글자를 하나씩 따왔습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;jam.png&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;1089&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BTRwm/btrTGX3Zkm8/zurg7DzYECPuAIoubpbJsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BTRwm/btrTGX3Zkm8/zurg7DzYECPuAIoubpbJsK/img.png&quot; data-alt=&quot;JAM이란 표현은 Netlify가 제안했다고 하네요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BTRwm/btrTGX3Zkm8/zurg7DzYECPuAIoubpbJsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBTRwm%2FbtrTGX3Zkm8%2Fzurg7DzYECPuAIoubpbJsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;516&quot; data-filename=&quot;jam.png&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;1089&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JAM이란 표현은 Netlify가 제안했다고 하네요&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;쉽게 말하면, &quot;Javascript &amp;amp; Markup으로 정적 페이지를 표시하고, 필요 시에 API를 통해 데이터를 호출하는 것&quot;이네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전통적 웹 사이트: 서버에 요청하면 SSR 을 통해 HTML을 만들어 제공&lt;/li&gt;
&lt;li&gt;SPA : 처음 요청받은 페이지만 SSR 로 제공하고 나머지는 CSR 로 제공&lt;/li&gt;
&lt;li&gt;JAM Stack: 각 페이지를 HTML로 Pre-Render하여 캐싱 후 CDN에서 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; ↪ &lt;b&gt;사용자에게 화면을 보여주기 위해 준비하는 시간을 단축할 수 있다!는 장점 ⚡️&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;뭘로 마크업할까? (SSG, Static Site Generator)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-12-15 at 4.26.09 PM.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dav6Bd/btrTGbauVtC/B7EH0e5zvavQzkMkYL3HJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dav6Bd/btrTGbauVtC/B7EH0e5zvavQzkMkYL3HJ0/img.png&quot; data-alt=&quot;https://jamstack.org/generators/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dav6Bd/btrTGbauVtC/B7EH0e5zvavQzkMkYL3HJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdav6Bd%2FbtrTGbauVtC%2FB7EH0e5zvavQzkMkYL3HJ0%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;629&quot; height=&quot;549&quot; data-filename=&quot;Screen Shot 2022-12-15 at 4.26.09 PM.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1328&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://jamstack.org/generators/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;JS/React&lt;/code&gt; Next.js&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Go&lt;/code&gt; Hugo&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JS/React&lt;/code&gt; Gatsby&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ruby&lt;/code&gt; Jekyll&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JS/Vue&lt;/code&gt; Nuxt&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;그럼 JAM에서 M, 마크업은 뭘로 할 것이냐! 는 자기 마음인데, 저는 React 기반인 Next.js와 Gatsby 중 하나를 고르기로 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘의 큰 차이점은 SSR 지원 여부로, Next.js와 다르게 Gatsby는 SSR을 지원하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 경험하신 분들은 어떨지 검색해보니 이렇게들 말씀하시더라구요  &lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원페이지, 위키, 블로그 등 정적 콘텐츠가 대부분이고 볼륨이 가볍다면 &amp;rArr; Gatsby&lt;/li&gt;
&lt;li&gt;동적 데이터 활용도가 많고 &lt;span&gt;콘텐츠 양이 점점 많아질 것으로 예상된다면&lt;/span&gt;&amp;nbsp;&amp;rArr; Next.js&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 100% 정적 데이터만 활용해서 앱을 만들 예정이므로 개츠비를 셀렉했습니다. (넥스트는 넥스트 타임에...  )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;프로젝트 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;요구조건: Node.js 18.0.0+&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1671090829722&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install -g gatsby-cli
$ gatsby new # 터미널로 세팅 가능
$ cd {프로젝트 폴더}
$ npm run develop

$ gatsby --version # 5.2.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세팅할 때 TypeScript나 Sass를 바로 들고 시작할 수 있어 편했습니다.&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;Screen Shot 2022-12-15 at 5.11.56 PM.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SUh0i/btrTKtgKdfs/7BXtJVngfcJKeZDbE9KwQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SUh0i/btrTKtgKdfs/7BXtJVngfcJKeZDbE9KwQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SUh0i/btrTKtgKdfs/7BXtJVngfcJKeZDbE9KwQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSUh0i%2FbtrTKtgKdfs%2F7BXtJVngfcJKeZDbE9KwQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;388&quot; data-filename=&quot;Screen Shot 2022-12-15 at 5.11.56 PM.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 끝나면 &lt;code&gt;npm run develop&lt;/code&gt; 또는 &lt;code&gt;npm run start&lt;/code&gt;를 통해 로컬 개발 서버를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 모드일 때는 정적 파일을 생성하지 않고, &lt;code&gt;gatsby build&lt;/code&gt; 실행 시 public/ 폴더에 정적 파일(HTML, CSS, 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시작하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;페이지 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지를 만들고 싶다면 그냥 &lt;code&gt;pages/&lt;/code&gt; 폴더 아래에 JSX를 리턴하는 파일을 만들면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 pages/about-us.jsx(또는 tsx)만 만들면 localhost:8000/about-us에서 만나볼 수 있습니다  &lt;/p&gt;
&lt;pre id=&quot;code_1671091861098&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* src/pages/about-us.tsx */

import React from 'react';

export default function AboutUs(){
  return(
    &amp;lt;h1&amp;gt;ABOUT&amp;lt;/h1&amp;gt;
  )
}

// 이제 `localhost:8000/about-us` 에서 접근 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, 개츠비의 &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-head/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Head API&lt;/a&gt;를 통해 title 등 메타데이터를 쉽게 설정할 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 내에서 Head 컴포넌트를 내보내면 자동으로 처리해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671092185635&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* src/components/PageMeta.tsx */

import React from 'react';

interface PageMetaProps {
  title: string;
  description?: string;
}

export default function PageTitle({ title, description = 'Hello World' }: PageMetaProps) {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;title&amp;gt;My Site | {title}&amp;lt;/title&amp;gt;
      &amp;lt;meta name=&quot;description&quot; content={description} /&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1671092257474&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* src/pages/about-us.tsx */
//...

export const Head = () =&amp;gt; &amp;lt;PageMeta title=&quot;팀을 소개합니다&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-12-15 at 5.25.41 PM.png&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMMc4p/btrTFKEiYSH/B3FE5P2MktHlXsKFiAwOYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMMc4p/btrTFKEiYSH/B3FE5P2MktHlXsKFiAwOYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMMc4p/btrTFKEiYSH/B3FE5P2MktHlXsKFiAwOYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMMc4p%2FbtrTFKEiYSH%2FB3FE5P2MktHlXsKFiAwOYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;353&quot; data-filename=&quot;Screen Shot 2022-12-15 at 5.25.41 PM.png&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;636&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;h4 data-ke-size=&quot;size20&quot;&gt;페이지 연결하기: Link 컴포넌트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Router의 &amp;lt;Link/&amp;gt;와 비슷하게 &lt;code&gt;&amp;lt;Link to={이동할 페이지}/&amp;gt;&lt;/code&gt; 형태로 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671093709419&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Link to=&quot;about-us&quot;&amp;gt;ABOUT&amp;lt;/Link&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;재밌는 점, 개츠비의 Link 컴포넌트는 preloading을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 링크가 화면에 표시되거나 링크에 마우스오버했을 때 해당 페이지를 미리 불러옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 사용자가 클릭했을 때 미리 불러온 페이지를 보여줄 수 있어 빠른 전환이 가능합니다. (WoW)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;플러그인 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개츠비는 다양한 테마와 플러그인을 제공하는데요, '이런 게 있을까?' 싶어서 검색해보면 필요했던 플러그인이 있어서 신기했어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 개츠비가 폭풍성장을 하면서 버전이 쭉쭉 올라가는데 일부 플러그인의 호환성 버전은 그보다 낮아 '어쩌실?'하고 묻는 경우가 종종 있었습니다. (어쩌긴 뭘 어째 설치 고!!)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;peerDependencies&lt;/b&gt;&amp;nbsp; (*peer:동료)&lt;br /&gt;모듈이 작동되도록 설계된 다른 소프트웨어 라이브러리의 특정 버전이나 버전집합. 브라우저-브라우저 익스텐션 또는 react-redux 간의 관계와 유사하다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-12-16 at 10.53.50 AM.png&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUvUfN/btrTJMWi4NR/lQ8SDyCKsC0nWNYlAkKAp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUvUfN/btrTJMWi4NR/lQ8SDyCKsC0nWNYlAkKAp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUvUfN/btrTJMWi4NR/lQ8SDyCKsC0nWNYlAkKAp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUvUfN%2FbtrTJMWi4NR%2FlQ8SDyCKsC0nWNYlAkKAp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;373&quot; data-filename=&quot;Screen Shot 2022-12-16 at 10.53.50 AM.png&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;770&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;개츠비 플러그인은 &lt;a href=&quot;https://www.gatsbyjs.com/plugins/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;서 살펴볼 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 한번&amp;nbsp;&lt;b&gt;&lt;a href=&quot;https://www.gatsbyjs.com/plugins/gatsby-plugin-image&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;gatsby-plugin-image&lt;/a&gt; &lt;/b&gt;플러그인을 설치해봅니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그외 함께 쓰면 요긴한 gatsby-plugin-sharp 패키지도 같이 설치해 줍니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gatsby-plugin-sharp: 유동적 크기로 제공하거나 압축하는 등 실질적인 이미지 처리를 다룹니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1671159686521&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install gatsby-plugin-image gatsby-plugin-sharp&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;gatsby-config.js 파일에서 플러그인을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1671159988895&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = {
  plugins: [
    `gatsby-plugin-image`,
    `gatsby-plugin-sharp`,
  ],
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 플러그인은 이미지를 &lt;code&gt;StaticImage&lt;/code&gt; 컴포넌트로 제공합니다. Unsplash 이미지를 가져와 src 프롭에 슥 넣어봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1671165969907&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;StaticImage src=&quot;https://images.unsplash.com/photo-1588943211346-0908a1fb0b01?ixlib=rb-4.0.3&amp;amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=1035&amp;amp;q=80&quot; alt=&quot;&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cN4LMy/btrTMfwxwQk/EGHHN9kvZ4uoupqKKigupk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cN4LMy/btrTMfwxwQk/EGHHN9kvZ4uoupqKKigupk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;1068&quot; data-filename=&quot;Screen Shot 2022-12-16 at 1.45.33 PM.png&quot; style=&quot;width: 68.7842%; margin-right: 10px;&quot; data-widthpercent=&quot;69.59&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cN4LMy/btrTMfwxwQk/EGHHN9kvZ4uoupqKKigupk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcN4LMy%2FbtrTMfwxwQk%2FEGHHN9kvZ4uoupqKKigupk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1898&quot; height=&quot;1068&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3krw6/btrTMZf0TJ9/12IQofEXUvAodyvKCck3O0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3krw6/btrTMZf0TJ9/12IQofEXUvAodyvKCck3O0/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;510&quot; data-filename=&quot;Dec-16-2022 13-51-23.gif&quot; style=&quot;width: 30.053%;&quot; data-widthpercent=&quot;30.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3krw6/btrTMZf0TJ9/12IQofEXUvAodyvKCck3O0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3krw6%2FbtrTMZf0TJ9%2F12IQofEXUvAodyvKCck3O0%2Fimg.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;396&quot; height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/div&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;짠! 알아서 이미지 최적화를 해줍니다. 원본 파일은 jpeg인데 알아서 webp를 생성해준데다 srcset 설정까지 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 lazy load 설정까지 척척   placeholder는 기본적으로 dominantColor(가장 많이 추출된 색상값)로 설정되어 있지만 blurred 처리하는 것도 가능합니다.&lt;/p&gt;
&lt;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&gt;&lt;span&gt;하지만 저한테는 이미지 프로세싱보다 로컬에 있는 파일 정보를 긁어와 보여주는 플러그인이 더 절실했는데요.&lt;/span&gt;&lt;/span&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;span&gt;&lt;span&gt;그런 제게 딱 필요한 게 있었으니,&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;a href=&quot;https://www.gatsbyjs.com/plugins/gatsby-source-filesystem?=screen&quot;&gt;&lt;b&gt;gatsby-source-filesystem&lt;/b&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;말그대로 파일을 뒤져서(?) 저에게 정적 파일 데이터를 제공해줍니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;우선 패키지 설치 후 config 파일에 플러그인을 추가합니다.&lt;/span&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_1671166954680&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install gatsby-source-filesystem&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1671167044539&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `data`,
        path: `${__dirname}/static/`
      }
    },
  ],
}&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;GraphQL로 데이터 가져오기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 데이터를 어떻게 가져오죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GraphQL이라는 쿼리 언어를 써서 쉽게 데이터를 가져올 수 있어요!&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;data-layer.png&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1010&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Ggxk/btrTPtATo06/w4ndNpoXOlyoH0KZKjuXD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Ggxk/btrTPtATo06/w4ndNpoXOlyoH0KZKjuXD1/img.png&quot; data-alt=&quot;출처: https://www.gatsbyjs.com/docs/tutorial/part-4/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Ggxk/btrTPtATo06/w4ndNpoXOlyoH0KZKjuXD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Ggxk%2FbtrTPtATo06%2Fw4ndNpoXOlyoH0KZKjuXD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;514&quot; data-filename=&quot;data-layer.png&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1010&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://www.gatsbyjs.com/docs/tutorial/part-4/&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;웹 사이트에는 다양한 데이터가 있고 그 데이터들의 Source는 여러 군데로 나누어져 있습니다. (ex. 파일 시스템, DB, CMS, API...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 Source Plugin이라는 플러그인을 설치해서 제각각 특정 소스와 통신하게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 설치한 gatsby-source-filesystem은 파일 시스템과 통신하는 소스 플러그인인 거죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 플러그인이 가져온 데이터는 GraphQL Data라는 계층에 추가됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 우리가 할 일은? GraphQL 쿼리를 던져서 필요한 데이터를 가져오면 됩니다!&lt;/p&gt;
&lt;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;GraphiQL&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;npm run start를 했을 때 localhost:8000/___graphql 라는 엔드포인트를 알려주는 걸 볼 수 있습니다.&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;Screen Shot 2022-12-16 at 2.20.56 PM.png&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4V73E/btrTM0sHOeY/OHz32K1EBSE5MoiWENocUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4V73E/btrTM0sHOeY/OHz32K1EBSE5MoiWENocUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4V73E/btrTM0sHOeY/OHz32K1EBSE5MoiWENocUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4V73E%2FbtrTM0sHOeY%2FOHz32K1EBSE5MoiWENocUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;358&quot; data-filename=&quot;Screen Shot 2022-12-16 at 2.20.56 PM.png&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;738&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;각 필드를 탐색하고 클릭만으로 쉽게 쿼리를 작성할 수 있는데다 미리 실행하여 에러를 파악하는 것도 가능합니다 (WoW)22&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;Screen Shot 2022-12-16 at 2.40.28 PM.png&quot; data-origin-width=&quot;2452&quot; data-origin-height=&quot;1186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beLzXF/btrTLFvXS6v/jcRv1OqBVKU21E72s5rn50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beLzXF/btrTLFvXS6v/jcRv1OqBVKU21E72s5rn50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beLzXF/btrTLFvXS6v/jcRv1OqBVKU21E72s5rn50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeLzXF%2FbtrTLFvXS6v%2FjcRv1OqBVKU21E72s5rn50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;387&quot; data-filename=&quot;Screen Shot 2022-12-16 at 2.40.28 PM.png&quot; data-origin-width=&quot;2452&quot; data-origin-height=&quot;1186&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;config 파일에서 플러그인 옵션을 &lt;code&gt;{path: `${__dirname}/static/`}&lt;/code&gt;으로 지정했었죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 폴더엔 html 파일과 jpeg 파일을 두 개 넣어놨는데 GraphiQL에서 그 파일을 찾아봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;저처럼 외부 파일을 가져와서 표시해야 하는 경우, 루트에 &lt;a href=&quot;https://www.gatsbyjs.com/docs/how-to/images-and-media/static-folder/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;static이란 이름의 폴더&lt;/a&gt;를 만들어 사용합니다.&lt;br /&gt;그러면 빌드 시 static 폴더 안의 파일들은 건들지 않고 그대로 복사해 public에 넣어줍니다.&lt;/blockquote&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;Dec-16-2022 15-03-06.gif&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r0ODw/btrTMfqnzlf/EGh3TMKbC07cv3RJVkKgU1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r0ODw/btrTMfqnzlf/EGh3TMKbC07cv3RJVkKgU1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r0ODw/btrTMfqnzlf/EGh3TMKbC07cv3RJVkKgU1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/r0ODw/btrTMfqnzlf/EGh3TMKbC07cv3RJVkKgU1/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;800&quot; height=&quot;336&quot; data-filename=&quot;Dec-16-2022 15-03-06.gif&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클릭 몇 번 만으로 파일 데이터 정보를 손쉽게 가져왔습니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 코드 안에서 실제로 쿼리를 던져 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;graphql`` 안에 쿼리를 복사해 붙여넣으면 됩니다. 이때, 필터링을 해서 데이터를 받아올 수도 있어요. 아래는 확장자가 html인 파일만 가져오도록 한 코드입니다. REST API와 다르게 쿼리에 따라 다른 응답을 받아올 수 있다는 게 특징입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671171247458&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import { graphql, PageProps } from 'gatsby';
import PageMeta from '../components/PageMeta';

export default function Files({ data }: PageProps&amp;lt;Queries.MyFilesQuery&amp;gt;) {
  return (
    &amp;lt;&amp;gt;
      {data.allFile.nodes.map((file, index) =&amp;gt; (
        &amp;lt;div key={index}&amp;gt;{file.name}&amp;lt;/div&amp;gt;
      ))}
    &amp;lt;/&amp;gt;
  );
}

export const query = () =&amp;gt; graphql`
  query MyFiles {
    allFile(filter: { ext: { eq: &quot;.html&quot; } }) {
      nodes {
        name
      }
    }
  }
`;

export const Head = () =&amp;gt; &amp;lt;PageMeta title=&quot;파일 목록&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;config에서 &lt;code&gt;graphqlTypegen:true&lt;/code&gt; (디폴트 값)로 설정되어 있는 경우 쿼리 타입을 자동으로 생성해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gatsby-types.d.ts 파일 안에 알아서 눈치껏 생성해 주기에 제네릭으로 가져와 쓸 수 있습니다.&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;Screen Shot 2022-12-16 at 3.20.19 PM.png&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccNfhp/btrTNYV4HEf/f0nAHcQhvNedx4WyNzZ3K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccNfhp/btrTNYV4HEf/f0nAHcQhvNedx4WyNzZ3K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccNfhp/btrTNYV4HEf/f0nAHcQhvNedx4WyNzZ3K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccNfhp%2FbtrTNYV4HEf%2Ff0nAHcQhvNedx4WyNzZ3K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;217&quot; data-filename=&quot;Screen Shot 2022-12-16 at 3.20.19 PM.png&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;VS CODE에서 GraphQL이 제대로 표시되지 않는다면 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=GraphQL.vscode-graphql&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;익스텐션&lt;/a&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;StaticQuery&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;페이지가 아닌 컴포넌트에서 GraphQL을 쓰고 싶다면 &lt;a href=&quot;https://www.gatsbyjs.com/docs/how-to/querying-data/use-static-query/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;useStaticQuery를 사용&lt;/a&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 쿼리와는 다르게 변수를 받아와 쓸 수 없다는 차이점(그래서 &quot;static&quot;)이 있지만, 어떤 컴포넌트에서든 쿼리를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671172385690&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import { graphql, useStaticQuery } from 'gatsby';
import PageMeta from '../components/PageMeta';

export default function Files() {
  const { allFile: { nodes: data } } = useStaticQuery&amp;lt;Queries.getAllFileQuery&amp;gt;(graphql`
    query getAllFile {
      allFile {
        nodes {
          id
          name
          ext
          relativeDirectory
        }
      }
    }
  `);
  return (
    &amp;lt;&amp;gt;
      {data.map((file, index) =&amp;gt; (
        &amp;lt;p key={index}&amp;gt;{file.name}&amp;lt;/p&amp;gt;
      ))}
    &amp;lt;/&amp;gt;
  );
}

export const Head = () =&amp;gt; &amp;lt;PageMeta title=&quot;파일 목록&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;File System Route로 동적 페이지 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gatsby에서 라우트를 설정하는 방법은 pages/ 내에 컴포넌트를 위치시키는 것 말고도,&lt;br /&gt;&lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/routing/file-system-route-api/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;File System Route API&lt;/a&gt;를&amp;nbsp;사용하면&amp;nbsp;GraphQL에서&amp;nbsp;프로그래밍&amp;nbsp;방식으로&amp;nbsp;페이지를&amp;nbsp;생성하고&amp;nbsp;클라이언트&amp;nbsp;전용&amp;nbsp;경로를&amp;nbsp;만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &lt;code style=&quot;word-break: break-all;&quot;&gt;pages/{File.relativeDirectory}/{File.name}.tsx&lt;/code&gt; 는 디렉토리와 파일명으로 각 페이지를 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1671177942719&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* src/pages/{File.relativeDirectory}/{File.name}.tsx */

import React from 'react';
import { graphql } from 'gatsby';
import PageMeta from '../../components/PageMeta';

interface FilesProps {
  data: Queries.FileInfoQuery;
}

const File = ({ data: { file } }: FilesProps) =&amp;gt; {
  return &amp;lt;h1&amp;gt;{file?.name}&amp;lt;/h1&amp;gt;;
};

export default File;

export const query = () =&amp;gt; graphql`
  query FileInfo($name: String!) {
    file(name: { eq: $name }) {
      name
    }
  }
`;

export const Head = ({ data }: { data: Queries.FileInfoQuery }) =&amp;gt; (
  &amp;lt;PageMeta title={`${data.file?.name}`} /&amp;gt;
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-12-16 at 4.59.28 PM.png&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZfV76/btrTPQi69Dl/jYPhgWzRhohEuGUmKHUds0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZfV76/btrTPQi69Dl/jYPhgWzRhohEuGUmKHUds0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZfV76/btrTPQi69Dl/jYPhgWzRhohEuGUmKHUds0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZfV76%2FbtrTPQi69Dl%2FjYPhgWzRhohEuGUmKHUds0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;216&quot; data-filename=&quot;Screen Shot 2022-12-16 at 4.59.28 PM.png&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;coffee/ 폴더 아래 ameriacno와 latte 파일에 맞춰 페이지가 자동으로 생성됐습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 빌드하게 되면 Gatsby는 각 경로에 있는 페이지를 하나씩 정적으로 만들어 줍니다.&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;Screen Shot 2022-12-16 at 5.22.29 PM.png&quot; data-origin-width=&quot;504&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cewipv/btrTQQifjIK/9rs0ntZJtrPQIsNcDkJ7u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cewipv/btrTQQifjIK/9rs0ntZJtrPQIsNcDkJ7u0/img.png&quot; data-alt=&quot;빌드된 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cewipv/btrTQQifjIK/9rs0ntZJtrPQIsNcDkJ7u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcewipv%2FbtrTQQifjIK%2F9rs0ntZJtrPQIsNcDkJ7u0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;167&quot; data-filename=&quot;Screen Shot 2022-12-16 at 5.22.29 PM.png&quot; data-origin-width=&quot;504&quot; data-origin-height=&quot;280&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;이걸 보니 확실히 블로그 만들 때 좋을 것 같네요  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마크다운 문법으로 글 작성해서 mdx로 내보내면 받아올 수 있을 것 같은데요?!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넵, 하지만 그러려면 transformer plugin 설치가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671180440967&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install gatsby-plugin-mdx&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;소스의 데이터를 가져오기 위해 플러그인을 설치했던 것처럼, 데이터 형식을 적합하게 바꿔주는 변환기 플러그인을 설치하면 됩니다. MDX라면 gatsby-plugin-mdx 플러그인이 있어요!&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;data-layer-with-nodes.png&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baNjNZ/btrTQ0Zt1w7/1qUsys3KEdIi7rTMO3Odk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baNjNZ/btrTQ0Zt1w7/1qUsys3KEdIi7rTMO3Odk1/img.png&quot; data-alt=&quot;출처: https://www.gatsbyjs.com/docs/tutorial/part-5/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baNjNZ/btrTQ0Zt1w7/1qUsys3KEdIi7rTMO3Odk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaNjNZ%2FbtrTQ0Zt1w7%2F1qUsys3KEdIi7rTMO3Odk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;648&quot; data-filename=&quot;data-layer-with-nodes.png&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1274&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://www.gatsbyjs.com/docs/tutorial/part-5/&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;아까 데이터는 GraphQL data 계층에 저장된다고 했었는데요, 데이터 계층 내에서 정보들은 노드(node)라는 최소 단위의 객체에 저장됩니다. 이러한 노드들의 유형은 어떤 source plugin인가에 따라 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 노드의 유형을 변환하는 게 transformer plugin입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gatsby-source-filesystem이 데이터를 File 노드로 생성하고, gatsby-plugin-mdx 플러그인이 확장자가 .mdx인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;File 노드들을&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;MDX 노드로 바꿉니다. 이렇게 데이터를 가져올 때 원하는 구조나 형식으로 바꿔 가져올 수도 있고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671180103231&quot; class=&quot;md markdown&quot; data-ke-language=&quot;md&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* my-post.mdx */

---
title: &quot;My First Post&quot;
date: &quot;2022-12-25&quot;
slug: &quot;my-first-post&quot;
---

메리 크리스마스!&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1671180272773&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const query = () =&amp;gt; graphql`
  query BlogPosts {
    allMdx {
      nodes {
        frontmatter {
          title
          date(formatString: &quot;YYYY.MM.DD&quot;)
          slug
        }
        excerpt
      }
    }
  }
`;

const Blog = ({ data }: PageProps&amp;lt;Queries.BlogPostsQuery&amp;gt;) =&amp;gt; {
  return (
    &amp;lt;&amp;gt;
    {data.allMdx.nodes.map((file, index) =&amp;gt; (
      &amp;lt;article key={index}&amp;gt;
        &amp;lt;Link to={`${file.frontmatter?.slug}`}&amp;gt;
          &amp;lt;h2&amp;gt;{file.frontmatter?.title}&amp;lt;/h2&amp;gt;
          &amp;lt;strong&amp;gt;
            {file.frontmatter?.date}
          &amp;lt;/strong&amp;gt;
          &amp;lt;p&amp;gt;{file.excerpt}&amp;lt;/p&amp;gt;
        &amp;lt;/Link&amp;gt;
      &amp;lt;/article&amp;gt;
    ))}
    &amp;lt;/&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;Gatsby로 블로그 만드는 법은 &lt;a href=&quot;https://www.gatsbyjs.com/docs/tutorial/part-5/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 튜토리얼&lt;/a&gt;이나 좋은 강좌들이 많기 때문에 생략합니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이정도면 제가 원하는 걸 만들기엔 충분했기에 플러그인을 설치해 작업했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. static/ 아래 정적 html 파일을 둔다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. File System Route로 새 html 파일이 추가될 때마다 {File.name}으로 경로가 만들어지도록 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. {File.name} 내에 해당하는 정적 html 파일을 iframe으로 표시하고 관련된 정보를 표시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. {File.name} 내에서 파일 다운로드 및 새창열기 클릭 시, 원본 static 파일의 URI로 연결시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. GNB에 allFile 정보를 가져와 맵으로 고이 뿌려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. GNB 클릭 시 Link 컴포넌트로 해당 페이지에 연결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 끝 -&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-12-16 at 6.03.18 PM.png&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;1136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chpyof/btrTRpLCllp/Spe9KokLWOqQv56vlPdkn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chpyof/btrTRpLCllp/Spe9KokLWOqQv56vlPdkn1/img.png&quot; data-alt=&quot;좌측 GNB 클릭시 해당하는 로컬 파일을 우측에 표시하여 빠른 탐색이 가능해졌습니다  &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chpyof/btrTRpLCllp/Spe9KokLWOqQv56vlPdkn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fchpyof%2FbtrTRpLCllp%2FSpe9KokLWOqQv56vlPdkn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;487&quot; data-filename=&quot;Screen Shot 2022-12-16 at 6.03.18 PM.png&quot; data-origin-width=&quot;1866&quot; data-origin-height=&quot;1136&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;좌측 GNB 클릭시 해당하는 로컬 파일을 우측에 표시하여 빠른 탐색이 가능해졌습니다  &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 data-ke-size=&quot;size26&quot;&gt;gh-pages로 배포하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포하려면 우선 빌드해야겠죠! &lt;code&gt;gatsby build&lt;/code&gt;를 입력하면 빌드해서 public/ 폴더를 만들어 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로덕션 빌드를 미리 보고 싶다면 &lt;code&gt;gatsby serve&lt;/code&gt;를 실행합니다. 이는 localhost:9000 포트에서 볼 수 있어요.&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;Screen Shot 2022-12-19 at 4.20.05 PM.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNUtH2/btrT5r90gx5/CkXMsCfQQriyGxbHMiVls1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNUtH2/btrT5r90gx5/CkXMsCfQQriyGxbHMiVls1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNUtH2/btrT5r90gx5/CkXMsCfQQriyGxbHMiVls1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNUtH2%2FbtrT5r90gx5%2FCkXMsCfQQriyGxbHMiVls1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;169&quot; data-filename=&quot;Screen Shot 2022-12-19 at 4.20.05 PM.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발이나 빌드 중 에러가 뜬다면 &lt;code&gt;gatsby clean&lt;/code&gt;으로 말끔하게 청소해주면 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 빌드를 하게 되면 Gatsby는 필요한 모든 데이터가 있는 페이지들을 미리 구축하고 컴파일한 사이트를 내놓습니다. 그래서 배포 후에는 SSR이 필요하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드하는 동안 사이트에 필요한 모든 데이터를 정적 스냅샷과 함께 GraphQL 스키마로 결합됩니다. 빌드 시 이미 데이터가 수집되었기 때문에 네트워크를 통해 이동할 필요가 없고, 모든 데이터가 데이터 레이어에 결합되기 때문에 동시에 여러 소스를 쿼리하는 것도 가능합니다. (&lt;a href=&quot;https://www.gatsbyjs.com/docs/conceptual/overview-of-the-gatsby-build-process/#what-happens-when-you-run-gatsby-build&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;출처&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 빌드된 파일을 고이 들고 배포할 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 깃페이지로 배포하기 위해 gh-pages 패키지를 사용했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1671434642764&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i gh-pages&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;깃헙 레포 설정 화면에 들어가 Pages에서 배포용 브랜치를 선택합니다.&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;Screen Shot 2022-12-19 at 4.38.33 PM.png&quot; data-origin-width=&quot;1602&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GgaAy/btrTUEw6mKM/eyqYQkhckmn53TK6dv0E0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GgaAy/btrTUEw6mKM/eyqYQkhckmn53TK6dv0E0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GgaAy/btrTUEw6mKM/eyqYQkhckmn53TK6dv0E0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGgaAy%2FbtrTUEw6mKM%2FeyqYQkhckmn53TK6dv0E0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;361&quot; data-filename=&quot;Screen Shot 2022-12-19 at 4.38.33 PM.png&quot; data-origin-width=&quot;1602&quot; data-origin-height=&quot;722&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;package.json 에서 배포용 브랜치에 배포되도록 설정하면 끝!&lt;/p&gt;
&lt;pre id=&quot;code_1671435425249&quot; class=&quot;json&quot; data-ke-language=&quot;json&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
    &quot;deploy&quot;: &quot;gatsby build &amp;amp;&amp;amp; gh-pages -d public -b deploy&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;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유의할 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성한 코드 내용 중, window.location을 활용해 판단하는 구문이 있었는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 모드일 때는 정상 동작하지만 빌드를 돌리면 에러가 발생했습니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-12-19 at 5.22.47 PM.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCee0k/btrTXMuKFka/ClHL3fv7B59kHjuolDlys0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCee0k/btrTXMuKFka/ClHL3fv7B59kHjuolDlys0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCee0k/btrTXMuKFka/ClHL3fv7B59kHjuolDlys0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCee0k%2FbtrTXMuKFka%2FClHL3fv7B59kHjuolDlys0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;388&quot; data-filename=&quot;Screen Shot 2022-12-19 at 5.22.47 PM.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;738&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;이유는 Node.js 환경에서는 window나 document 같이 브라우저 전역을 참조하는 게 불가능하기 때문입니다. (&lt;a href=&quot;https://www.gatsbyjs.com/docs/debugging-html-builds/&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;code&gt;gatsby devlop(또는 npm run start)&lt;/code&gt;은 런타임이고, &lt;code&gt;gatsby build&lt;/code&gt;는 빌드타임입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드타임에는 브라우저를 실행할 수 없으니 당연히 브라우저 API를 호출할 수 없겠죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 window 객체를 찾을 수 없다면 방어하는 내용이 필요합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1671437073152&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const isBrowser = typeof window !== &quot;undefined&quot;

if (isBrowser) {
window.localStorage.getItem(&quot;isLoggedIn&quot;) === &quot;true&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;아니면 루트 페이지에서부터 location 객체를 자식 컴포넌트로 계속 보낼 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671437308869&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* src/pages/index.tsx */

const IndexPage = ({ location }: { location: Location }) =&amp;gt; {
  return &amp;lt;PageLayout location={location}/&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;만약 개발모드에서 SSR과 관련해 경고가 필요하다면 config에서 설정하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671436101752&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = {
  flags: {
    DEV_SSR: true
  },
  plugins: [...]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-12-19 at 5.05.05 PM.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DB7mj/btrT41X8iRR/4oykNugGag3FSLKj3su5l0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DB7mj/btrT41X8iRR/4oykNugGag3FSLKj3su5l0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DB7mj/btrT41X8iRR/4oykNugGag3FSLKj3su5l0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDB7mj%2FbtrT41X8iRR%2F4oykNugGag3FSLKj3su5l0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;215&quot; data-filename=&quot;Screen Shot 2022-12-19 at 5.05.05 PM.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;502&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG(not 마트)를 짧게 찍먹해보았습니다만 음 맛있네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;정적 사이트를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;쉽게&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;구축할 수 있다는 점에서 퍼블리셔에게도 좋은 선택지가 될 수 있을 것 같아요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방치해둔지 n년째인 포트폴리오 사이트도 개츠비를 활용해 업뎃해볼까 고려하고 있습니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그레잇한 개츠비네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Untitled.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8pwmO/btrTKclLAjs/ZL6Vss2hSu8IbLjYCBUL60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8pwmO/btrTKclLAjs/ZL6Vss2hSu8IbLjYCBUL60/img.png&quot; data-alt=&quot;생각해보니 위대한 건... 개츠비의 얼굴인가...?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8pwmO/btrTKclLAjs/ZL6Vss2hSu8IbLjYCBUL60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8pwmO%2FbtrTKclLAjs%2FZL6Vss2hSu8IbLjYCBUL60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;250&quot; data-filename=&quot;edited_Untitled.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&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;</description>
      <category>Blog/Tools</category>
      <category>GATSBY</category>
      <category>React</category>
      <author>나나 (nykim)</author>
      <guid isPermaLink="true">https://anneslab.tistory.com/116</guid>
      <comments>https://anneslab.tistory.com/116#entry116comment</comments>
      <pubDate>Tue, 20 Dec 2022 00:11:43 +0900</pubDate>
    </item>
    <item>
      <title>[아무튼 Sass] 4. 더 멋지게 활용하기 - 흐름제어, 내장 모듈, 함수</title>
      <link>https://anneslab.tistory.com/115</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cover copy.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJ8yuf/btrQr6CnEOv/oAh4rfZ1PI2SJKIY3HsKfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJ8yuf/btrQr6CnEOv/oAh4rfZ1PI2SJKIY3HsKfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJ8yuf/btrQr6CnEOv/oAh4rfZ1PI2SJKIY3HsKfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJ8yuf%2FbtrQr6CnEOv%2FoAh4rfZ1PI2SJKIY3HsKfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;450&quot; data-filename=&quot;cover copy.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIH71I%2FbtrQj6cJPDb%2FvkCYAMM9ktCGa6WL5r7e4K%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프롤로그&lt;/h2&gt;
&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;흐름제어와 함수를 통해 Sass를 더 멋진 방법으로 활용해 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #ff1a1a;&quot;&gt;&lt;b&gt;아무튼 Sass 시리즈&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;https://nykim.work/97&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;1. 시작하기 - Sass 개념, 컴파일러 설치&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://nykim.work/111&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2. 기본 작성법 익히기 - 중첩, 참조, 변수, 보간&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://nykim.work/113&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;3. 더 편하게 CSS 다루기 - mixin, extend, 모듈화(import/use)&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;4. 더 멋지게 활용하기 - 흐름제어, 내장 모듈, 함수&lt;/b&gt;&lt;/span&gt;&amp;nbsp;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;  Here!&lt;/span&gt;&lt;/i&gt;&lt;/blockquote&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 style=&quot;color: #666666;&quot;&gt;이번 글에서 다루는 것:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;- @if, @each 등의 흐름제어문&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;- list 변수와 map 변수&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;- 내장 모듈이 제공하는 사용하기&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIH71I%2FbtrQj6cJPDb%2FvkCYAMM9ktCGa6WL5r7e4K%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sass의 기본 특징들은 다 살펴봤으니 이번에는 좀더 기깔나게 활용해 볼 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 흐름 제어문이라고도 부르는 @At-Rules들을 살펴봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@if&lt;/code&gt;, &lt;code&gt;@each&lt;/code&gt;, &lt;code&gt;@for&lt;/code&gt;, &lt;code&gt;@while&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름만 봐도 어떻게 써야 할지 감이 오는데요   Sass 내에서 조건문/반복문을 위해 사용합니다.&lt;/p&gt;
&lt;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;@if, @else&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말그대로 조건에 따라 스타일을 반환합니다. 그래서&amp;nbsp;&lt;b&gt;mixin과 찰떡궁합&lt;/b&gt;이에요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인자로 뭘 받아오느냐에 따라 분기할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 글자를 말줄임 처리하는 믹스인입니다. 상황에 따라 1줄 또는 그 이상으로 말줄임 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1666249176395&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@mixin ellipsis($lines: 1) {
  @if ($lines==1) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  } @else {
    display: -webkit-box;
    overflow: hidden;
    text-overflow: ellipsis;
    -webkit-line-clamp: $lines;
    -webkit-box-orient: vertical;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1666249362209&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.text { @include ellipsis; }
.long-text { @include ellipsis(3); }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$lines&lt;/code&gt;라는 인자가 뚫려있고 그 값에 따라 다른 스타일을 적용해주고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인자가 들어오지 않는다면 기본값인 1이 들어왔다고 칠 거고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;code&gt;.text&lt;/code&gt; 는 첫 번째 조건(@if 아래)의 스타일을 적용받고, &lt;code&gt;.long-text&lt;/code&gt;는 두 번째 조건(@else 아래)의 스타일을 적용받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@else 문 없이 @if 문만 써도 되고, @else if로 추가 분기를 하는 것도 가능합니다.&lt;/p&gt;
&lt;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 class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;OJEXKeB&quot; data-user=&quot;nykim_&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/nykim_/pen/OJEXKeB&quot;&gt; Untitled&lt;/a&gt; by nanalike (&lt;a href=&quot;https://codepen.io/nykim_&quot;&gt;@nykim_&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이렇게 쓰지 않고 믹스인을 두 개 만들어서 각각 호출해도 되긴 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 사실상 거의 유사한 스타일이기 때문에 같은 이름 아래에서 분기하는 게 좀 더 관리하기 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭣보다... 믹스인 이름을 하나만 외워도 됩니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@for&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 숫자 범위 내에서 반복시킬 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;현재 해당하는 인덱스(주로 $i로 표현)를 활용해야 할 때 요긴&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;예를 들어 아이템 10개의 그림자가 겹쳐지는데 역순으로 z-index가 높아야 하는 상황입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1666250956988&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.item:nth-child(1) { z-index: 10; }
.item:nth-child(2) { z-index: 9; }
.item:nth-child(3) { z-index: 8; }
.item:nth-child(4) { z-index: 7; }
.item:nth-child(5) { z-index: 6; }
// ...&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;이런 식으로 한땀한땀 정성스럽게 z-index를 지정해줘도 되지만 귀찮으니까 반복문을 돌려버리죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nth-child가 1에서 하나씩 올라가고 그에 따라 z-index는 하나씩 내려가기 때문에 1부터 10까지 반복하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@for {반복자} from {시작} through {끝}&lt;/code&gt; 으로 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1666252574461&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.item {
  $_length: 10;
  $_color: #b2c0d5;

  @for $i from 1 through $_length {
    &amp;amp;:nth-child(#{$i}) {
      z-index: ($_length + 1) - $i;
      background-color: color.scale($_color,  $lightness: $i * -5%);
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;oNdrwJX&quot; data-user=&quot;nykim_&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/nykim_/pen/oNdrwJX&quot;&gt; Untitled&lt;/a&gt; by nanalike (&lt;a href=&quot;https://codepen.io/nykim_&quot;&gt;@nykim_&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&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 data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;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;:nth-child와 $i를 결합해 귀찮은 반복 노가다를 손쉽게 끝내버렸습니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;code&gt;color.scale()&lt;/code&gt;라는 걸 통해 각각의 배경색도 다르게 줬는데요, 이러한 Sass 함수에 대해서도 잠시 후 다뤄봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;@for 반복문에는 through 대신 to를 쓰는 방법도 있는데, 이는 끝-1 만큼만 반복합니다.&lt;br /&gt;1) &lt;code style=&quot;display: inline-block; border: 1px solid #aaa; padding: 0px 2px; font-size: 80%; background-color: #fefefe; border-radius: 3px;&quot;&gt;@for $i from 1 through 5&lt;/code&gt; = 1~5만큼 반복&lt;br /&gt;2) &lt;code style=&quot;display: inline-block; border: 1px solid #aaa; padding: 0px 2px; font-size: 80%; background-color: #fefefe; border-radius: 3px;&quot;&gt;@for $i from 1 to 5&lt;/code&gt; = 1~4만큼 반복&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;h3 data-ke-size=&quot;size23&quot;&gt;@each&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 목록 내에서 반복시킬 때 사용합니다. &lt;b&gt;list, map 변수와 함께 쓰면 더욱 좋아요.&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;Sass에서 변수는 &lt;code&gt;$변수명: 값;&lt;/code&gt;으로 선언해 다양한 값을 담을 수 있다! 고 했는데요,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그중에는 좀 특이하게 생긴 list와 map이 있습니다.&lt;/p&gt;
&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;먼저 list는 말그대로 하나의 변수에 여러 개의 값을 담고 있는 형태입니다. 배열이랑 비슷해요!&lt;/p&gt;
&lt;pre id=&quot;code_1666253479726&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 리스트
$list: facebook, youtube, instagram;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대충 어떻게 써야할지 감이 오지 않나요?!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안에 든 것들을 꺼내서 반복시키기 딱 좋게 생겼습니다  &lt;/p&gt;
&lt;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;주어진 목록 내에서 반복시킬 때는 @each를 사용한다고 했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@each {현재 요소를 가리키는 이름} in {변수 이름}&lt;/code&gt; 형태로 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1666256029717&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.sns {
  $_list: facebook, youtube, instagram;

  @each $sns in $_list {
    &amp;amp;.#{$sns} {
      background-image: url(./img/icon-#{$sns}.png);
    }
  }
}

/*
 * 출력결과:
 
 .sns.facebook { background-image: url(./img/icon-facebook.png); }
 .sns.youtube { background-image: url(./img/icon-youtube.png); }
 .sns.instagram { background-image: url(./img/icon-instagram.png); }

*/&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;한편 map 변수는 어떻게 생겼을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얘는 key와 value가 한 쌍을 이루는 형태입니다. JSON이랑 비슷하죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1666256723792&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 맵
$_map: (
  'facebook': #d3e6f3,
  'instagram': #e9e9e9,
  'youtube': #ffe0e0
);&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;얘는 key 뿐만 아니라 value 값도 담고 있기 때문에 더 복잡한 활용도 가능해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 @each문과 찰떡인데, value가 있기 때문에 &lt;code&gt;@each {현재 요소}, {현재 요소의 값} in {변수}&lt;/code&gt; 형태로 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1666256791508&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.sns {
  $_map: (
    'facebook': #d3e6f3,
    'instagram': #e9e9e9,
    'youtube': #ffe0e0
  );

  @each $sns, $color in $_map {
    &amp;amp;.#{$sns} {
      background-color: $color;
    }
  }
}

/*
 * 출력결과:
 
 .sns.facebook { background-color: #d3e6f3; }
 .sns.instagram { background-color: #e9e9e9; }
 .sns.youtube { background-color: #ffe0e0; }

*/&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&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/modest-robinson-hbiqyg?autoresize=1&amp;amp;fontsize=14&amp;amp;hidenavigation=1&amp;amp;module=%2Fsrc%2Fmodule.scss&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;300&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;이렇게 Sass문을 쓰면 각각 하나씩 치지 않아도 되니 덜 귀찮죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 &lt;b&gt;수정이 한 군데서 일어나기 때문에 관리하기 쉽다&lt;/b&gt;는 점이 매력적입니다. 나중에 변경사항이 생기면 위의 변수 부분만 수정해주면 되니까요  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;@while&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건이 true인 동안 반복시킵니다. 하지만 다른 반복문인 @for나 @each가 더 직관적이기 때문에 거의 사용하지 않습니다. (=찬밥신세)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 &lt;code&gt;$idx&lt;/code&gt;라는 변수값을 계속 바꿀 수 있다는 점을 포인트로 봐주세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$idx + 1처럼 숫자만큼 증가시키거나 calc()내에 넣어 계산된 값을 쓸 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1666863102369&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.item {
  $idx: 1;
  @while $idx &amp;lt; 5 {
    &amp;amp;:nth-child(#{$idx}) {
      filter: grayscale(1);
      font-size: calc(10vmin + #{$idx * 2}vmin);
    }
    $idx: $idx + 1;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;MWXYqzG&quot; data-user=&quot;nykim_&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/nykim_/pen/MWXYqzG&quot;&gt; Untitled&lt;/a&gt; by nanalike (&lt;a href=&quot;https://codepen.io/nykim_&quot;&gt;@nykim_&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIH71I%2FbtrQj6cJPDb%2FvkCYAMM9ktCGa6WL5r7e4K%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sass의 매력 중 하나는 '네가 필요할 것 같아서 미리 준비해왔어'라는 쎈스가 넘친다는 점  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색상 값을 계산하거나 맵 변수의 특정 값을 알아내는 등, 미리 정의된 빌트-인 함수를 제공하고 있어 언제든 가져와 쓸 수 있어요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;색상 함수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼에 hover 했을 때 색을 좀 더 밝게 만들고자 합니다. 이때 색상값을 직접 계산하기 귀찮으므로 함수인 &lt;code&gt;scale()&lt;/code&gt;을 써봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해선 우선 &lt;b&gt;sass:color라는 내장 모듈을 가져와야 하므로 @use 규칙으로 가져와 주세요.&lt;b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667363638477&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'sass:color'; // 모듈 호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 간단해요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;{네임스페이스}.함수()&lt;/code&gt; 형태에 인자를 넣어 사용하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667296347701&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'sass:color'; // 모듈 호출
$navy: #0a4069; // 변수 선언

.btn {
  background-color: $navy; // 변수 그대로 사용

  &amp;amp;:hover {
    background-color: color.scale($navy, $lightness: 20%); // 색상값 변환 후 사용
  }
}&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 class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;OJERLEK&quot; data-user=&quot;nykim_&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/nykim_/pen/OJERLEK&quot;&gt; Untitled&lt;/a&gt; by nanalike (&lt;a href=&quot;https://codepen.io/nykim_&quot;&gt;@nykim_&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;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;예제에서 버튼에 hover 하면 배경이 기본 색상(#0a4069)에서 20% 만큼 밝아진 색상(#116cb1)이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 $navy의 값이 달라지더라도 거기에 맞춰 20% 만큼 밝아질 테니 hover 색상을 따로 수정할 필요가 없어지죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 눈썰미 좋은 분은 scss 코드 내에 &lt;code&gt;rgb()&lt;/code&gt;라는 함수와 &lt;code&gt;color.red()&lt;/code&gt;, &lt;code&gt;color.green()&lt;/code&gt;, &lt;code&gt;color.blue()&lt;/code&gt; 라는 함수가 있는 걸 발견하셨을 텐데요  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들도 Sass 내장 모듈이 제공하는 함수에요!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;red(), green(), blue()로 r,g,b 값을 받아온 뒤 rgb()에 넣어 HEX 값을 얻었습니다.&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://sass-lang.com/documentation/modules/color&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;를 참고해 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 90%; height: 114px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;width: 15%; height: 38px;&quot;&gt;&lt;b&gt;scale()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%; height: 38px;&quot;&gt;색상의 rgb, hsl 등을 -100%에서 +100%까지 조절한 값을 리턴&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 38px;&quot;&gt;&lt;code&gt;color.scale(#998099, $alpha: -40%); // rgba(153, 128, 153, 0.6)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;&lt;b&gt;mix()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%;&quot;&gt;색상끼리 혼합한 색상값을 리턴&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;code&gt;color.mix(#036, #d2e1dd, 75%); // #355f84&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 15%; height: 19px;&quot;&gt;&lt;b&gt;invert()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%; height: 19px;&quot;&gt;대비되는 색상값을 리턴&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;code&gt;color.invert(black); // white&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;width: 15%; height: 38px;&quot;&gt;&lt;b&gt;grayscale()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%; height: 38px;&quot;&gt;동일한 밝기의 회색값을 리턴. color.change($color, $saturation: 0%)와 동일&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 38px;&quot;&gt;&lt;code&gt;color.grayscale(#6b717f); // #757575&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Sass에 모듈 시스템이 생기기 전에는 이런 빌트인 함수도 전역으로 사용했습니다. &lt;br /&gt;Sass 팀은 전역으로 쓰지 않는 것을 추천하지만, 필요하다면 호환성을 위해 네임스페이스 없이 전역으로 사용할 수 있습니다.&lt;br /&gt;다만 &lt;code style=&quot;display: inline-block; border: 1px solid #aaa; padding: 0px 2px; font-size: 80%; background-color: #fefefe; border-radius: 3px;&quot;&gt;if()&lt;/code&gt;, &lt;code style=&quot;display: inline-block; border: 1px solid #aaa; padding: 0px 2px; font-size: 80%; background-color: #fefefe; border-radius: 3px;&quot;&gt;hsl()&lt;/code&gt;, &lt;code style=&quot;display: inline-block; border: 1px solid #aaa; padding: 0px 2px; font-size: 80%; background-color: #fefefe; border-radius: 3px;&quot;&gt;rgb()&lt;/code&gt;는 멤버명 없이 전역으로 사용할 수 있습니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;기존에 Sass를 쓰셨다면 색상을 밝게 만들 때 lighten() 함수가 더 익숙한 분들도 계실 거에요.&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;하지만 &lt;code style=&quot;display: inline-block; border: 1px solid #aaa; padding: 0px 2px; font-size: 80%; background-color: #fefefe; border-radius: 3px;&quot;&gt;lighten()&lt;/code&gt;이나 &lt;code style=&quot;display: inline-block; border: 1px solid #aaa; padding: 0px 2px; font-size: 80%; background-color: #fefefe; border-radius: 3px;&quot;&gt;darken()&lt;/code&gt;은 종종 원하는 밝기가 아닌, 고정된 양만큼의 밝기를 변화시키는 문제가 있어 새 모듈에 포함되지 못했습니다..... &lt;br /&gt;호환성을 위해 해당 함수를 아직 사용할 수 있긴 하지만 더 정확한 값을 얻으려면 &lt;code style=&quot;display: inline-block; border: 1px solid #aaa; padding: 0px 2px; font-size: 80%; background-color: #fefefe; border-radius: 3px;&quot;&gt;scale()&lt;/code&gt; 사용을 추천합니다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;list, map 함수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;list와 map 변수를 인자로 받아 특정 값을 리턴합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얘도 정말 다양한 함수를 제공하는데, 저는 주로 &lt;code&gt;nth()&lt;/code&gt;와 &lt;code&gt;get()&lt;/code&gt; 함수를 사용해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 nth()는 n번째에 해당하는 list 변수의 값을 알려줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 3개의 값이 들어 있는 fontSize 변수에서 3번째 값을 가져와 사용한 예시입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하기 전 @use로 모듈 가져온 부분도 체크해 보세요! (하지만 네임스페이스 없이 쓰는 것도 가능해요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667364366426&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'sass:list';

$fontSize: 12px, 16px, 20px;

.title {
  font-size: list.nth($fontSize, 3); //결과: 20px
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 &lt;code&gt;map.get()&lt;/code&gt;이란 함수를 써봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;font-weight의 값을 map 변수로 만든 상태인데요, 여기서 'bold'에 해당하는 값을 알아내기 위해 get()을 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667363231623&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'sass:map';

$weight: (
  'light': 300,
  'regular': 400,
  'bold': 700
);

.title {
  font-weight: map.get($weight, 'bold'); //결과: 700
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 내장 함수는 반복문이나 조건문과 결합하면 효율을 더 극대화할 수 있어요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 @for은 1씩 늘어나는 인덱스를 사용하니 list 내부를 돌면서 인덱스와 일치하는 값을 쓰도록 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667366905161&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'sass:list';
@use 'sass:color';

$color: red, orange, yellow, green, blue;

.lv {
  @for $i from 1 through list.length($color) {
    &amp;amp;:nth-child(#{$i}) {
      background-color: list.nth($color, $i);
      color: color.invert(list.nth($color, $i));
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;list.nth()&lt;/code&gt;함수를 통해 $i번째 $color값을 알아내 자동으로 넣어줬습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 반복문의 횟수를 &lt;code&gt;length()&lt;/code&gt;로 지정했기 때문에 $color 길이가 늘거나 줄어도 신경쓸 필요가 없어요. 와! 진.짜.편.합.니.다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나 더. color 값을 보면 nth()를 &lt;code&gt;invert()&lt;/code&gt;로 감싸고 있는 걸 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴된 값을 알아서 잘 가져다 쓰기 때문에 이처럼 함수를 인자로 넣어 쓰는 것도 가능합니다. 물론 @if와 같은 조건문 안에 넣는 것도 가능하고요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 한번에 너무 많이 감싸는 경우 가독성이 떨어지므로 중간에 지역 변수로 잘라내는 것도 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;qBKbeKV&quot; data-user=&quot;nykim_&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/nykim_/pen/qBKbeKV&quot;&gt; Untitled&lt;/a&gt; by nanalike (&lt;a href=&quot;https://codepen.io/nykim_&quot;&gt;@nykim_&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;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;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;width: 15%; height: 38px;&quot;&gt;&lt;b&gt;nth()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%; height: 38px;&quot;&gt;리스트의 n번째에 해당하는 값을 리턴 (음수인 경우 뒤에서부터 계산)&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 38px;&quot;&gt;&lt;code&gt;list.nth(10px 12px 16px, 2); // 12px&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 15%; height: 19px;&quot;&gt;&lt;b&gt;length()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%; height: 19px;&quot;&gt;리스트의 길이를 리턴&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;code&gt;list.length(10px 20px 30px); // 3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 15%; height: 19px;&quot;&gt;&lt;b&gt;set-nth()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%; height: 19px;&quot;&gt;리스트의 특정 n번째 요소를 교체하여 리턴&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 19px;&quot;&gt;&lt;code&gt;list.set-nth(10px 20px, 1, 1px); // 1px 20px&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;&lt;b&gt;get()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%;&quot;&gt;맵의 키와 연결된 값을 리턴&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;code&gt;map.get($font-weights, &quot;Bold&quot;);&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;&lt;b&gt;set()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%;&quot;&gt;맵의 키와 연결된 값을 수정하여 리턴&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;code&gt;map.set($font-weights,&amp;nbsp;&quot;regular&quot;,&amp;nbsp;300);&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;&lt;b&gt;has-key()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%;&quot;&gt;맵의 키가 존재하는지 여부&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;code&gt;map.has-key($font-weights, &quot;semiBold&quot;);&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;&lt;b&gt;keys()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%;&quot;&gt;맵의 모든 키를 쉼표로 구분하여 리턴&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;code&gt;map.keys($font-weights);&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;&lt;b&gt;values()&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35%;&quot;&gt;맵의 모든 값을 쉼표로 구분하여 리턴&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;code&gt;map.values($font-weights);&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 자세한 내용은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://sass-lang.com/documentation/modules/list&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;를 참고해 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그 외&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그밖에 내장 모듈로는 숫자를 계산해주는 &lt;code&gt;sass:math&lt;/code&gt;, 문자열을 다루는 &lt;code&gt;sass:string&lt;/code&gt;, 다른 모듈을 평가할 수 있는 &lt;code&gt;sass:meta&lt;/code&gt;, 선택자를 평가하는 &lt;code&gt;sass:selecter&lt;/code&gt;가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서부터 그 외로 묶이는 이유는... 저도 잘 안 써서 어색하기 때문... 사실 이 글 쓰면서 알게 된 함수가 더 많습니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667370647578&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'sass:math';
h1 {
 position: absolute;
 left: #{math.ceil(48.8)}px; // 올림처리 = 50px
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1667371670242&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'sass:selector';

#{selector.append(&quot;.button&quot;, &quot;.active, .disabled&quot;)} { // 셀렉터 묶기
	color: red;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1667370667280&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'sass:meta';

@function add($a, $b) {
  @return $a + $b;
}

.title {
  @if meta.function-exists('add') { // 해당 함수가 존재하는지 판단
    font-size: #{add(50, 30)}px; // true
  } @else {
    font-size: 20px;
  }
}&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIH71I%2FbtrQj6cJPDb%2FvkCYAMM9ktCGa6WL5r7e4K%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 함수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내장 함수도 좋지만 '아 뭔가 딱 나한테 맞는 기능의 함수가 없네...' 할 때! 직접 함수를 만들어 쓸 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@function&lt;/code&gt; 으로 함수를 선언하고 &lt;code&gt;@return&lt;/code&gt; 으로 리턴값을 설정해주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 익혀보죠! &lt;a href=&quot;https://nykim.work/85&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;이 포스팅&lt;/b&gt;&lt;/a&gt;에 나온 것처럼 html의 폰트 크기를 62.5%로 잡고 나머지를 rem 단위로 작업하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 시안은 px 단위기 때문에 작성할 때 변환이 필요합니다. 5px &amp;rarr; 0.5rem, 200px &amp;rarr; 20rem처럼요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 머리속에서 계산하고 써도 되지만, 좀 더 안전하게 작성하기 위해 함수를 쓰기로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667440399603&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@function rem($px) {
  @return #{calc($px / 10)}rem;
}

.logo {
  width: rem(70); // 70px = 7rem
  height: rem(30); // 30px = 3rem
}&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;인자로 넘어온 $px 값을 10으로 나눠 리턴하는 단순한 함수입니다. @return으로 리턴값을 명시해야 하는 것에 주의하세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 때는 내장 함수를 썼던 것처럼 호출해 주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 예제는 함수가 같은 파일 안에 있어서 네임스페이스가 없지만 함수가 다른 파일에 있다면 모듈을 불러와야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667440727337&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'fn';

.logo {
  width: fn.rem(70);
  height: fn.rem(30);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 내에서 다른 함수를 불러와 쓰는 것도 물론 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 SVG 아이콘을 변수에 저장한 다음 함수를 호출해 조합해서 사용하는 예제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(백그라운드 이미지로 SVG 사용하는 방법은 &lt;a href=&quot;https://nykim.work/95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;요 포스팅&lt;/b&gt;&lt;/a&gt;을 참고하세요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667445263331&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'sass:map';
@use 'sass:string';

$icons: (
  sun: 'M23.9922 38.4651C24.6394 38.4651 25.1717 38.957 25.2358 39.5873L25.2422 39.7151V42.7527C25.2422 43.443 24.6826 44.0027 23.9922 44.0027C23.345 44.0027 22.8127 43.5108 22.7487 42.8805L22.7422 42.7527V39.7151C22.7422 39.0248 23.3019 38.4651 23.9922 38.4651ZM35.8947 34.0978L35.9962 34.1889L38.1441 36.3368C38.6322 36.8249 38.6322 37.6164 38.1441 38.1046C37.6884 38.5602 36.9686 38.5905 36.4778 38.1957L36.3763 38.1046L34.2284 35.9567C33.7403 35.4685 33.7403 34.6771 34.2284 34.1889C34.684 33.7333 35.4039 33.7029 35.8947 34.0978ZM13.755 34.1889C14.2106 34.6445 14.241 35.3644 13.8461 35.8552L13.755 35.9567L11.6071 38.1046C11.119 38.5927 10.3275 38.5927 9.83937 38.1046C9.38376 37.6489 9.35339 36.9291 9.74825 36.4383L9.83937 36.3368L11.9872 34.1889C12.4754 33.7008 13.2668 33.7008 13.755 34.1889ZM23.9999 13.0805C30.0306 13.0805 34.9194 17.9693 34.9194 24C34.9194 30.0306 30.0306 34.9194 23.9999 34.9194C17.9693 34.9194 13.0805 30.0306 13.0805 24C13.0805 17.9693 17.9693 13.0805 23.9999 13.0805ZM23.9999 15.5805C19.35 15.5805 15.5805 19.3501 15.5805 24C15.5805 28.6499 19.35 32.4194 23.9999 32.4194C28.6499 32.4194 32.4194 28.6499 32.4194 24C32.4194 19.3501 28.6499 15.5805 23.9999 15.5805ZM42.7308 22.787C43.4212 22.787 43.9808 23.3467 43.9808 24.037C43.9808 24.6842 43.489 25.2166 42.8586 25.2806L42.7308 25.287H39.6933C39.0029 25.287 38.4433 24.7274 38.4433 24.037C38.4433 23.3898 38.9352 22.8575 39.5655 22.7935L39.6933 22.787H42.7308ZM8.30657 22.7287C8.99692 22.7287 9.55657 23.2884 9.55657 23.9787C9.55657 24.6259 9.06469 25.1582 8.43437 25.2223L8.30657 25.2287H5.26904C4.57869 25.2287 4.01904 24.6691 4.01904 23.9787C4.01904 23.3315 4.51092 22.7992 5.14124 22.7352L5.26904 22.7287H8.30657ZM11.5056 9.8043L11.6071 9.89542L13.755 12.0433C14.2432 12.5314 14.2432 13.3229 13.755 13.811C13.2994 14.2667 12.5796 14.297 12.0887 13.9022L11.9872 13.811L9.83937 11.6632C9.35122 11.175 9.35122 10.3836 9.83937 9.89542C10.295 9.43981 11.0148 9.40943 11.5056 9.8043ZM38.1441 9.89542C38.5997 10.351 38.63 11.0709 38.2352 11.5617L38.1441 11.6632L35.9962 13.811C35.508 14.2992 34.7166 14.2992 34.2284 13.811C33.7728 13.3554 33.7425 12.6356 34.1373 12.1448L34.2284 12.0433L36.3763 9.89542C36.8644 9.40727 37.6559 9.40727 38.1441 9.89542ZM24.0004 3.99731C24.6476 3.99731 25.18 4.48919 25.244 5.11951L25.2504 5.24731V8.28484C25.2504 8.97519 24.6908 9.53484 24.0004 9.53484C23.3532 9.53484 22.8209 9.04296 22.7569 8.41264L22.7504 8.28484V5.24731C22.7504 4.55696 23.3101 3.99731 24.0004 3.99731Z',
  moon: 'M9.66862399,33.0089622 C14.6391867,41.6182294 25.647814,44.5679822 34.2570813,39.5974194 C36.6016136,38.243803 38.5753268,36.4126078 40.0785961,34.229664 C40.5811964,33.4998226 40.256086,32.4918794 39.421758,32.193262 C32.6414364,29.766492 29.0099482,26.9542522 26.9026684,22.9317305 C24.6842213,18.6970048 24.110919,14.0582926 25.662851,7.69987534 C25.8774494,6.82064469 25.1829812,5.98348115 24.27924,6.03196802 C21.4771404,6.18230425 18.739608,6.98721743 16.2570813,8.42050489 C7.64781404,13.3910676 4.69806124,24.3996949 9.66862399,33.0089622 Z M24.6881449,24.0918536 C26.9913881,28.4884439 30.80035,31.5226926 37.1145781,33.998575 C35.9639388,35.3646621 34.5800621,36.524195 33.0070813,37.4323559 C25.5935456,41.7125627 16.1138943,39.1724978 11.8336875,31.7589622 C7.55348069,24.3454265 10.0935456,14.8657752 17.5070813,10.5855684 C19.0445047,9.69793654 20.6992772,9.08707059 22.4136896,8.76727896 L22.882692,8.68729053 C21.6894389,14.6550319 22.2911719,19.5163454 24.6881449,24.0918536 Z'
);

@function get-color($color) {
  @if $color != '' {
    @return if(
      string.index($color, '#'),
      '%23#{string.slice($color, 2)}',
      '%23#{$color}'
    );
  }
  @return 'none';
}

@function icon($name,  $fill: '') {
  $fill-color: get-color($fill);
  $icon: map.get($icons, $name);
  @if $icon {
    $svg: &quot;%3Csvg viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg'%3E&quot;;
    $path: &quot;%3Cpath d='#{$icon}' fill='#{$fill-color}' /%3E&quot;;
    @return url('data:image/svg+xml;charset=utf8,#{$svg}#{$path}%3C/svg%3E');
  } @else {
    @error '#{$name}에 해당하는 아이콘이 존재하지 않습니다.';
  }
}

.icon {
  background-image: icon(sun, $fill: 'ff5a40');
}

.icon2 {
  background-image: icon(moon, $fill: '#ffdb34');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 좀 복잡해졌지만 괜찮습니다, 우리가 다 스캔 끝낸 것들이니까요  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 맨 위에 @use로 필요한 모듈들을 가져왔고, $icons에 SVG 값을 담아두었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667445529813&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@function get-color($color) {
  @if $color != '' {
    @return if(
      string.index($color, '#'),
      '%23#{string.slice($color, 2)}',
      '%23#{$color}'
    );
  }
  @return 'none';
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;get-color()&lt;/code&gt;는 SVG에 넣을 컬러값을 리턴하도록 제가 만든 커스텀 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 인자가 없다면 'none'을 리턴하고, 인자가 있다면 #을 떼고 리턴합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해주는 이유는 색상코드에 #이 들어있든 아니든 에러 없이 적용하고 싶기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;code&gt;index()&lt;/code&gt;로 '#'이 있는지 판단해, &lt;code&gt;slice()&lt;/code&gt;로 잘라주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667445611900&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@function icon($name,  $fill: '') {
  $fill-color: get-color($fill);
  $icon: map.get($icons, $name);
  @if $icon {
    $svg: &quot;%3Csvg viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg'%3E&quot;;
    $path: &quot;%3Cpath d='#{$icon}' fill='#{$fill-color}' /%3E&quot;;
    @return url('data:image/svg+xml;charset=utf8,#{$svg}#{$path}%3C/svg%3E');
  } @else {
    @error '#{$name}에 해당하는 아이콘이 존재하지 않습니다.';
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;icon()&lt;/code&gt; 함수는 $icons 변수에서 해당하는 d값을 가져오고, get-color() 함수로 계산해온 색상값을 적용해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한번에 입력해도 되지만 가독성이 떨어져서 여러 개의 변수로 분리했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, 오타 등으로 존재하지 않는 아이콘을 쓰려고 했을 때 @error로 에러를 발생시키도록 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;ExRgYdo&quot; data-user=&quot;nykim_&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/nykim_/pen/ExRgYdo&quot;&gt; Untitled&lt;/a&gt; by nanalike (&lt;a href=&quot;https://codepen.io/nykim_&quot;&gt;@nykim_&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIH71I%2FbtrQj6cJPDb%2FvkCYAMM9ktCGa6WL5r7e4K%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) 기타 팁&lt;/h2&gt;
&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;span&gt;추가로 알아두면 좋은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;@at-rules 몇 가지와 Dart Sass의 주요 변경사항에 대해 짧게 다뤄봅니다.&lt;/p&gt;
&lt;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;@error, @warn&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인수가 들어가는 함수/믹스인에게 올바른 인수가 들어왔는지 확인하고 싶을 때 &lt;code&gt;@error&lt;/code&gt;나 &lt;code&gt;@warn&lt;/code&gt;을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@error는 오류났다는 걸 알리며 컴파일을 중지하지만, @warn은 경고만 표시할 뿐 컴파일을 중지하지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667466490416&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@mixin lang($lang) {
  $langs: 'ja', 'en';

  @if not list.index($langs, $lang) {
    @if $lang == 'jp' or $lang == 'jap' {
      @warn '`#{$lang}`이 아니라 `ja` 입니다  ';
      $lang: 'ja';
    } @else {
      @error '`#{$lang}`는 `#{$langs}`에 포함되어 있지 않습니다   ';
    }
  }

  :lang(#{$lang}) {
    @content;
  }
}


@include lang(jp) { // 경고 표시
  .logo {
    font-size: 18px;
  }
}

@include lang(asdf) { // 오류 표시 후 컴파일 중지
  .logo {
    font-size: 22px;
  }
}&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;@at-root&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 쓰면 표현식을 잠깐 root 레벨로 보내버릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667467766001&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.header {
  .nav {
    .menu {
      .item {
        @at-root {
          .title {
            font-size: 30px;
          }
        }
      }
    }
  }
}

// 컴파일 결과: .title { font-size:30px; }&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;@debug&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@debug&lt;/code&gt;는 변수나 표현식이 맞게 들어갔나... 긴가만가 할 때 씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 달라지는 건 없고 컴파일 시 @debug 결과를 보여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667468004278&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$var1: 150;
$var2: $var1 * 10;
$var3: $var1 + $var2;

@debug calc(100vw - #{$var3}px);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-11-03 at 6.15.30 PM.png&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bt7BdR/btrQkA4JYON/mkkC7mDd5O4Cwukb1FkH6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bt7BdR/btrQkA4JYON/mkkC7mDd5O4Cwukb1FkH6k/img.png&quot; data-alt=&quot;음, 역시 내 계산대로군  &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bt7BdR/btrQkA4JYON/mkkC7mDd5O4Cwukb1FkH6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbt7BdR%2FbtrQkA4JYON%2FmkkC7mDd5O4Cwukb1FkH6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;196&quot; data-filename=&quot;Screen Shot 2022-11-03 at 6.15.30 PM.png&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;290&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;h3 data-ke-size=&quot;size23&quot;&gt;Dart Sass의 변경사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dart Sass는 주요 변경사항이 릴리즈 하기 전 경고를 표시하고 이후 릴리즈를 실시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Dart Sass에서 바뀐 (또는 곧 바뀔) 변경사항 중 흥미로운 2가지를 긁어왔습니다. &lt;span&gt;자세한 내용은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://sass-lang.com/documentation/breaking-changes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;문서&lt;/b&gt;&lt;/a&gt;를 참고하세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;사용자 정의 변수에 Sass 변수 사용&lt;/h4&gt;
&lt;pre id=&quot;code_1667469117641&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;:root {
  --color-accent-800: $color-accent; // not working
  --color-accent-800: #{$color-accent};
  --color-accent-200: #{rgba($color-accent, 0.2)};
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-11-03 at 6.55.21 PM.png&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSwCGq/btrQj5Ee6rq/HvYIkPXWYkkzvnLjWktbeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSwCGq/btrQj5Ee6rq/HvYIkPXWYkkzvnLjWktbeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSwCGq/btrQj5Ee6rq/HvYIkPXWYkkzvnLjWktbeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSwCGq%2FbtrQj5Ee6rq%2FHvYIkPXWYkkzvnLjWktbeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;142&quot; data-filename=&quot;Screen Shot 2022-11-03 at 6.55.21 PM.png&quot; data-origin-width=&quot;882&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nykim.work/2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;사용자 정의 변수&lt;/b&gt;&lt;/a&gt;에 Sass 변수를 쓰려면 보간으로 감싸서 사용해야 제대로 컴파일 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 안 들어가는 거지?! 하면 당황하지 말고 감싸주세요! 감싼 후에는 함수 등으로 값을 조절하는 것도 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;슬래시로 나눗셈 연산&lt;/h4&gt;
&lt;pre id=&quot;code_1667468341280&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$size: 200px;

div {
  width: $size / 5; // warning
}

div {
  width: calc($size / 5); // ok
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-11-03 at 6.43.53 PM.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p0gqx/btrQgL0OA0b/9NRqukBQhIULVpVqSVqfA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p0gqx/btrQgL0OA0b/9NRqukBQhIULVpVqSVqfA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p0gqx/btrQgL0OA0b/9NRqukBQhIULVpVqSVqfA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp0gqx%2FbtrQgL0OA0b%2F9NRqukBQhIULVpVqSVqfA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;201&quot; data-filename=&quot;Screen Shot 2022-11-03 at 6.43.53 PM.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;574&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;Sass에서 &lt;code&gt;/&lt;/code&gt; 는 나눗셈 연산을 수행하기도 하지만 구분자 기호로도 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 혼동을 막기 위해 나눗셈을 작성할 때는 &lt;code&gt;calc()&lt;/code&gt;로 감싸거나, &lt;code&gt;math.div()&lt;/code&gt; 함수를 써서 계산하는 것을 권장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IH71I/btrQj6cJPDb/vkCYAMM9ktCGa6WL5r7e4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIH71I%2FbtrQj6cJPDb%2FvkCYAMM9ktCGa6WL5r7e4K%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에필로그&lt;/h2&gt;
&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;최대한 실무에서 사용할 만한 응용 가능한 예제를 많이 넣으려고 했는데, 익히는 데 도움이 됐다면 기쁩니다 :-D&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 CSS 작성에 있어 Sass만이 방법인 건 아니긴 합니다.&lt;br /&gt;JS랑 분리되어 있다보니 프롭만 받아오면 해결될 것을 굳이 클래스로 나눠야 하는 불편함,&lt;br /&gt;그냥 속성 하나만 주고 싶은데 굳이 마크업으로 나누고 셀렉팅해줘야 하는 불편함도 물론 있죠...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 믹스인, 함수 등 다양한 기능으로 활용도가 높고 CSS와 유사해 쉽게 쓸 수 있다는 점에서 참 좋아합니다 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;Sass와 함께 더 편한 CSS 라잎을 즐기자구요&amp;nbsp; ＼\&amp;nbsp;٩(&amp;nbsp;ᐛ&amp;nbsp;)و&amp;nbsp;/／&lt;/p&gt;</description>
      <category>Blog/CSS</category>
      <author>나나 (nykim)</author>
      <guid isPermaLink="true">https://anneslab.tistory.com/115</guid>
      <comments>https://anneslab.tistory.com/115#entry115comment</comments>
      <pubDate>Sat, 5 Nov 2022 19:44:21 +0900</pubDate>
    </item>
    <item>
      <title>Framer-motion 라이브러리 훑어보기</title>
      <link>https://anneslab.tistory.com/114</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blog-react.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb1tpl/btrLKiHQ4dW/XQxIenzrhkt5wDYo0jh7W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb1tpl/btrLKiHQ4dW/XQxIenzrhkt5wDYo0jh7W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb1tpl/btrLKiHQ4dW/XQxIenzrhkt5wDYo0jh7W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb1tpl%2FbtrLKiHQ4dW%2FXQxIenzrhkt5wDYo0jh7W0%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;1600&quot; height=&quot;900&quot; data-filename=&quot;blog-react.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프롤로그&lt;/h2&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;Sep-08-2022 17-16-03.gif&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;942&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RkVLu/btrLLg27bec/DDqM3tGFq4ftY0cbzLvlE0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RkVLu/btrLLg27bec/DDqM3tGFq4ftY0cbzLvlE0/img.gif&quot; data-alt=&quot;motion을 활용한 예시 (SVG Drawing, Slide, Gesture, Modal)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RkVLu/btrLLg27bec/DDqM3tGFq4ftY0cbzLvlE0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/RkVLu/btrLLg27bec/DDqM3tGFq4ftY0cbzLvlE0/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;1412&quot; height=&quot;942&quot; data-filename=&quot;Sep-08-2022 17-16-03.gif&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;942&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;motion을 활용한 예시 (SVG Drawing, Slide, Gesture, Modal)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Framer-motion은 &lt;a href=&quot;https://www.framer.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Framer&lt;/a&gt;가 제공하는 리액트용 애니메이션 라이브러리입니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NomadCoders의 &lt;a href=&quot;https://nomadcoders.co/react-masterclass/lobby&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&amp;lt;ReactJS 마스터클래스&amp;gt;&lt;/a&gt;를 듣던 중, framer-motion 라이브러리를 사용하는 내용이 있어 공식 문서를 슥 훑어보았는데요,&amp;nbsp;&lt;br /&gt;아니... 이것은... 애니메이션 총집합 선물세트?!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 다양한 애니메이션 모션을 제공하는 멋쟁이 라이브러리였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서 보러 들어갈 때마다 구글 번역을 쓰기 귀찮아서 일부 내용만 발췌해 쫌쫌따리 가져왔습니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이 글은 framer-motion v.7 을 기준으로 작성되었습니다. (2022.09)&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Introduction&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.framer.com/docs/introduction/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://github.com/framer/motion&quot;&gt;Github&lt;/a&gt;&amp;nbsp;(Star 15.4k)&lt;br /&gt;MIT 라이센스&lt;br /&gt;요구사항: React 17+&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서는 요기로  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.framer.com/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.framer.com/docs/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1662625992049&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;product&quot; data-og-title=&quot;Documentation | Framer for Developers&quot; data-og-description=&quot;An open source, production-ready motion library for React on the web.&quot; data-og-host=&quot;www.framer.com&quot; data-og-source-url=&quot;https://www.framer.com/docs/&quot; data-og-url=&quot;https://www.framer.com/docs/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.framer.com/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.framer.com/docs/&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;Documentation | Framer for Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;An open source, production-ready motion library for React on the web.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.framer.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단은 아묻따 설치합니다!&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;$ npm i framer-motion&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;hellip;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { motion } from &quot;framer-motion&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;motion.div 와 같이, HTML 태그 앞에 motion 키워드를 붙여줍니다. 이렇게 motion 키워드가 붙은 요소를 &lt;a href=&quot;https://www.framer.com/docs/component/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;motion component&lt;/a&gt; 라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 상태를 initial 속성에 객체 형태로 넣고, 애니메이션 할 상태를 animate 속성에 객체 형태로 넣습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  initial={{ scale: 0 }} 
  animate={{ scale: 1, rotateZ: 360 }}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/naughty-driscoll-7q587x?autoresize=1&amp;amp;fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 적절한 속성을 달아주기만 하면 끝!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;animate 값이 변경되면 Motion이 자동으로 애니메이션을 적용해 줍니다  &lt;/p&gt;
&lt;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Animation &lt;a href=&quot;https://www.framer.com/docs/animation/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h2&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;b&gt;Simple animations &lt;a href=&quot;https://www.framer.com/docs/animation/#simple-animations&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Transitions&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 Motion 라이브러리는 &amp;lsquo;알잘딱깔센&amp;rsquo;하게 애니메이션을 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 x/y값 변경이나 scale이 바뀔 때는 튀는 듯한 스프링 효과를 적용하고, opacity나 color가 바뀔 때는 자연스러운 트윈 효과를 적용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 transition 속성으로 원하는 유형의 애니메이션을 정의할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  initial={{ opacity: 0, scale: 0.5 }}
  animate={{ opacity: 1, scale: 1 }}
  transition={{
    duration: 0.8,
    delay: 0.5,
    ease: [0, 0.71, 0.2, 1.01]
  }}
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/framer-motion-transition-prop-j9m7d2?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Enter animations&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;motion 컴포넌트가 처음 생성될 때, animate 속성에 적용된 값이 style 또는 inital 에 정의된 값과 다르다면 animate 속성에 적용된 값으로 자동으로 애니메이션을 적용해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동으로 적용하길 원치 않는다면 inital 값을 false로 설정해 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div animate={{ x: 100 }} initial={false} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Exit animations&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서는 컴포넌트가 트리에서 삭제될 경우 &amp;ldquo;즉시&amp;rdquo; 사라져버리기 때문에 사라지는 애니메이션을 적용하기 어렵다는 문제가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Motion을 쓴다면?!&amp;nbsp;&lt;a href=&quot;https://www.framer.com/docs/animate-presence/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;AnimatePresence&lt;/b&gt;&lt;/a&gt; 컴포넌트를 사용하면 사라지는 애니메이션이 보여지는 동안 DOM에 유지되도록 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;AnimatePresence&amp;gt;
  {isVisible &amp;amp;&amp;amp; (
    &amp;lt;motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    /&amp;gt;
  )}
&amp;lt;/AnimatePresence&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-exit-animations-rpo379?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Keyframes&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;animate의 값을 배열로 설정하면 Motion이 각 값을 차례로 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재값을 초기 키프레임으로 사용하고 싶다면 null 값을 주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 애니메이션 되는 도중에 애니메이션이 시작되더라도 전환이 자연스러워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  animate={{
    scale: [1, 2, 2, 1, 1],
    rotate: [0, 0, 270, 270, 0],
    borderRadius: [&quot;20%&quot;, &quot;20%&quot;, &quot;50%&quot;, &quot;50%&quot;, &quot;20%&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&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/framer-motion-keyframes-0fzv21?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;p data-ke-size=&quot;size16&quot;&gt;일단 배열로 값을 넘기면 라이브러리가 알아서 값들을 균등하게 배치해주지만, &lt;br /&gt;정확한 타이밍을 지정하고 싶다면 transition 속성의 times를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  animate={{ scale: [1, 1.5, 1.1] }}
  transition={{ duration: 3, times: [0, 0.2, 1] }}
/&amp;gt;

/*
 * 3초에 걸쳐 scale 애니메이션을 실행합니다.
 * [1 ⇢ 1.5]는 [0 ⇢ 0.2]에 걸쳐 실행되고,
 * [1.5 ⇢ 1.1]은 [0.2 ⇢ 1]에 걸쳐 실행됩니다.
 */
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/dazzling-carson-mhc8o3?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Gesture animations &lt;a href=&quot;https://www.framer.com/docs/animation/#gesture-animations&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hover, tap, drag, focus, inView 등의 &lt;a href=&quot;https://www.framer.com/docs/gestures/#animation-helpers&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;제스처가 시작될 때&lt;/b&gt;&lt;/a&gt; 값 집합에 애니메이션을 적용할 수도 있어요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
	initial={{ opacity: 0.2 }}
	whileInView={{
	  opacity: 1,
	  rotate: [0, 360],
	  borderRadius: [&quot;20%&quot;, &quot;50%&quot;],
	  transition: { delay: 0.05 }
	}}
	whileHover={{
	  scale: 1.2,
	  transition: { type: &quot;spring&quot;, stiffness: 400, damping: 10 }
	}}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/relaxed-framework-z8dkd4?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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;Variants &lt;a href=&quot;https://www.framer.com/docs/animation/#variants&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 개체에 애니메이션을 설정하는 것은 쉽습니다. &lt;br /&gt;그런데 DOM 전체에 파생되는 애니메이션이나, 차례로 이뤄지는 애니메이션을 설정하고 싶을 때는 어떻게 할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럴 때는 variants(변형)를 써봅시다! 이건 미리 정의한 애니메이션 세트라고 할 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const variants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1 }
}
&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;motion 컴포넌트에 variants 속성으로 사전 정의한 내용을 넘겨줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div variants={variants} /&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;초기 상태 initial, 적용할 애니메이션 animate 속성을 variants 객체에 있는 속성 이름으로 지정하면 끝!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.h1
  initial=&quot;hidden&quot;
  animate=&quot;visible&quot;
  variants={variants}
/&amp;gt;

&amp;lt;motion.button
  initial=&quot;hidden&quot;
  animate=&quot;visible&quot;
  variants={variants}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Propagation&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 motion 컴포넌트에 자식 요소가 있다면, 자식 요소가 자체 animate 속성을 정의하기 전까지 variants의 변화를 상속받도록 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해, variants에 정의한 속성명을 자식에게 그대로 물려줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const list = {
  hidden: { opacity: 0 },
  visible: { opacity: 1 },
}

const item = {
  hidden: { opacity: 0, y: -100 },
  visible: { opacity: 1, y: 0 },
}

return (
  &amp;lt;motion.ul
    initial=&quot;hidden&quot;
    animate=&quot;visible&quot;
    variants={list}
  &amp;gt;
    &amp;lt;motion.li variants={item} /&amp;gt;
    &amp;lt;motion.li variants={item} /&amp;gt;
    &amp;lt;motion.li variants={item} /&amp;gt;
  &amp;lt;/motion.ul&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-propgation-8p0dzx?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;p data-ke-size=&quot;size16&quot;&gt;이 경우 li에 달린 variants={item}은 initial=&quot;hidden&quot;과 animate=&quot;visible&quot;가 자동으로 적용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;h4 data-ke-size=&quot;size20&quot;&gt;Orchestration&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제에서 볼 수 있는 것처럼, item에 달린 애니메이션은 모두 동시에 시작됩니다. &lt;br /&gt;하지만 transition에 추가적인 속성을 더해 자식 애니메이션의 실행을 조정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const list = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      when: &quot;beforeChildren&quot;,
      staggerChildren: 0.2
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-orchestration-wgje4h?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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;Dynamic variants&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 정의해서 각 variant에 동적으로 애니메이션을 설정할 수도 있습니다. &lt;br /&gt;이러한 variant 함수들은 컴포넌트의 custom 속성으로 넘어오는 값을 인자로 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const variants = {
  hidden: {
    opacity: 0.2,
    y: 15
  },
  visible: i =&amp;gt; ({
    opacity: 1,
    y: 0,
    transition: {
      delay: i * 0.2,
      duration: 1,
      repeat: Infinity,
      repeatType: &quot;reverse&quot;
    }
  })
};

return (
  &amp;lt;ul&amp;gt;
    {items.map((item, i) =&amp;gt; (
      &amp;lt;motion.li
        key={item}
        initial=&quot;hidden&quot;
        animate=&quot;visible&quot;
        variants={variants}
        custom={i}
      &amp;gt;
        {item}
      &amp;lt;/motion.li&amp;gt;
    ))}
  &amp;lt;/ul&amp;gt;
  );
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-dynamic-variants-0hv3q6?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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;b&gt;Manual Controls&lt;/b&gt; &lt;a href=&quot;https://www.framer.com/docs/animation/#manual-controls&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;b&gt;ㅤ&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 UI 인터랙션에 맞춰 착착 애니메이션이 실행되지만,&lt;br /&gt;좀더 복잡한 시퀀스를 구현하고 싶다면 &lt;b&gt;&lt;a href=&quot;https://www.framer.com/docs/use-animation-controls/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;useAnimationControls&lt;/a&gt;&lt;/b&gt; 훅으로 애니메이션을 수동 시작/중지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;import { useEffect, useState } from &quot;react&quot;;
import { motion, useAnimationControls } from &quot;framer-motion&quot;;

export default function App() {
  const [show, setShow] = useState(false);
  const controls = useAnimationControls();

  useEffect(() =&amp;gt; {
    if (show) {
      controls.start({ scale: 6 });
    }
  }, [controls, show]);

  return (
    &amp;lt;div className=&quot;wrap&quot;&amp;gt;
      &amp;lt;motion.h1 animate={controls}&amp;gt;{show ? &quot;Wow!&quot; : &quot;...&quot;}&amp;lt;/motion.h1&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setShow(true)}&amp;gt;setShow(true)&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-manual-control-46yxcd?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;3. &lt;b&gt;Motion components&lt;/b&gt; &lt;a href=&quot;https://www.framer.com/docs/component/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;motion 컴포넌트는 60fps 애니메이션에 최적화된 DOM 기본 요소입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자, 숫자를 포함한 문자열, 16진수 또는 RGB 색상값 등 원하는 값만 넣으세요! Motion이 알아서 다 해줍니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Supported values &lt;a href=&quot;https://www.notion.so/8-5f61f43d2fb14322911989883a040e55&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 같은 유형끼리(px to px) 애니메이션이 가능하지만,&lt;br /&gt;HTML의 x, y, width, height, top, left, &amp;nbsp;right, &amp;nbsp;bottom 은 다른 값 유형이더라도 자유롭게 애니메이션될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  initial={{ x: &quot;100%&quot; }}
  animate={{ x: &quot;calc(100vw - 50%)&quot; }}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Transform&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS Transform 속성은 GPU에 의해 가속화되므로 애니메이션 하기 딱 좋은 속성이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 값이 있을 때 translate &amp;rArr; scale &amp;rArr; rotate &amp;rArr; skew 순대로 적용되지만, 원한다면 transformTemplate 속성을 써서 사용자 정의도 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const transform = ({ rotate, x }) =&amp;gt; `rotate(${rotate}) translateX(${x})`

&amp;lt;motion.div transformTemplate={transform} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CSS variables&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;똑똑한 Motion은 CSS 변수값도 애니메이션 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(타입스크립트를 쓰고 있다면 as any 로 타입 처리를 해주세요!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.ul
  initial={{ '--rotate': '0deg' } as any}
  animate={{ '--rotate': '360deg' } as any}
&amp;gt;
  &amp;lt;li style={{ transform: 'rotate(var(--rotate))' }} /&amp;gt;
&amp;lt;/motion.ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;SVG line drawing&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pathLength,&amp;nbsp;pathSpacing,&amp;nbsp;pathOffset 속성을 사용하여 SVG 패스 애니메이션을 만들 수 있습니다. 0~1 사이의 값으로 설정되며, 여기서 1은 path의 측정된 길이입니다. (그러니 0에서 1로 만들면 되겠죠!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패스 애니메이션은 circle,&amp;nbsp;ellipse,&amp;nbsp;line,&amp;nbsp;path,&amp;nbsp;polygon,&amp;nbsp;polyline,&amp;nbsp;rect&amp;nbsp;와 호환됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;const variants = {
  start: { pathLength: 0, fill: &quot;rgba(255, 255, 255,0)&quot; },
  end: { pathLength: 1, fill: &quot;rgba(255, 255, 255, 1)&quot; }
};

&amp;lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 400 500&quot;&amp;gt;
  &amp;lt;motion.path
    variants={variants}
    initial=&quot;start&quot;
    animate=&quot;end&quot;
    transition={{
      default: { duration: 1.8 },
      fill: { duration: 1, delay: 1.1 }
    }}
    d=&quot;M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z&quot;
  /&amp;gt;
&amp;lt;/svg&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-svg-drawing-1z1uhf?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;4. Layout animations &lt;a href=&quot;https://www.framer.com/docs/layout-animations/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Framer Motion의 강력한 강점 중 하나는 손쉽게 레이아웃 애니메이션을 설정할 수 있다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS 레이아웃은 사실 힘들고 어려운 일입니다. 예를 들어 height를 100px에서 500px로 만들고 트랜지션을 적용하면 리플로우(Reflow)가 발생하면서 성능이 저하됩니다. 게다가 트랜지션으로는 한계가 있습니다. justify-content 상태일 때 flex-start와 flex-end를 트랜지션할 수 있을까요? 음, 힘들 것 같은데요&amp;hellip;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만?! motion을 쓴다면 가능합니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div layout /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-layout-uo78vs?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 layout 속성만 달아주세요! 그러면 리렌더링 결과로 발생하는 모든 레이아웃 변경사항을 감지해 애니메이션을 실행합니다. 다음과 같은 조합이 있을 수 있겠죠.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목록 순서 변경&lt;/li&gt;
&lt;li&gt;컴포넌트 자체에 설정된 스타일 변경 (width, position 등)&lt;/li&gt;
&lt;li&gt;부모 레이아웃의 변경 (flexbox, grid 등)&lt;/li&gt;
&lt;li&gt;그밖에 다른 모든 레이아웃 변경사항&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;Scale correction &lt;a href=&quot;https://www.framer.com/docs/layout-animations/##scale-correction&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 레이아웃 애니메이션은 transform 속성을 사용하므로 부드럽게 구현됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;transform을 사용하는 레이아웃 애니메이션은 때때로 자식 요소가 시각적으로 왜곡되곤 하는데요, 이를 수정하기 위해 요소의 첫 번째 자식 요소에도 layout 속성을 지정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;box-shadow나 border-radius 속성이 왜곡되는 경우도 있지만 돈 워리! 값이 설정되어 있는 한 motion 컴포넌트는 이를 자동으로 수정합니다. 만약 이런 값들을 애니메이션화 하지 않을 거라면 그냥 style로 지정하는 것이 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div layout style={{ borderRadius: 20 }} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Customising layout animations&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레이아웃 애니메이션도 transition 속성으로 커스터마이징 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div layout transition={{ duration: 0.3 }} /&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;특별히 레이아웃 애니메이션에만 트랜지션을 지정하고 싶다면 layout 속성을 콕 집어 설정해 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  layout
  animate={{ opacity: 0.5 }}
  transition={{
    opacity: { ease: &quot;linear&quot; },
    layout: { duration: 0.3 }
  }}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Animating within scroll containers&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤 가능한 요소 내에서 레이아웃 애니메이션을 실행하고 싶다면 layoutScroll 속성을 꼭 넣어주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 라이브러리가 자식을 측정할 때 요소의 스크롤 옵셋을 고려할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div layoutScroll style={{ overflow: &quot;scroll&quot; }}/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Coordinating layout animations&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 리렌더링되면서 레이아웃이 변경되면 애니메이션이 자동으로 트리거됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 만약! 동시에 리렌더링 되는 일은 없지만 서로의 레이아웃에 영향을 주는 컴포넌트가 함께 있다면 어떨까요? 예를 들면 FAQ 등에서 자주 쓰는 아코디언 메뉴가 있겠죠.&lt;/p&gt;
&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;a href=&quot;https://www.framer.com/docs/layout-group/&quot;&gt;LayoutGroup&lt;/a&gt;&lt;/b&gt; 컴포넌트를 사용해 함께 묶어 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;import { LayoutGroup } from &quot;framer-motion&quot;

function List() {
  return (
    &amp;lt;LayoutGroup&amp;gt;
      &amp;lt;Accordion /&amp;gt;
      &amp;lt;Accordion /&amp;gt;
    &amp;lt;/LayoutGroup&amp;gt;  
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 그룹화된 컴포넌트 중 하나에서 레이아웃 변경이 감지되면 부가적인 리렌더링 없이 다른 모든 컴포넌트들에게도 레이아웃 애니메이션이 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;Shared layout animations &lt;a href=&quot;https://www.framer.com/docs/layout-animations/#shared-layout-animations&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;layoutId 속성을 가진 기존 컴포넌트와 일치하는 새로운 컴포넌트가 추가될 경우, 이전 컴포넌트에서 자동으로 애니메이션이 적용됩니다.&lt;br /&gt;이를 통해 이전 컴포넌트에서 새 컴포넌트를 보여줄 때 자연스러운 처리가 가능합니다. (어메이징하죠!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
	{tabs.map((item) =&amp;gt; (
	  &amp;lt;li
	    key={item.label}
	    className={item === selectedTab ? &quot;selected&quot; : &quot;&quot;}
	    onClick={() =&amp;gt; setSelectedTab(item)}
	  &amp;gt;
	    {`${item.icon} ${item.label}`}
	    {item === selectedTab ? (
	      &amp;lt;motion.div className=&quot;underline&quot; layoutId=&quot;underline&quot; /&amp;gt;
	    ) : null}
	  &amp;lt;/li&amp;gt;
	))}
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/framer-motion-layout-animations-snxgv?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;5. &lt;b&gt;Gestures &lt;a href=&quot;https://www.framer.com/docs/gestures/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Motion은 React에서 제공하는 기본 이벤트 리스너 세트를 확장해 제스처 애니메이션을 제공합니다. &lt;br /&gt;motion 컴포넌트에 hover, tap, pan, viewport, drag 등의 제스처 이벤트 리스너를 붙일 수 있습니다.&lt;/p&gt;
&lt;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;Animation helpers &lt;a href=&quot;https://www.framer.com/docs/gestures/#animation-helpers&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;motion 컴포넌트는 다양한 애니메이션 제스쳐 속성을 제공합니다:&lt;br /&gt;whileHover, whileTap, whileFocus, whileDrag, whileInView&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.button
  whileHover={{
    scale: 1.2,
    transition: { duration: 1 },
  }}
  whileTap={{ scale: 0.9 }}
/&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;애니메이션할 값을 직접 입력해도 되고, variant 속성을 통해 정의된 이름으로 설정할 수도 있습니다. &lt;br /&gt;이 경우, variants에 정의된 속성명을 자식 요소에서 그대로 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const buttonVariants = {
  hover: { scale: 2, rotzteZ: 90 }
};

&amp;lt;motion.button
  whileHover=&quot;hover&quot;
  variants={buttonVariants}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Hover &lt;a href=&quot;https://www.framer.com/docs/gestures/#hover&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호버 제스처는 포인터가 컴포넌트 위로 이동하거나 컴포넌트를 떠날 때를 감지합니다. onMouseEnter나 onMouseLeave 와는 다르게 실제 마우스 이벤트 결과가 있을 때만 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.a
	whileHover={{ scale: 1.1 }}
  onHoverStart={e =&amp;gt; {}} // 컴포넌트 위로 포인터를 가져갈 때 실행되는 콜백 함수
  onHoverEnd={e =&amp;gt; {}}   // 컴포넌트에서 포인터가 떠날 때 실행되는 콜백 함수
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/framer-motion-hover-animations-h44jpy?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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;Tap &lt;a href=&quot;https://www.framer.com/docs/gestures/#tap&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탭 제스처는 포인터가 동일한 컴포넌트를 눌렀다 뗄 때를 감지합니다. &lt;br /&gt;어떤 구성요소에서 탭핑이 성공적으로 완료되면 tap 이벤트를 실행하고, 컴포넌트 외부에서 탭핑이 종료되면 tapCancel 이벤트를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 컴포넌트가 드래그 가능한 컴포넌트의 자식인 경우 제스처 중 포인터가 3픽셀 이상 이동하면 탭 제스처가 자동으로 취소됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  whileTap={{ scale: 0.9 }}
	onTap={e =&amp;gt; {}}       // 탭 제스처가 성공적으로 종료될 때 실행되는 콜백 함수
	onTapStart={e =&amp;gt; {}}  // 탭 제스처가 시작될 때 실행되는 콜백 함수
	onTapCancel={e =&amp;gt; {}} // 탭 제스처가 컴포넌트 외부에서 끝날 때 실행되는 콜백 함수
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/framer-motion-press-tap-animations-1igkgz?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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;Drag &lt;a href=&quot;https://www.framer.com/docs/gestures/#drag&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포인터가 구성 요소를 누르고 3픽셀 이상 이동할 때를 인식합니다.&amp;nbsp;포인터를 놓으면 제스처가 종료됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;drag=&quot;x&quot; 를 통해 x 방향(또는 y 방향)으로만 드래그 되도록 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div 
	drag
	dragConstraints={{top:100}} // 드래그 허용 영역 (useRef hook으로 생성된 컴포넌트도 지정 가능)
	dragSnapToOrigin            // 드래그한 요소를 놓으면 원점으로 되돌아감
	dragElastic                 // constraints 밖으로 허용되는 움직임의 정도 (0=없음, 1=전체)
	dragTransition={}           // 드래그 요소의 관성(inertia) 애니메이션을 지정
 /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-gestures-drag-s8z6d4?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;6. Scroll Animations &lt;a href=&quot;https://www.framer.com/docs/scroll-animations/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Motion에는 2가지의 스크롤 애니메이션 유형이 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스크롤 링크(Scroll-linked): 스크롤 진행 상황이 애니메이션 진행 상황과 연결됨&lt;/li&gt;
&lt;li&gt;스크롤 트리거(Scroll-triggered): 요소가 뷰포트에 들어오거나 나갈 때 애니메이션이 트리거됨&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;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;Scroll-linked animations &lt;a href=&quot;https://www.framer.com/docs/scroll-animations/#scroll-linked-animations&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모션 값(Motion Values)과 &lt;b&gt;&lt;a href=&quot;https://www.framer.com/docs/use-scroll/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;useScroll&lt;/a&gt;&lt;/b&gt; 훅을 사용해 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;useScroll&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 페이지 스크롤을 추적하며, 4개의 모션 값을 반환합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;scrollX, scrollY : 절대 위치값&lt;/li&gt;
&lt;li&gt;scrollXProgress, scrollYProgresss: 정의된 오프셋 사이의 스크롤 값 (0~1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;import { motion, useScroll } from &quot;framer-motion&quot;

function Component() {
  const { scrollYProgress } = useScroll();
  
  return (
    &amp;lt;motion.div style={{ scaleX: scrollYProgress }} /&amp;gt;  
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-usescroll-ofgvw5?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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;Scroll-triggered animations &lt;a href=&quot;https://www.framer.com/docs/scroll-animations/#scroll-triggered-animations&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;whileInView&amp;nbsp;속성 세트(&amp;amp; 트랜지션)를 정의하여 요소가 뷰에 있을 때 애니메이션을 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  initial={{ opacity: 0 }}
  whileInView={{ opacity: 1 }}
  viewport={} // 뷰포트가 감지되는 방식을 정의하는 뷰포트 옵션 개체
  onViewportEnter={()=&amp;gt;{}}  // 뷰포트에 진입할 때 호출 (단, 브라우저가 IntersectionObserver를 지원해야 함)
  onViewportLeave={()=&amp;gt;{}}  // 뷰포트에서 떠날 때 호출 (단, 브라우저가 IntersectionObserver를 지원해야 함)
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Viewport options&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;once&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(boolean)&lt;/span&gt; &lt;br /&gt;true인 경우, 뷰포트에 들어가면 whileInView 상태가 유지됩니다. 즉, 뷰포트 콜백이 호출되지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;root&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(RefObject&amp;lt;Element&amp;gt;)&lt;/span&gt; &lt;br /&gt;viewport.root와 조상 요소에 ref 객체를 넘기면, 해당 요소를 뷰포트 측정에 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;margin&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(string)&lt;/span&gt; &lt;br /&gt;뷰포트 진입을 판단할 때 마진을 추가할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;amount&lt;/b&gt;&lt;span style=&quot;color: #666666;&quot;&gt; (&quot;some&quot; | &quot;all&quot; | number)&lt;/span&gt; &lt;br /&gt;요소가 쬐끔(some) 보일 때 뷰포트와 교차한다고 판단할지, 전부(all) 또는 일정한 만큼 보일 때 판단할지 정의합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;function Component() {
  const scrollRef = useRef(null)
  
  return (
    &amp;lt;div ref={scrollRef}&amp;gt;
      &amp;lt;motion.div
        variants={emojiVariants}
        initial=&quot;hidden&quot;
        whileInView=&quot;visible&quot;
        viewport={{ root: scrollRef, once: true, amount: 0.3 }}
      /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-scroll-2-xgni3e?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;7. &lt;b&gt;Transition &lt;a href=&quot;https://www.framer.com/docs/transition/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS에서도 자주 써먹는 그 속성 맞습니다! 두 값 사이에 애니메이션을 적용할 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  animate={}
  transition={{ duration: 1 }}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 정의한 트랜지션은 모든 속성에 적용되지만, 값에 각각 다른 트랜지션을 적용하는 것도 가능해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  transition={{
		default: { duration: 1 },
    opacity: {
      delay: 0.3
    },
    y: {
      type: &quot;spring&quot;,
      damping: 3,
      stiffness: 50,
      restDelta: 0.01,
      duration: 0.3
    }
  }}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Orchestration &lt;a href=&quot;https://www.framer.com/docs/transition/#orchestration&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 속성을 통해 트랜지션을 보다 심미적으로 만들 수 있습니다.&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;delay&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(number)&lt;/span&gt; &lt;br /&gt;말그대로 n초 간의 딜레이를 적용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;delayChildren&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(number)&lt;/span&gt; &lt;br /&gt;variants를 사용하는 경우, n초 후 자식 컴포넌트의 애니메이션이 시작됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;staggerChildren&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(number)&lt;/span&gt; &lt;br /&gt;variants를 사용할 때, 자식 컴포넌트 간 애니메이션 간격을 설정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;staggerDirection&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(number)&lt;/span&gt; &lt;br /&gt;스태거를 적용할 방향입니다. -1을 주면 역순이 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;when&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(false | &quot;beforeChildren&quot; | &quot;afterChildren&quot; | string)&lt;/span&gt; &lt;br /&gt;varaints를 사용할 때, 하위 트랜지션이 시작하기 전 또는 완료한 후에 이 트랜지션을 완료할지 정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;repeat&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(number)&lt;/span&gt; &lt;br /&gt;트랜지션을 반복할 횟수입니다. Infinity로 설정하면 영원히 계속됩니다&amp;hellip; ☆ 값을 설정하지 않으면 repeatType애니메이션이 반복됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;repeatType&lt;/b&gt;&lt;span style=&quot;color: #666666;&quot;&gt; (&quot;loop&quot; | &quot;reverse&quot; | &quot;mirror&quot;)&lt;/span&gt; &lt;br /&gt;트랜지션이 반복되는 경우 어떻게 처리할지 정의합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Spring &lt;a href=&quot;https://www.framer.com/docs/transition/#spring&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Motion의 좋은 점은 보다 사실적인 애니메이션을 표현해 준다는 것입니다. &lt;br /&gt;transition 타입으로 spring을 설정하면 용수철이 튀어오르는 것처럼 사실적인 물리 표현을 할 수 있습니다.&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;bounce&lt;/b&gt;&lt;span style=&quot;color: #666666;&quot;&gt; (number)&lt;/span&gt; &lt;br /&gt;말그대로 얼마나 바운스바운스할지 정합니다(두근..!) 0이라면 탄력이 없고, 1은 매우 탄력적입니다. duration 기본값은 0.25로 설정됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;damping&lt;/b&gt;&lt;span style=&quot;color: #666666;&quot;&gt; (number)&lt;/span&gt; &lt;br /&gt;저항하는 반대 힘. 0으로 설정하면 스프링이 무한으로 빠운스합니다. (기본값: 10)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;mass&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(number)&lt;/span&gt; &lt;br /&gt;질량. 값이 높을수록 움직임이 둔해집니다. (기본값: 1)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;stiffness&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(number)&lt;/span&gt; &lt;br /&gt;값이 높을수록 더 갑작스럽게 움직입니다. (기본값: 100)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;velocity&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(number)&lt;/span&gt; &lt;br /&gt;스프링의 초기 속도.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;restSpeed&lt;/b&gt; &lt;span style=&quot;color: #666666;&quot;&gt;(number)&lt;/span&gt; &lt;br /&gt;절대 속도가 이 값 아래로 떨어지고 델타가 restDelta보다 작으면 애니메이션을 종료합니다. (기본값: 0.01)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;restDelta&lt;/b&gt;&lt;span style=&quot;color: #666666;&quot;&gt; (number)&lt;/span&gt; &lt;br /&gt;거리(distance)가 이 값보다 작고 속도가 restSpeed 보다 작으면 애니메이션을 종료합니다. (기본값: 0.01)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;motion.div
  animate={{ rotate: 180 }}
  transition={{ type: 'spring' }}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-spring-o1hpll?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;8. &lt;b&gt;Motion values &lt;a href=&quot;https://www.framer.com/docs/motionvalue/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 motion 컴포넌트는 내부적으로 MotionValue를 사용해 애니메이션 값의 상태와 속도를 추적합니다. &lt;br /&gt;이는 자동으로 생성되지만 사용자가 원한다면 수동으로 생성해 motion 컴포넌트에 넣을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수동으로 MotionValue를 설정하는 경우,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태를 Set/Get 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;여러 컴포넌트 간 모션을 동기화할 수 있습니다.&lt;/li&gt;
&lt;li&gt;useTransform 훅을 통해 MotionValue를 연결할 수 있습니다.&lt;/li&gt;
&lt;li&gt;React의 렌더링 주기를 트리거하지 않고도 시각적 속성을 업데이트할 수 있습니다.&lt;/li&gt;
&lt;li&gt;업데이트를 구독(Subscribe)할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Overview &lt;a href=&quot;https://www.framer.com/docs/motionvalue/#overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MotionValue는 useMotionValue 훅으로 생성할 수 있습니다. 문자열 또는 숫자로 된 초기값을 지정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 set() 메서드로 업데이트할 수 있고, get() 으로 값을 읽어올 수 있습니다. getVelocity() 로 초당 계산된 속도를 받아올 수도 있고요. 세부 메서드는 &lt;a href=&quot;https://www.framer.com/docs/motionvalue/#motionvalue&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;여기&lt;/b&gt;&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재밌는 점은 MotionValue가 바뀌더라도 컴포넌트는 리렌더링되지 않는다는 것입니다. &lt;br /&gt;&amp;lt;motion.div/&amp;gt;가 움직인다고 한들 이 컴포넌트는 다시 랜더링되지 않기 때문에 여기 붙어있는 api가 계속해서 호출된다거나 하는 불상사는 없겠죠!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;import { motion, useMotionValue } from &quot;framer-motion&quot;

function App() {
  const x = useMotionValue(0)

	x.set(100);
	x.get();
	x.getVelocity();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Injecting&amp;nbsp;MotionValues&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 MotionValue가 생성되면, 시각적 속성을 줬던 것처럼 motion component에 연결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML이라면 style 속성을 통해, SVG라면 SVG 속성을 통해 넣어주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const x = useMotionValue(0);
const cx = useMotionValue(0);

&amp;lt;motion.div style={{ x }} /&amp;gt; // HTML
&amp;lt;motion.circle cx={cx} /&amp;gt;    // SVG
&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;하나의 MotionValue를 여러 개의 컴포넌트에 넣는 것도 가능합니다. 그럼 그 MotionValue를 바라보는 모든 컴포넌트에 영향이 가겠죠!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Responding to changes&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onChange 메서드로 MotionValue에 리스너를 추가할 수도 있습니다. &lt;br /&gt;리액트 컴포넌트 안에서 onChange를 호출할 때는 useEffect 훅으로 꼭 감싸 주세요. onChange는 unsubscribe 함수를 반환하므로 subscriber 중복을 막기 위해 useEfeect() 안에서 쓰여야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; x.onChange(latest =&amp;gt; {}), [])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Composing MotionValues&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useTransform 등의 훅을 써서 MotionValue값을 구성할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useTransform : 값 범위를 다른 값 범위로 매핑할 수 있습니다.&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;박스를 왼쪽으로 드래그하면 scale(3)로, 오른쪽으로 드래그하면 scale(0.1)로 바뀌도록 하는 예제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const x = useMotionValue(0);
const scale = useTransform(
  x,
  // Map x from these values:
  [-400, 400],
  // Into these values:
  [3, 0.1]
);

&amp;lt;motion.div drag=&quot;x&quot; style={{ x, scale }}/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-usetransform-5tuyxh?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-usetransform-2-h1k6nl?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;9. Examples&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/motion-slide-2k9gkx?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/elastic-hofstadter-u39hd0?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; width=&quot;100%&quot; height=&quot;500px&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;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://www.framer.com/docs/use-scroll/&quot;&gt;useScroll | Framer for Developers&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.framer.com/docs/animate-shared-layout/&quot;&gt;AnimateSharedLayout | Framer for Developers&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.framer.com/docs/reorder/&quot;&gt;Reorder | Framer for Developers&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.framer.com/docs/three-introduction/&quot;&gt;3D: Introduction | Framer for Developers&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;훑어봤지만 아직 볼 게 더 많이 남은 시리즈물 같은 느낌...!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 애니메이션을 하나의 라이브러리로 구현 가능하고, 사용법도 (그렇게) 어렵지 않다는 점에서 매력적이라고 느꼈습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 팀 세미나 주제로 이녀석을 들고가서 라이브러리 도입에 대해 얘기해봤는데,&lt;br /&gt;의존성 / 유지보수성 / 전파성을 모두 고려해야하는 문제로 결국 실무에선 보수적인 태도로 보게 되더라고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 다양한 모션을 필요로 하는 서비스가 아니라는 것도 있고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;framer-motion은 디자인+개발을 모두하는 만능일꾼에게는 빠른 구현이 가능하다는 점에선 좋은 선택지가 될 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #dddddd;&quot;&gt;&lt;s&gt;motion 말고도 다른 사람들이 자주 쓰는 리액트 애니메이션 라이브러리에 뭐가 있는지는 openbase에 서 볼 수 있...었으나 닫혔습니다^_ㅠ&lt;/s&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;&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;긴 스크롤을 끝까지 내리셨다니 hoxy..  ?&lt;/p&gt;</description>
      <category>Blog/Library</category>
      <author>나나 (nykim)</author>
      <guid isPermaLink="true">https://anneslab.tistory.com/114</guid>
      <comments>https://anneslab.tistory.com/114#entry114comment</comments>
      <pubDate>Thu, 8 Sep 2022 19:00:35 +0900</pubDate>
    </item>
    <item>
      <title>[아무튼 Sass] 3. 더 편하게 CSS 다루기 - mixin, extend, 모듈화(import/use)</title>
      <link>https://anneslab.tistory.com/113</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0sass-cover2.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi1kep/btryAXjl06K/qmeOuECJRyotrWsb37q2qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi1kep/btryAXjl06K/qmeOuECJRyotrWsb37q2qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi1kep/btryAXjl06K/qmeOuECJRyotrWsb37q2qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi1kep%2FbtryAXjl06K%2FqmeOuECJRyotrWsb37q2qk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;450&quot; data-filename=&quot;0sass-cover2.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbahAgT%2FbtryDbU9xYj%2F54PYLJkWYV2GYe6TjyUj7k%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프롤로그&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시리즈의 3번째 글! 이런 분들이 읽으시면 좋아요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- CSS를 다룰 수 있어요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 하지만 Sass는 잘 몰라요&lt;/p&gt;
&lt;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;style3&quot;&gt;&lt;span style=&quot;color: #ff1a1a;&quot;&gt;&lt;b&gt;아무튼 Sass 시리즈&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;https://nykim.work/97&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;1. 시작하기 - Sass 개념, 컴파일러 설치&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://nykim.work/111&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2. 기본 작성법 익히기 - 중첩, 참조, 변수, 보간&lt;/a&gt;&lt;br /&gt;&lt;b&gt;3. 더 편하게 CSS 다루기 - mixin, extend, 모듈화(import/use)&lt;/b&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;  Here!&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;https://anneslab.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;4. 더 멋지게 활용하기 - 흐름제어, 내장 모듈, 함수&lt;/a&gt;&lt;/blockquote&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;br /&gt;- mixin&lt;br /&gt;- extend&lt;br /&gt;- 모듈화(import/use)&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbahAgT%2FbtryDbU9xYj%2F54PYLJkWYV2GYe6TjyUj7k%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 조금 더 우리의 작업을 편하게 해줄 Sass 문법을 써봅시다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 @으로 시작하는 At-Rules입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 CSS에서도 @으로 시작하는 문법들이 몇 개 있었죠. (@font-face 또는 @counter-style, @media 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;Sass 에서는 여기에 더해 어썸한 기능들을 제공하는 at-rules 들이 추가됩니다. 예를 들면...&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;@mixin / @include&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 스타일 청크(묶음)를 쉽게 재사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;@extend&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 여러 선택자가 스타일을 상속받도록 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;@import / @use&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 다른 스타일시트에 있는 변수, 함수, 믹스인 등을 불러와 결합해서 쓸 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;@function&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: SassScript에서 사용할 수 있는 사용자 정의 함수를 만들 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;@if / @each / @for&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 상황에 맞춰 CSS가 적용되도록 흐름을 제어할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&amp;nbsp; &amp;nbsp;(이 모든 게 공짜..!)&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 알아볼 것은 '찍어내기 틀'인 믹스인(mixin) 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;믹스인을 사용하면 여기저기 써먹을 수 있는 스타일을 정의할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;① @mixin {이름} 으로 공통으로 쓸 속성들을 묶습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;② @include {믹스인 이름} 으로 불러와 사용합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;③  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649235588396&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@mixin set-color {
  color: #fff;
  background-color: yellow;
}

h1 {
  @include set-color
}

p {
  @include set-color
}&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0-3-mixin.png&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xeQXT/btryEsPJs2d/UVsknEDq6piEleNkR6rKcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xeQXT/btryEsPJs2d/UVsknEDq6piEleNkR6rKcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xeQXT/btryEsPJs2d/UVsknEDq6piEleNkR6rKcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxeQXT%2FbtryEsPJs2d%2FUVsknEDq6piEleNkR6rKcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;357&quot; data-filename=&quot;0-3-mixin.png&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&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;반복되는 속성을 하나하나 타이핑할 필요가 없으니 좋네요!&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;TMI&lt;/b&gt;&lt;br /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수와 마찬가지로 믹스인 이름을 지을 때 하이픈(-)과 언더스코어(_)는 동일하게 취급됩니다.&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;믹스인에게 인자를 주자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;믹스인은 '인자(Arguments)'를 가질 수 있습니다. 넵, 함수가 실행될 때 넘겨받는 값 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 호출할 때마다 다르게 정의할 수도 있다는 뜻이죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속성은 동일하게 지정하지만 값은 다르게 주고 싶다면 아래처럼 인자를 달리 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649235588396&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@mixin set-color($color, $bg-color) {
    color: $color;
    background-color: $bg-color;
}

h1 {
    @include set-color(#000, #f0f0f0);
}

p {
    @include set-color(#bada77, #234510);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1567&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/viedq/btryBtbiZ0w/cFSzBO4284drVxzoMcniw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/viedq/btryBtbiZ0w/cFSzBO4284drVxzoMcniw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/viedq/btryBtbiZ0w/cFSzBO4284drVxzoMcniw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fviedq%2FbtryBtbiZ0w%2FcFSzBO4284drVxzoMcniw0%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;860&quot; height=&quot;403&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1567&quot; data-origin-height=&quot;734&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;모든 인자를 다 넘겨받는 대신, &lt;b&gt;일부 인자에는 기본값을 주거나 특정 인자만 따로 넘기는 것도 가능합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 경우가 있겠죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;background-image 를 여기저기에 써야 하는 상황입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649236046594&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.logo {
  background-image: url('images/logo.png');
}

.arrow {
  background-image: url('images/arrow.png');
}

.photo {
  background-image: url('images/photo.jpg');
}&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;우리가 Sass를 쓰는 이유는? 귀찮으니까요  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 보니까 중복되는 부분이 꽤 많은데 믹스인으로 처리할 수 있지 않을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0-3-mixin-example2.png&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;871&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFXvZ3/btryCEQRnMz/HS33Insz99oKcNcdIIqTX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFXvZ3/btryCEQRnMz/HS33Insz99oKcNcdIIqTX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFXvZ3/btryCEQRnMz/HS33Insz99oKcNcdIIqTX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFXvZ3%2FbtryCEQRnMz%2FHS33Insz99oKcNcdIIqTX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;526&quot; data-filename=&quot;0-3-mixin-example2.png&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;871&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;배경 이미지를 쓸 때 지정해야 하는 값은 1) 경로 2) 파일명 3) 파일확장자로 나눠 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 파일명은 계속 바뀌지만, 경로는 images/ 일 가능성이 높고 확장자도 .png를 쓸 확률이 높네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이 3가지를 인자로 만들어 믹스인에 쏙 넣습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0-3-mixin-example3.png&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwwqzt/btryCDj7t93/2On9mP5T2aC1UnjD5GoRM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwwqzt/btryCDj7t93/2On9mP5T2aC1UnjD5GoRM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwwqzt/btryCDj7t93/2On9mP5T2aC1UnjD5GoRM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwwqzt%2FbtryCDj7t93%2F2On9mP5T2aC1UnjD5GoRM0%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;860&quot; height=&quot;293&quot; data-filename=&quot;0-3-mixin-example3.png&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;488&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;pre id=&quot;code_1649236097830&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@mixin bg($name, $path: &quot;/images/&quot;, $format: &quot;png&quot;) {
    background-image: url(&quot;#{$path}#{$name}.#{$format}&quot;);
}

.logo {
  @include bg('logo');
}

.arrow {
  @include bg('arrow');
}

.photo {
  @include bg('photo', $format:'jpg');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복되는 부분을 확 줄이고 인자만으로 배경 이미지를 쉽게 넣을 수 있게 됐습니다!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 인자는 차례대로 넣어야 하기 때문에 &lt;b&gt;특정 인자만 넣으려면 &lt;/b&gt;&lt;code&gt;$format:'jpg'&lt;/code&gt;&lt;b&gt; 처럼 콕 집어서 넣어야 합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 &lt;b&gt;기본값이 없는 인자는 필수값으로 취급&lt;/b&gt;되므로, &lt;code&gt;@include bg();&lt;/code&gt; 처럼 사용하면 에러가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 값을 줄 때 &lt;code&gt;#{}&lt;/code&gt; &lt;b&gt;보간을 사용해서 문자열 안에 변수를 집어넣은 것&lt;/b&gt;도 확인해보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 이미지 폴더의 경로가 바뀌었을 때 믹스인만 수정하면 되니 수정 포인트가 줄어들죠.&lt;/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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&lt;b&gt;TMI&lt;br /&gt;&lt;/b&gt;&lt;/b&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인자의 개수를 확정할 수 없는 상태라면 ... 를 써서 임의의 인자를 전달&lt;/b&gt;할 수 있습니다.&lt;br /&gt;ex) &lt;code&gt;@mixin button ($size, $color, $args...) { }&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;임의 인자는 @for나 @each 같은 제어문과 함께 사용하기 좋아요. 이것도 나중에 다뤄봅니다.&lt;/span&gt;&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;믹스인의 레벨은?!&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;믹스인은&amp;nbsp;최상위&amp;nbsp;문(Top-Level&amp;nbsp;Statements)이므로,&amp;nbsp;&lt;b&gt;스타일시트의&amp;nbsp;최상위&amp;nbsp;레벨에서&amp;nbsp;사용&lt;/b&gt;합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다는 건 &lt;b&gt;최상위 문이 아닌 다른 문을 믹스인 속에 포함시켜 쓸 수 있다&lt;/b&gt;는 거죠!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 if 문을 써볼 수 있겠네요. 아직 @if를 배우기 전이지만 대충 감이 오실 거예요.&lt;/p&gt;
&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;radius 값이 인자로 넘어온 경우에만 border-radius 속성을 적용하는 예시입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649236403355&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@mixin button($size, $radius: 0) {
  width: $size;
  height: $size;

  @if $radius != 0 {
    border-radius: $radius;
  }
}

.btn-square {
  @include button(100px);
}

.btn-circle {
  @include button(100px, $radius: 50%);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;gOePQKQ&quot; data-user=&quot;nykim_&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/nykim_/pen/gOePQKQ&quot;&gt; Untitled&lt;/a&gt; by nanalike (&lt;a href=&quot;https://codepen.io/nykim_&quot;&gt;@nykim_&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;믹스인에 내용 덧붙이기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@content 구문&lt;/b&gt;을 쓰면 @include로 불러와 쓸 때 내용을 추가로 입력할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657009075447&quot; class=&quot;scss&quot; data-ke-language=&quot;scss&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1) 믹스인을 만들고...
@mixin hover {
    &amp;amp;:hover {
        font-weight: bold;
        @content;
    }
    &amp;amp;.on {
        @content;
    }
}

button {
   // 2) 불러와 쓸 때 내용을 입력합니다.
    @include hover {
        color: red;
    }
}

/*
 * 출력결과:
 
 button:hover {
   font-weight: bold;
   color: red;
 }
 
 button.on {
   color: red;
 }
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 자주 쓰는 미디어쿼리 분기문을 믹스인으로 만들어 놓고, 다른 곳에서 불러와 쓰는 용도로 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657009547551&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Break Point
$tablet: 768px;
$mobile: 540px;


// Tablet
@mixin res--tablet {
  @media screen and (min-width: #{$tablet}) {
    @content;
  }
}

// Mobile
@mixin res--mobile {
  @media screen and (max-width: #{$mobile}) {
    @content;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1657009585324&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.title {
  font-size: 16px;
    
  @include res--tablet {
    font-size: 14px;
  }
    
  @include res--mobile {
    font-size: 12px;
  }
}

/*
  * 출력:

  .title {
    font-size: 16px;
  }

  @media screen and (min-width: 768px) {
    .title {
      font-size: 14px;
    }
  }
  
  @media screen and (max-width: 540px) {
    .title {
      font-size: 12px;
    }
  }
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 각 분기문에서 요소가 어떤 형태로 변할지 한눈에 보이죠! 수정하기도 쉽고요 ;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;번외: 애용하는 믹스인을 소개합니다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 애용하는 믹스인은  IR 기법(접근성을 위한 대체 텍스트 표시)과 말줄임 처리를 위한 속성입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요 두 개는 프로젝트를 할 때마다 기본 아이템처럼 만들고 시작하는 편이에요✌️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657115520713&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@mixin blind {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  border: 0;
  white-space: nowrap;
  clip: rect(0, 0, 0, 0);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1657115520713&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@mixin ellipsis($lines: 1) {
  @if ($lines==1) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  } @else {
    display: -webkit-box;
    overflow: hidden;
    text-overflow: ellipsis;
    -webkit-line-clamp: $lines;
    -webkit-box-orient: vertical;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbahAgT%2FbtryDbU9xYj%2F54PYLJkWYV2GYe6TjyUj7k%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 @extend를 살펴봅니다. 이건 말그대로 '확장'으로, 기본 내용에서 뭔가를 덧붙일 때 사용해요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 이미 존재하는 셀렉터를 @extend로 지정해주면 됩니다.&lt;/p&gt;
&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;그러면 공통되는 부분(폰트굵게+보더있음)을 먼저 선언하고 @extend로 공통 스타일을 상속받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657099171085&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.button {
  font-weight: bold;
  border-width: 1px;
}

.button-confirm {
  @extend .button;
  border-color: blue;
}

.button-error {
  @extend .button;
  border-color: red;
}


/*
 * 출력:

 .button, .button-error, .button-warn {
    font-weight: bold;
    border-width: 1px;
 }

 .button-confirm {
    border-color: blue;
 }

 .button-error {
    border-color: red;
 }
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;OJvMawd&quot; data-user=&quot;nykim_&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/nykim_/pen/OJvMawd&quot;&gt; Untitled&lt;/a&gt; by nanalike (&lt;a href=&quot;https://codepen.io/nykim_&quot;&gt;@nykim_&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&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;.button-confirm과 .button-error는 거기서 &quot;확장&quot;된 스타일을 가지고 있고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;믹스인과 마찬가지로 반복되는 부분을 적지 않아도 되니 엄청나게 편해지네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;placeholder&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;익스텐드는 확장시킬 '기본 스타일 선언'이 필요합니다. 그렇다는 건 이 스타일은 컴파일이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 오로지 확장만을 위해 스타일을 선언하고 싶다면 % 기호를 사용할 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;%가 포함된 셀렉터는 오로지 @extend로 불러와 쓰기 위해 사용되며 컴파일된 CSS에 포함되지 않습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;rNdxyKv&quot; data-user=&quot;nykim_&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/nykim_/pen/rNdxyKv&quot;&gt; Untitled&lt;/a&gt; by nanalike (&lt;a href=&quot;https://codepen.io/nykim_&quot;&gt;@nykim_&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;vs3-1-1.png&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;891&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0xXzZ/btrGElDnYhG/2zXJEC2uRkhNXiOeYwnKvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0xXzZ/btrGElDnYhG/2zXJEC2uRkhNXiOeYwnKvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0xXzZ/btrGElDnYhG/2zXJEC2uRkhNXiOeYwnKvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0xXzZ%2FbtrGElDnYhG%2F2zXJEC2uRkhNXiOeYwnKvK%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;860&quot; height=&quot;533&quot; data-filename=&quot;vs3-1-1.png&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;891&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;&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;@mixin vs. @extend&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이쯤 들어 생기는 의문, 믹스인과 익스텐드는 꽤 비슷하게 생겼는데 뭐가 다른 걸까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 스타일을&amp;nbsp;캡슐화하고&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;style2&quot;&gt;&lt;b&gt;@mixin&lt;/b&gt;&lt;br /&gt;- 인자를 넣을 수 있어요.&lt;br /&gt;- 선택자 관리가 쉬워서 전역으로 쓰기 편해요.&lt;br /&gt;- 컴파일된 CSS가 길어져요.&lt;/blockquote&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;b&gt;@extend&lt;/b&gt;&lt;br /&gt;- 연관성 있는 셀렉터들을 묶어 관리할 수 있어요.&lt;br /&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;p data-ke-size=&quot;size16&quot;&gt;앞서 컴파일된 CSS를 보면 알 수 있듯이&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@mixin: 미리 정의한 속성을 복사해서 따로 적용&lt;/li&gt;
&lt;li&gt;@extend: 선택자를 함께 묶어 속성을 한번에 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한다는 차이점이  있고, 여기서의 핵심은 &quot;얼마나 연관성이 있는가&quot;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택자끼리 의미론적 관계가 있다면 @extend를, 그렇지 않고 단순히 속성만 겹치는 관계라면 @mixin을 쓰는 것을 추천합니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 저는 '말줄임 처리하는 속성'을 믹스인으로 애용한다고 했는데요, @extend로 쓰지 않는 이유는 &quot;아무데서나&quot; 쓰이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 적용하는 게 헤더의 타이틀이 될 수도 있고, 본문이 될 수도 있고, 아니면 저기 푸터에 있는 버튼이 될 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들 각각은 전혀 연관성이 없습니다. 그저 같은 속성을 쓸 뿐이죠. 따라서 단순 복붙인 @mixin을 사용합니다.&lt;/p&gt;
&lt;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;vs3-1-2.png&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;1099&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDLdOc/btrGF8piRtm/jEnMPJWdWioRevKKSQwbGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDLdOc/btrGF8piRtm/jEnMPJWdWioRevKKSQwbGK/img.png&quot; data-alt=&quot;@mixin / @include&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDLdOc/btrGF8piRtm/jEnMPJWdWioRevKKSQwbGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDLdOc%2FbtrGF8piRtm%2FjEnMPJWdWioRevKKSQwbGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;535&quot; data-filename=&quot;vs3-1-2.png&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;1099&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;@mixin / @include&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;vs3-1-3.png&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;1099&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1gaba/btrGEwkmWfh/sEqG9JSWtHN6fgGpxtGh91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1gaba/btrGEwkmWfh/sEqG9JSWtHN6fgGpxtGh91/img.png&quot; data-alt=&quot;%placeholder / @extend&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1gaba/btrGEwkmWfh/sEqG9JSWtHN6fgGpxtGh91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1gaba%2FbtrGEwkmWfh%2FsEqG9JSWtHN6fgGpxtGh91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;514&quot; data-filename=&quot;vs3-1-3.png&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;1099&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;%placeholder / @extend&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;공통 속성을 @mixin과 @extend 로 각각 사용해본 결과입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일된 CSS를 보면, @extend 는 전혀 상관없는 요소들끼리 묶여 있는 모습이네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 셀렉터가 짧아서 그렇지, 클래스명이 길어지고 깊어질수록 이보다 더 지저분하게 선언될 게 분명합니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 CSS가 아주 길다면 수백줄은 떨어져 있는 녀석들을 불러다 같이 있으라고 세워놓은 셈이 되는 거고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@mixin의 경우 컴파일된 CSS가 더 길긴 하지만 큰 문제는 되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;대부분의 웹 서버는 반복되는 텍스트 청크를 처리하는 알고리즘으로 CSS를 압축하기 때문에 사용자가 다운로드해야하는 양을 크게 늘리지 않습니다. &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;즉, 컴파일된 CSS의 길이에 크게 신경쓸 필요가 없다는 뜻이죠.&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;@extend가 적합한 곳은 하나의 컴포넌트가 여러 스타일을 갖는 경우, 그것들을 하나의 스타일시트에서 관리할 때입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 아까처럼 버튼을 스타일링할 때 button.scss에서 &lt;code&gt;@extend %button&lt;/code&gt;으로 확장시켜나가는 거죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 셀렉터끼리 연관성이 높기 때문이 꼬일 염려가 없고 관리하기도 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;JjLGemr&quot; data-user=&quot;nykim_&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/nykim_/pen/JjLGemr&quot;&gt; Untitled&lt;/a&gt; by nanalike (&lt;a href=&quot;https://codepen.io/nykim_&quot;&gt;@nykim_&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;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;저는 @extend가 제약이 많다고 여겨 별로 사용하지 않는 편이지만 그렇다고 @extend가 나쁜 것만은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 방법이 적합할지는 사람 바이 사람, 플젝 바이 플젝이죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 처음에도 언급했듯이 &quot;나 편하자고 쓰는 도구&quot;인 만큼 자기 손에 착착 붙는 방법을 택해서 쓰시면 됩니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbahAgT%2FbtryDbU9xYj%2F54PYLJkWYV2GYe6TjyUj7k%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 모듈화: CSS 쪼개기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 CSS를 쪼개볼 차례입니다!  ✨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 CSS는 보통 하나의 파일을 쓰지만, 그렇다고 해서 작업할 때도 한곳에 몰아 쓸 필요는 없죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤더 / 메인 / 푸터 각 영역의 스타일을 분리해서 작성한다면 더 효율적으로 스타일시트를 관리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 _header.scss / _main.scss / _footer.scss 와 같이 쪼개놓는 거죠! 이걸 필요할 때마다 불러와 사용하면 됩니다.&lt;/p&gt;
&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;헤더에 수정사항이 생긴다면 _header.scss 파일만 수정하면 되니까요. 그럼 _header.scss가 컴파일 되면서 해당 파일을 사용하는 home.css, mypage.css, cart.css 등도 알아서 바뀌겠죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그외에 reset이나 common 같은 것들도 따로 작성해 두면 수정할 때 그 파일만 열면 되니까 편하겠네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 쪼개놓은 스타일시트를 '모듈'이라고 하며, 지금부터 CSS를 모듈화하는 법을 알아봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@import&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;방법은 간단합니다. 넵, 그냥 따로따로 작성하고 @import로 불러오면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;사실 Sass가 아닌 일반 CSS에서도 스타일시트 내 다른 스타일시트를 임포트할 수 있어요.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657162146835&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* homepage.css */

@import &quot;common.css&quot;;
@import &quot;header.css&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;참고:&amp;nbsp;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/CSS/@import&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/CSS/@import&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 data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;하지만 &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;위와 같이 CSS를 불러오는 건 별로 권장하지 않는 방법인데요, 이유는 브라우저가 각각의 CSS를 직렬로 불러오기 때문입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;따라서 common 요청하고, header 요청하고, 그 다음 CSS 요청하고... 하느라 로딩이 느려집니다.&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;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;한편, Sass에서 @import를 통해 다른 스타일시트를 불러오는 것은 조금 다릅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Sass의 임포트는 &quot;컴파일 과정에서 처리되기 때문에&quot; 브라우저가 HTTP 요청을 여러 번 하지 않아도 되기 때문이죠!&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;_common.scss와 style.scss 파일을 만들고 각각 내용을 작성해 보겠습니다.&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_1657181587022&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// _common.scss
button {margin: 0; padding: 0; background: #fff;}

// style.scss
@import 'common';
.button-important {color: red;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;a.png&quot; data-origin-width=&quot;2682&quot; data-origin-height=&quot;894&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfBGbS/btrGJGtqHig/2EmnKpbrVNlmqmmT5spOYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfBGbS/btrGJGtqHig/2EmnKpbrVNlmqmmT5spOYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfBGbS/btrGJGtqHig/2EmnKpbrVNlmqmmT5spOYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfBGbS%2FbtrGJGtqHig%2F2EmnKpbrVNlmqmmT5spOYK%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;860&quot; height=&quot;287&quot; data-filename=&quot;a.png&quot; data-origin-width=&quot;2682&quot; data-origin-height=&quot;894&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;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;그리고 컴파일을 시켜보면...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;_common.scss와 style.scss의 내용이 모두 들어가 있는 style.css 파일 하나만 생성됩니다.&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;b.png&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;894&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhv30Q/btrGFGnpxnO/GhlhdB9oY0SYwph5DlcSsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhv30Q/btrGFGnpxnO/GhlhdB9oY0SYwph5DlcSsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhv30Q/btrGFGnpxnO/GhlhdB9oY0SYwph5DlcSsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhv30Q%2FbtrGFGnpxnO%2FGhlhdB9oY0SYwph5DlcSsK%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;860&quot; height=&quot;471&quot; data-filename=&quot;b.png&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;894&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;오호라! 그럼 공통 스타일은 _common.scss에다 몰아놓고, 필요할 때마다 다른 scss 파일에서 가져와 쓰면 되겠군요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;@import를 통해 변수, 믹스인도 가져올 수 있어요. 이들만 따로 모아서 _variable.scss, _mixin.scss 등으로 분리하는 것도 좋겠네요.&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;언더바_는 무시합니다 &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;(__);&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서&amp;nbsp;알&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;점은&amp;nbsp;이름&amp;nbsp;앞에&amp;nbsp;언더바(_)가&amp;nbsp;붙은&amp;nbsp;scss는&amp;nbsp;컴파일되지&amp;nbsp;않는다는&amp;nbsp;것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언더바 없이 작성한 스타일시트는 모두 독립적으로 컴파일됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-07-07 at 5.20.11 PM.png&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vPMMp/btrGKcSVjKD/Asdmbto3dhZF0775QpeTK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vPMMp/btrGKcSVjKD/Asdmbto3dhZF0775QpeTK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vPMMp/btrGKcSVjKD/Asdmbto3dhZF0775QpeTK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvPMMp%2FbtrGKcSVjKD%2FAsdmbto3dhZF0775QpeTK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;248&quot; data-filename=&quot;Screen Shot 2022-07-07 at 5.20.11 PM.png&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 앞에 언더바가 있는 파일을 보면 '이건 컴파일이 되지 않고, 가져다 쓰기 위한 모듈 파일이구나!'라고 파악할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;또 하나 알 수 있는 점!&lt;br /&gt;스타일시트를 불러올 때 언더바나 확장자 없이 @import 'common'; 만으로 가져왔다는 점 눈치채셨나요?&lt;br /&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;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;@use&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@import는 쉽고 빠르게 다른 스타일시트를 가져올 수 있도록 해주지만, &lt;br /&gt;&lt;b&gt;Dart Sass를 쓰고 있다면 @import 대신 @use를 사용하는 것이 좋습니다.&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;이유는 @import는 대상을 전역으로 불러오기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수, 믹스인, 함수 등(Sass에서는 이걸 '멤버'라고 불러요)에 전역으로 접근할 수 있기 때문에, 각 멤버가 어디서 정의되었는지 파악하기가 어렵고 이름이 충돌할 수 있다는 단점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657184567000&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// style.scss

@import 'module1';
@import 'module2';
@import 'module3';

h1 {
  font-weight: $font-weight;
}&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;세 개의 스타일시트를 불러와 사용하는 style.scss 파일입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수가 있는데 내부에서 보이지 않는 걸로 보아 다른 스타일시트에서 선언된 것 같네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 모듈 파일을 열어봤더니...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-07-07 at 6.17.38 PM.png&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm5NCV/btrGLf2BVXY/VDUrV3VtZBiVjTH5fKnOjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm5NCV/btrGLf2BVXY/VDUrV3VtZBiVjTH5fKnOjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm5NCV/btrGLf2BVXY/VDUrV3VtZBiVjTH5fKnOjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm5NCV%2FbtrGLf2BVXY%2FVDUrV3VtZBiVjTH5fKnOjk%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;860&quot; height=&quot;194&quot; data-filename=&quot;Screen Shot 2022-07-07 at 6.17.38 PM.png&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;648&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;$font-weight라는 동일한 이름의 변수가 module1에도 있고 module2에도 있네요&lt;span style=&quot;color: #777777;&quot;&gt;?!&lt;/span&gt; 게다가 3은 2를 부르고 있고... &lt;span style=&quot;color: #777777;&quot;&gt; &amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 컴파일된 결과는 어떨까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-07-07 at 6.19.11 PM.png&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cq6HIv/btrGJFBsOH4/EccmPkouguncWj9T0kuuNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cq6HIv/btrGJFBsOH4/EccmPkouguncWj9T0kuuNk/img.png&quot; data-alt=&quot;200은 어디서 받아온 거지..?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cq6HIv/btrGJFBsOH4/EccmPkouguncWj9T0kuuNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcq6HIv%2FbtrGJFBsOH4%2FEccmPkouguncWj9T0kuuNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;334&quot; data-filename=&quot;Screen Shot 2022-07-07 at 6.19.11 PM.png&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;200은 어디서 받아온 거지..?&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;변수를&amp;nbsp;받아와서&amp;nbsp;썼다는&amp;nbsp;건&amp;nbsp;알겠는데,&amp;nbsp;어디서&amp;nbsp;받아왔고&amp;nbsp;누가&amp;nbsp;덮어쓴&amp;nbsp;건지&amp;nbsp;알&amp;nbsp;수가&amp;nbsp;없습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, 주석을 보면 알 수 있지만 같은 모듈을 여러번 @import하면 전부 컴파일한다는 단점도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한&amp;nbsp;단점을&amp;nbsp;해결하기&amp;nbsp;위해&amp;nbsp;개발된&amp;nbsp;것이&amp;nbsp;@use입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;Sass 팀에서도 2022년 10월까지는 &lt;a href=&quot;https://sass-lang.com/blog/the-module-system-is-launched#future-plans&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@import랑 헤어지겠다고 다짐&lt;/a&gt;한 모양이라 @use 사용을 추천합니다.&amp;nbsp;&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2022년 7월, @import의 사용 중단 및 제거 일정이 연기되었습니다. 80%의 사용자가 Dart Sass를 사용할 때까지 기다렸다가 사용을 중단할 예정이라고 하네요!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 글에서도 앞으로 @use를 사용해 모듈을 가져옵니다!&lt;/p&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;b&gt;@use의 주요 특징&lt;/b&gt;&lt;br /&gt;- 항상 파일 시작부분에 쓰여야 합니다. 스타일 규칙에 포함될 수 없어요.&lt;br /&gt;- 가져온 멤버는 모두 네임스페이스를 갖습니다.&amp;nbsp;&lt;br /&gt;-&amp;nbsp;여러번&amp;nbsp;@use&amp;nbsp;하더라도&amp;nbsp;중복해서&amp;nbsp;가져오지&amp;nbsp;않습니다.&amp;nbsp;&lt;br /&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;@use도 @import와 비슷하게 &lt;code&gt;@use &amp;lt;불러올 scss 파일&amp;gt;&lt;/code&gt;로 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이점이 있다면 &lt;b&gt;멤버를 사용할 때 네임스페이스를 지정해 줘야 한다는 것!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657186181364&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use &quot;module1&quot;;
@use &quot;module2&quot;;
@use &quot;module3&quot;;

h1 {
  font-weight: module1.$font-weight;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게&amp;nbsp;하면&amp;nbsp;어느&amp;nbsp;모듈에서&amp;nbsp;가져온&amp;nbsp;변수인지&amp;nbsp;한눈에&amp;nbsp;알&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수,&amp;nbsp;믹스인&amp;nbsp;등&amp;nbsp;다른&amp;nbsp;멤버들에게도&amp;nbsp;동일하게&amp;nbsp;네임스페이스를&amp;nbsp;붙이면&amp;nbsp;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@include할 때 어느 파일의 믹스인인지 이름을 적어주세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657186230060&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'mixin';

.title {
  @include mixin.ellipsis;
}&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;@use를 쓰면 네임스페이스를 같이 적어야 하므로 이름을 지을 때 간단하게 짓는 게 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@import를 쓸 때는 충돌을 피하기 위해 최대한 구체적으로 지었던 것과 비교되죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657186603863&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use 'me';
@use 'you';

.my-name {
  content: me.$name;
}

.your-name {
  content: you.$name;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네임스페이스는&amp;nbsp;기본적으로&amp;nbsp;파일명을&amp;nbsp;가리킵니다.&amp;nbsp;(URL의&amp;nbsp;마지막&amp;nbsp;요소)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만&amp;nbsp;난&amp;nbsp;다른&amp;nbsp;네임스페이스로&amp;nbsp;부르고&amp;nbsp;싶어!&amp;nbsp;하시는&amp;nbsp;분들은&amp;nbsp;다르게&amp;nbsp;설정할&amp;nbsp;수도&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@use &quot;&amp;lt;url&amp;gt;&quot; as &amp;lt;namespace&amp;gt;&lt;/code&gt; 이렇게 하면 더 짧게 호출하거나 동일한 이름으로 여러 모듈을 불러오는 것도 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657186693055&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use &quot;module1&quot; as m;
@use &quot;module2&quot; as mmmmm;

h1 {
  color: mmmmm.$color;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것마저 귀찮다면?! 네임스페이스 부분을 * (와일드카드)로 쓰면 네임스페이스 없이 모듈을 불러올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1657186768709&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@use &quot;module1&quot; as *;
@use &quot;module2&quot; as *;

h1 {
  color: $color; //단, 동일한 변수가 있는 경우 충돌
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-07-07 at 6.47.34 PM.png&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;1380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0Cq6K/btrGLhlR8ia/jBFmJ6SHpZe2idy6khvCr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0Cq6K/btrGLhlR8ia/jBFmJ6SHpZe2idy6khvCr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0Cq6K/btrGLhlR8ia/jBFmJ6SHpZe2idy6khvCr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0Cq6K%2FbtrGLhlR8ia%2FjBFmJ6SHpZe2idy6khvCr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;605&quot; data-filename=&quot;Screen Shot 2022-07-07 at 6.47.34 PM.png&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;1380&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;하지만 멤버 이름이 쉽게 충돌날 수 있기 때문에 주의하여 사용하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 @import와 다르게 @use는 덮어씌우지 않고 컴파일 단계에서 알려줍니다. 친절쓰!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멤버명&amp;nbsp;앞에&amp;nbsp;언더바(_)나&amp;nbsp;하이픈(-)이&amp;nbsp;붙으면&amp;nbsp;프라이빗으로&amp;nbsp;간주하며&amp;nbsp;다른&amp;nbsp;모듈에서&amp;nbsp;불러와&amp;nbsp;쓸&amp;nbsp;수&amp;nbsp;없습니다.&amp;nbsp;일종의 지역변수죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-07-07 at 6.46.23 PM.png&quot; data-origin-width=&quot;2050&quot; data-origin-height=&quot;1244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cj6NS9/btrGKc6HOIf/2Ab60mjPKo2iuGmM7y2Uv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cj6NS9/btrGKc6HOIf/2Ab60mjPKo2iuGmM7y2Uv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cj6NS9/btrGKc6HOIf/2Ab60mjPKo2iuGmM7y2Uv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcj6NS9%2FbtrGKc6HOIf%2F2Ab60mjPKo2iuGmM7y2Uv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;485&quot; data-filename=&quot;Screen Shot 2022-07-07 at 6.46.23 PM.png&quot; data-origin-width=&quot;2050&quot; data-origin-height=&quot;1244&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;&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;@forward&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;@use를 사용하면 오로지 불러온 곳에서만 멤버들을 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;분리된 변수나 믹스인을 쓰려면 각 모듈 파일 상단에서 불러와야 한다는 뜻입니다.&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;Screen Shot 2022-07-07 at 8.11.46 PM.png&quot; data-origin-width=&quot;2502&quot; data-origin-height=&quot;1022&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnkB4L/btrGJ8DjofW/AQoTpjvoDj0mUfhkzZOOs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnkB4L/btrGJ8DjofW/AQoTpjvoDj0mUfhkzZOOs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnkB4L/btrGJ8DjofW/AQoTpjvoDj0mUfhkzZOOs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnkB4L%2FbtrGJ8DjofW%2FAQoTpjvoDj0mUfhkzZOOs0%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;860&quot; height=&quot;351&quot; data-filename=&quot;Screen Shot 2022-07-07 at 8.11.46 PM.png&quot; data-origin-width=&quot;2502&quot; data-origin-height=&quot;1022&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;네임스페이스를 명확히 한다는 목적에 맞춰 저렇게 작성하는 것이 좋지만, 그래도 좀 귀찮은 것도 사실이죠.&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이럴 땐 @forward를 써봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;helper/ 폴더 안에 _index.scss 라는 진입점을 만들고, 폴더 내 모듈을 _index.scss가 &quot;전달&quot;하도록 합니다.&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;Screen Shot 2022-07-07 at 8.22.09 PM.png&quot; data-origin-width=&quot;2658&quot; data-origin-height=&quot;1380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ky6gP/btrGJ8QRhrL/Ay5SgLgeo9Ibswqf5upy5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ky6gP/btrGJ8QRhrL/Ay5SgLgeo9Ibswqf5upy5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ky6gP/btrGJ8QRhrL/Ay5SgLgeo9Ibswqf5upy5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fky6gP%2FbtrGJ8QRhrL%2FAy5SgLgeo9Ibswqf5upy5k%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;860&quot; height=&quot;447&quot; data-filename=&quot;Screen Shot 2022-07-07 at 8.22.09 PM.png&quot; data-origin-width=&quot;2658&quot; data-origin-height=&quot;1380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;원한다면 @forward 할 때 접두사를 지정할 수도 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;code&gt;@forward &quot;&amp;lt;url&amp;gt;&quot; as &amp;lt;prefix&amp;gt;-*&lt;/code&gt;로 전달하고, 사용할 때는 &lt;code&gt;&amp;lt;prefix&amp;gt;-&lt;/code&gt;를 앞에 붙여 씁니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;네임스페이스를 지정할 거라 이름을 너무 짧게 지어서 의미를 파악하기 어렵다면 이렇게 쓸 수도 있겠죠.&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;Screen Shot 2022-07-07 at 8.23.49 PM.png&quot; data-origin-width=&quot;2658&quot; data-origin-height=&quot;1380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZgP7O/btrGLAMtRGt/6rDy7zX3p0ouj1lnkuVMI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZgP7O/btrGLAMtRGt/6rDy7zX3p0ouj1lnkuVMI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZgP7O/btrGLAMtRGt/6rDy7zX3p0ouj1lnkuVMI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZgP7O%2FbtrGLAMtRGt%2F6rDy7zX3p0ouj1lnkuVMI1%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;860&quot; height=&quot;447&quot; data-filename=&quot;Screen Shot 2022-07-07 at 8.23.49 PM.png&quot; data-origin-width=&quot;2658&quot; data-origin-height=&quot;1380&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;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;이런 식으로 component/ 폴더에도 _index.scss를 만들어 컴포넌트 관련 scss 들을 한번에 전달할 수도 있겠네요!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-07-07 at 8.16.51 PM.png&quot; data-origin-width=&quot;2490&quot; data-origin-height=&quot;1336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/42fTp/btrGGRPTbj7/TZmDqsTNKcrsj9UNUU7vkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/42fTp/btrGGRPTbj7/TZmDqsTNKcrsj9UNUU7vkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/42fTp/btrGGRPTbj7/TZmDqsTNKcrsj9UNUU7vkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F42fTp%2FbtrGGRPTbj7%2FTZmDqsTNKcrsj9UNUU7vkK%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;860&quot; height=&quot;461&quot; data-filename=&quot;Screen Shot 2022-07-07 at 8.16.51 PM.png&quot; data-origin-width=&quot;2490&quot; data-origin-height=&quot;1336&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; 그래서 어떻게 쪼갤까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;...는 사람 마음이죠! 저는 요런 식으로 쪼개는 취향입니다.&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt; (말이 좀...?)&lt;/i&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;pre id=&quot;code_1657193761422&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scss/
├─ base/
│  ├─ _index.scss
│  ├─ _reset.scss
│  ├─ _common.scss
├─ helpers/
│  ├─ _index.scss
│  ├─ _mixin.scss
│  ├─ _meidaQuery.scss
│  ├─ _variable.scss
│  ├─ _animation.scss
├─ components/
│  ├─ _index.scss
│  ├─ _button.scss
├─ pages/
│  ├─ _index.scss
│  ├─ _home.scss
├─ vendors/
├─ style.scss&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;base/: 리셋이나 노멀라이즈, 또는 아주 기본으로 쓰일 스타일(타이포그래피 등)&lt;/li&gt;
&lt;li&gt;helpers/: 직접 스타일을 적용하지 않지만 개발에 필요한 스타일(믹스인, 함수 등)&lt;/li&gt;
&lt;li&gt;components/: 말그대로 개별 컴포넌트!&lt;/li&gt;
&lt;li&gt;pages/: 말그대로 개별 페이지!&lt;/li&gt;
&lt;li&gt;vendors/: 외부 라이브러리 등을 사용하는 경우 필요한 스타일&lt;/li&gt;
&lt;li&gt;styles.scss: 컴파일할 스타일시트&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bahAgT/btryDbU9xYj/54PYLJkWYV2GYe6TjyUj7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbahAgT%2FbtryDbU9xYj%2F54PYLJkWYV2GYe6TjyUj7k%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에필로그&lt;/h2&gt;
&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;이것만 알아도 Sass의 반을 쓰는 거 아닐까요?! 하는 기능이었어요  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음엔 미처 다루지 못했던 @at-rules의 함수, 흐름 제어문을 다뤄보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직까진 그래도 Sass지 할 만한 기능들이라 꼭 짚어보고 싶네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #ff1a1a;&quot;&gt;&lt;b&gt;이 시리즈의 다음 글&lt;br /&gt;&lt;/b&gt;&lt;a href=&quot;https://anneslab.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;4. 더 멋지게 활용하기 - 흐름제어, 내장 모듈, 함수&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>Blog/CSS</category>
      <category>css</category>
      <category>SASS</category>
      <author>나나 (nykim)</author>
      <guid isPermaLink="true">https://anneslab.tistory.com/113</guid>
      <comments>https://anneslab.tistory.com/113#entry113comment</comments>
      <pubDate>Thu, 7 Jul 2022 21:02:16 +0900</pubDate>
    </item>
    <item>
      <title>[아무튼 Sass] 2. 기본 작성법 익히기 - 중첩, 참조, 변수, 보간</title>
      <link>https://anneslab.tistory.com/111</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0sass-cover.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wTBTT/btryt5oGN9n/ZwZS3dLsjz2K8IfkF0bRkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wTBTT/btryt5oGN9n/ZwZS3dLsjz2K8IfkF0bRkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wTBTT/btryt5oGN9n/ZwZS3dLsjz2K8IfkF0bRkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwTBTT%2Fbtryt5oGN9n%2FZwZS3dLsjz2K8IfkF0bRkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;450&quot; data-filename=&quot;0sass-cover.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdDnkn%2FbtrytjAcVfl%2F8dibLadpTUd9g35iPb29Y1%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프롤로그&lt;/h2&gt;
&lt;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://nykim.work/97&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[아무튼 Sass] 1. 시작하기&lt;/a&gt;의 다음 글입니다. 두 번째 글이지만 어느덧 반년만...  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 분들을 위한 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- CSS를 다룰 수 있어요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 하지만 Sass는 1도 몰라요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #ff1a1a;&quot;&gt;&lt;b&gt;아무튼 Sass 시리즈&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;https://nykim.work/97&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;1. 시작하기 - Sass 개념, 컴파일러 설치&lt;/a&gt;&lt;br /&gt;&lt;b&gt;2. 기본 작성법 익히기 - 중첩, 참조, 변수, 보간&lt;/b&gt; &lt;span style=&quot;color: #9d9d9d;&quot;&gt;  Here!&lt;br /&gt;&lt;a href=&quot;https://anneslab.tistory.com/113&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;3. 더 편하게 CSS 다루기 - mixin, extend, 모듈화(import/use)&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href=&quot;https://nykim.work/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;4. 더 멋지게 활용하기 - 흐름제어, 내장 모듈, 함수&lt;/a&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이번 글에서 다루는 것:&lt;br /&gt;- 중첩, 부모참조 선택자&lt;br /&gt;- 변수, 보간&lt;br /&gt;- 인라인 주석&lt;br /&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdDnkn%2FbtrytjAcVfl%2F8dibLadpTUd9g35iPb29Y1%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 중첩: 이것이 &quot;C&quot;SS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 글에서는 Sass 만 설치하고 똑 끝나버려서 '왜 굳이 이걸 설치해서 써야하지  ' 하는 생각이 들 수도 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 Sass는 쓰기 편하니까 믿어주세요(?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sass가 제공하는 어썸한 문법들을 몇 가지 알아볼 건데, 그 중 하나는 CSS를 중첩해서 사용하는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS의&amp;nbsp;뜻은?&amp;nbsp;&lt;b&gt;케스케이딩&amp;nbsp;스타일&amp;nbsp;시트&lt;/b&gt;죠!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넵. CSS의 가장 큰 특징은 &quot;C&quot;, 바로 케스케이딩&lt;span style=&quot;color: #ee2323;&quot;&gt;*&lt;/span&gt;에 있습니다. &lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;*&lt;/span&gt;cascading: 위에서 아래로 흐르는&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;CSS는&amp;nbsp;.grand-parent&amp;nbsp;&amp;gt;&amp;nbsp;.parent&amp;nbsp;&amp;gt;&amp;nbsp;.child와&amp;nbsp;같이&amp;nbsp;부모에서&amp;nbsp;자식&amp;nbsp;순서로&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;엉뚱한 애한테 스타일이 상속되지 않도록 케스케이딩을 관리하는 게 CSS 작성의 중요 포인트죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 CSS의 길고 길고 기다란 셀렉팅에선 cascading 의 느낌을 받을 수가 없는데요...&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;0sass.png&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OQ9iW/btryt6AFLQO/esSctFKWJRxSATCjRCPu5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OQ9iW/btryt6AFLQO/esSctFKWJRxSATCjRCPu5k/img.png&quot; data-alt=&quot;이건 흐른다기 보단 기어가는 거...?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OQ9iW/btryt6AFLQO/esSctFKWJRxSATCjRCPu5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOQ9iW%2Fbtryt6AFLQO%2FesSctFKWJRxSATCjRCPu5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;358&quot; data-filename=&quot;0sass.png&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;616&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;하지만 이걸 Sass 문법으로 작성한다면?!&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;0sass-2.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cY5nNz/btrytkMIlAF/Ozh8fGBWGTmgFWZU8KKn7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cY5nNz/btrytkMIlAF/Ozh8fGBWGTmgFWZU8KKn7k/img.png&quot; data-alt=&quot;A-ha!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cY5nNz/btrytkMIlAF/Ozh8fGBWGTmgFWZU8KKn7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcY5nNz%2FbtrytkMIlAF%2FOzh8fGBWGTmgFWZU8KKn7k%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;400&quot; height=&quot;436&quot; data-filename=&quot;0sass-2.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;686&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;A-ha!&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;구조가&amp;nbsp;시각적으로&amp;nbsp;바뀌면서&amp;nbsp;한눈에&amp;nbsp;들어옵니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제야&amp;nbsp;&quot;Cascading&quot;&amp;nbsp;이란&amp;nbsp;느낌이네요! 부모 선택자를&amp;nbsp;반복하지&amp;nbsp;않아도&amp;nbsp;되니&amp;nbsp;보기에도&amp;nbsp;훨씬&amp;nbsp;좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명은... 이게 끝이에요!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 사용해야 할지 감이 오실 텐데, 중첩하고&amp;nbsp;싶은&amp;nbsp;만큼&amp;nbsp;대괄호{}&amp;nbsp;안에&amp;nbsp;자식&amp;nbsp;셀렉터를&amp;nbsp;넣으면&amp;nbsp;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게&amp;nbsp;중첩해서&amp;nbsp;쓰면&amp;nbsp;좋은&amp;nbsp;점은&amp;nbsp;&lt;b&gt;어디서부터&amp;nbsp;어디까지가&amp;nbsp;셀렉터인지&amp;nbsp;한눈에&amp;nbsp;알&amp;nbsp;수&amp;nbsp;있다는&amp;nbsp;점&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649141697978&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.wrap {
  //...
  .main {
    //...
    .section {
       //...
       .article {
         //...
         .title {
           //...
         }
       }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;얼마나 중첩해서 쓸 것인가는 고민이 필요한 부분&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 너무 들여쓰는 경우 '그래서 얘 부모가 누구지...' 하고 한눈에 파악이 힘들 때가 있더라고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nykim.work/15&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;BEM 방법론&lt;/a&gt;을 사용한다면 Element 단위에서 끊어 쓰거나, CSS Module 을 사용한다면 어차피 중복되지 않을 테니 깊게 들여쓰지 않아도 되긴 하겠죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdDnkn%2FbtrytjAcVfl%2F8dibLadpTUd9g35iPb29Y1%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sass에는&amp;nbsp;특별한&amp;nbsp;선택자가&amp;nbsp;몇&amp;nbsp;개&amp;nbsp;있는데,&amp;nbsp;그&amp;nbsp;중&amp;nbsp;하나인&amp;nbsp;&lt;code&gt;&amp;amp;&lt;/code&gt; 를 소개합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;&amp;amp;는&amp;nbsp;중첩된&amp;nbsp;상태일&amp;nbsp;때&amp;nbsp;외부의&amp;nbsp;선택자를&amp;nbsp;참조할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0sass-3.png&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9eq5f/btryuWjS29L/MkUANyINZOjBR9OixIXppK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9eq5f/btryuWjS29L/MkUANyINZOjBR9OixIXppK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9eq5f/btryuWjS29L/MkUANyINZOjBR9OixIXppK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9eq5f%2FbtryuWjS29L%2FMkUANyINZOjBR9OixIXppK%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;380&quot; height=&quot;505&quot; data-filename=&quot;0sass-3.png&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;760&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짠, 부모선택자의 이름을 반복해서 쓸 필요없이 &lt;code&gt;&amp;amp;&lt;/code&gt; 하나만으로 뚝딱 대체할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 부모선택자의 이름을 계속해서 가져다 쓰는 BEM 방법론에 찰떡이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649142615856&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.accordion {
  max-width: 600px;
  margin: 4rem auto;
  width: 90%;
  font-family: &quot;Raleway&quot;, sans-serif;
  background: #f4f4f4;

  &amp;amp;__copy {
    display: none;
    padding: 1rem 1.5rem 2rem 1.5rem;
    color: gray;
    line-height: 1.6;
    font-size: 14px;
    font-weight: 500;

    &amp;amp;--open {
      display: block;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일된 결과와 비교해 보세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0sass-vs-css.png&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdrVHk/btryxrKtav2/bUa0pzElkG4ET4hkpQWKoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdrVHk/btryxrKtav2/bUa0pzElkG4ET4hkpQWKoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdrVHk/btryxrKtav2/bUa0pzElkG4ET4hkpQWKoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdrVHk%2FbtryxrKtav2%2FbUa0pzElkG4ET4hkpQWKoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;493&quot; data-filename=&quot;0sass-vs-css.png&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 자기 자신을 재선택할 때도 유용합니다. &lt;code&gt;&amp;amp;:hover&lt;/code&gt; 나 &lt;code&gt;&amp;amp; + p&lt;/code&gt; 처럼 스스로를 선택해서 작성할 수도 있죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649142661489&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;button {
  color: blue;

  &amp;amp;:hover {
    opacity: 0.5
  }

  &amp;amp;:disabled {
    opacity: 0.3;
    cursor: not-allowed;
  }

  &amp;amp;.isLoading {
    color: green;
  }

  &amp;amp; + button {
    margin-top: 10px;
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0sass-vs-css-2.png&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CGkqd/btryxcGNN0h/MOJNgRIVu0Kj6Ltd82jLN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CGkqd/btryxcGNN0h/MOJNgRIVu0Kj6Ltd82jLN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CGkqd/btryxcGNN0h/MOJNgRIVu0Kj6Ltd82jLN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCGkqd%2FbtryxcGNN0h%2FMOJNgRIVu0Kj6Ltd82jLN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;500&quot; data-filename=&quot;0sass-vs-css-2.png&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;가 컴파일되면서 부모 선택자로 치환되는 것이므로, &lt;code&gt;&amp;amp; span&lt;/code&gt; / &lt;code&gt;&amp;amp;span&lt;/code&gt; / &lt;code&gt;&amp;amp;.span&lt;/code&gt; 은 각각 다르게 취급됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 아래의 Sass 문이 어떻게 컴파일될지 느낌이 팍 오죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649143872797&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.hello {
  &amp;amp; span {
    color: blue;
  }

  &amp;amp;span {
    color: green;
  }

  &amp;amp;.span {
    color: purple;
  }
}&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;답은 차례대로 ① .hello span ② .hellospan ③ .hello.span 입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.sassmeister.com/&quot;&gt;SassMeister&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에서 쳐보시면 금방 감이 잡힐 거예요  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유의할 점 한 가지! &lt;code&gt;&amp;amp;&lt;/code&gt; 선택자는 복합 선택자의 시작 부분에만 쓸 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;:not(&amp;amp;)&lt;/code&gt; 는 가능하지만 &lt;code&gt;.item&amp;amp;&lt;/code&gt; 처럼 끝에 쓰는 건 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-04-05 at 4.37.55 PM.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXJWpz/btrysp754LM/rCqYXD1vhV9dWLNCvOUf51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXJWpz/btrysp754LM/rCqYXD1vhV9dWLNCvOUf51/img.png&quot; data-alt=&quot;Error: &amp;quot;&amp;amp;amp;&amp;quot; may only used at the beginning of a compound selector&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXJWpz/btrysp754LM/rCqYXD1vhV9dWLNCvOUf51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXJWpz%2Fbtrysp754LM%2FrCqYXD1vhV9dWLNCvOUf51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;142&quot; data-filename=&quot;Screen Shot 2022-04-05 at 4.37.55 PM.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Error: &quot;&amp;amp;&quot; may only used at the beginning of a compound selector&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;또, @media 같은 at-rules 가 중첩되어 있으면 스타일 규칙이 그 안에 있도록 바꿔주기 때문에 쉽게 조건을 추가할 수 있어요.&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;0sass-vs-css copy.png&quot; data-origin-width=&quot;1167&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rnkw2/btryxrryLny/OwaDYJUGjHcKKd6IoUWrAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rnkw2/btryxrryLny/OwaDYJUGjHcKKd6IoUWrAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rnkw2/btryxrryLny/OwaDYJUGjHcKKd6IoUWrAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frnkw2%2FbtryxrryLny%2FOwaDYJUGjHcKKd6IoUWrAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;273&quot; data-filename=&quot;0sass-vs-css copy.png&quot; data-origin-width=&quot;1167&quot; data-origin-height=&quot;531&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdDnkn%2FbtrytjAcVfl%2F8dibLadpTUd9g35iPb29Y1%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 변수: 걔가 얘야&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sass에서는 &lt;code&gt;$변수명: 값;&lt;/code&gt; 으로&amp;nbsp;변수를&amp;nbsp;선언해&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가&amp;nbsp;지난&amp;nbsp;글에서&amp;nbsp;예제로&amp;nbsp;살펴봤던&amp;nbsp;것처럼&amp;nbsp;쓰면&amp;nbsp;됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에는 문자, 숫자, 불린, 컬러값, null 등 정말 다양한 값을 담을 수 있어요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649144409033&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$width: 100rem;
$font: &quot;Noto Sans KR&quot;;
$color: #000;
$darkMode: true;
$init: null;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;TMI&lt;br /&gt;&lt;/b&gt;이렇게 속성이나 변수 선언 오른쪽에 있는 값을 SassScript 라고 부릅니다.&lt;br /&gt;값을 담는 데서 끝이 아니고 != 처럼 비교하거나 +/-/* 등의 산술을 할 수도 있고, 함수의 인자로 넘길 수도 있죠.&lt;br /&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;또한 선언 위치에 따라 전역변수 또는 지역변수로 사용할 수도 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649144476817&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.header {
  $var-header: '이 변수는 .header 내에서만 쓸 수 있어요!';
}

.body {
  content: $var-header; //오류!
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-04-05 at 4.41.29 PM.png&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKmDLC/btrywvUbZaC/7ITCtHuWCRwJVzhUl8vSu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKmDLC/btrywvUbZaC/7ITCtHuWCRwJVzhUl8vSu0/img.png&quot; data-alt=&quot;Error: Undefined variable.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKmDLC/btrywvUbZaC/7ITCtHuWCRwJVzhUl8vSu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKmDLC%2FbtrywvUbZaC%2F7ITCtHuWCRwJVzhUl8vSu0%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;380&quot; height=&quot;149&quot; data-filename=&quot;Screen Shot 2022-04-05 at 4.41.29 PM.png&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Error: Undefined variable.&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;물론 CSS에서도 var(--변수명) 이라고 글로벌 변수를 쓸 수 있어요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Sass의 변수는 CSS 변수와 조금 다릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Sass의 변수는 컴파일되면서 사라집니다. CSS 파일에 보이지 않아요.&lt;/li&gt;
&lt;li&gt;Sass의 변수는 한 번에 단 하나의 값만 가집니다.&lt;/li&gt;
&lt;li&gt;Sass의 변수는 재할당 시 이전의 내용을 바꾸지 않습니다.&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_1649144623400&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$var: 'nana'; 
.first { content:$var; } //nana
$var: 'like'; 
.second { content:$var; } //like&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;또, Sass 변수에서 하이픈(-)과 언더스코어(_)는 동일하게 취급됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 $font-size와 $font_size는 완벽하게 동일합니다!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649144718168&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$font_size: 10px;
$font-size: 200px;

.underscore {
    font-size: $font_size; //200px;
}

.hypen {
    font-size: $font-size; //200px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;TMI&lt;/b&gt;&lt;br /&gt;옛날에는 Sass 변수에 언더스코어만 쓸 수 있었으나 업데이트되면서 하이픈도 지원하게 됐다고 하네요!&lt;br /&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;변수는&amp;nbsp;언제&amp;nbsp;쓰는&amp;nbsp;게&amp;nbsp;좋을까요?&amp;nbsp;그건&amp;nbsp;쓰는&amp;nbsp;사람&amp;nbsp;마음대로!&amp;nbsp;...긴&amp;nbsp;한데요,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수가 난잡하면 그것 나름대로 보기 어려워져서 '쓸 만한 상황이다'라고 판단될 때 쓰는 게 좋겠죠.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값이 여러 곳에서 반복되거나&lt;/li&gt;
&lt;li&gt;수정될 가능성이 높을 때 사용하는 것이 가장 좋겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdDnkn%2FbtrytjAcVfl%2F8dibLadpTUd9g35iPb29Y1%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) 보간: 슬쩍 끼워넣기!&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 &lt;code&gt;font-size: $size;&lt;/code&gt;와&amp;nbsp;같이&amp;nbsp;불러다&amp;nbsp;쓸&amp;nbsp;수&amp;nbsp;있다는&amp;nbsp;걸&amp;nbsp;배웠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면&amp;nbsp;CSS&amp;nbsp;속성값&amp;nbsp;안에&amp;nbsp;그대로&amp;nbsp;끼워넣는&amp;nbsp;것도&amp;nbsp;가능할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649144979038&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$name: &quot;cart&quot;;
.icon-cart {
    background-image: url(&quot;/icons/$name.png&quot;);
}

/*
 출력결과:
 .icon-cart {
   background-image: url(&quot;/icons/$name.png&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;의도한 거랑은 다르게 나왔네요. 저 이미지 주소 내에 $name을 끼워넣고 싶었는데 말이죠.. 머쓱...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니면 이런 것도 가능할까요? position 을 변수로 주고 top: 50px 을 적용하고 싶습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649145045455&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$position: top;
.header {
    position: fixed;
    $position: 50px;
}

/*
 출력결과: 
 .header {
   position: fixed;
 }
 (내 $position 어디 간 거야...  )
 */&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;넵, 문자열이나 셀렉터 등에 변수를 쓰려면 그냥은 안 되고 #{} 라는 보간(Interpolation)이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법은 변수를 &lt;code&gt;#{}&lt;/code&gt; 로 감싸주기만 하면 됩니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649145323257&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$position: top;
$name: cart;
$height: 200px;

.header {
    position: fixed;
    #{$position}: 50px;
    background-image: url(&quot;/icons/#{$name}.png&quot;);
    width: calc(100vh - #{$height});
}

/*
 출력결과:
 .header {
   position: fixed;
   top: 50px; //야호 top을 인식했다!
   background-image: url(&quot;/icons/cart.png&quot;); //야호(2) cart를 인식했다!
   width: calc(100vh - 200px);
 }
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 CSS 변수에서 Sass 변수나 함수를 끌어다쓰고 싶다면 보간법을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649145762110&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$color-accent: #0080ff;

:root {
  --color-accent: #{$color-accent};
  --color-accent-100: #{rgba($color-accent, 0.07)};
  --color-accent-200: #{rgba($color-accent, 0.2)};
  --color-accent-300: #{rgba($color-accent, 0.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;&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;한 가지 짚고 넘어가면 좋은 점! 보간이 리턴하는 것은 따옴표가 없는 문자열(unquoted strings)입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&amp;nbsp;단위&amp;nbsp;사이에&amp;nbsp;끼워넣을&amp;nbsp;때는&amp;nbsp;보간을&amp;nbsp;쓰기보다는&amp;nbsp;아래와&amp;nbsp;같이&amp;nbsp;쓰는&amp;nbsp;걸&amp;nbsp;추천합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649145393208&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$size: 100;

.main {
    width: #{$size}rem; //Not recommend  
}

.footer {
    width: #{$size} * 1rem; //Good  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 변수에 단위가 잘못 들어갔을 때, 컴파일 과정에서 에러를 잡을 수 없기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649145454924&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$size: 100px; //어이쿠! 모르고 px을 써버렸다!

.main {
    width: #{$size}rem;
}

.footer {
    width: #{$size} * 1rem;
}

/*
 컴파일 결과:
 .main { width: 100pxrem; }
 .footer { //에러 }
*/&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;보간은 문자열을 리턴하므로 pxrem이란 요상한 단위가 튀어나오게 됩니다.&lt;br /&gt;컴파일러 입장에선 문자열끼리 잘 있는 것이므로 딱히 에러를 내뱉지 않습니다. (다행히 브라우저는 이 단위를 무시하겠지만요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 100px * 1rem은 명확하게 단위가 맞지 않으니 개발단계에서 에러를 캐치하고 수정하는 것이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또,&amp;nbsp;문자열을&amp;nbsp;감싼&amp;nbsp;따옴표는&amp;nbsp;보간으로&amp;nbsp;사용&amp;nbsp;시&amp;nbsp;컴파일&amp;nbsp;과정에서&amp;nbsp;사라집니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649145830493&quot; class=&quot;scss&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$var: &quot;Nana&quot;;

.hello {
   background-image: url('/images/#{$var}.png');
}

/*
 출력결과:
 .hello {
   background-image: url(&quot;/images/Nana.png&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;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdDnkn%2FbtrytjAcVfl%2F8dibLadpTUd9g35iPb29Y1%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) 인라인 주석: 우리끼리만 보는 주석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS에서&amp;nbsp;주석은 &lt;code&gt;/* */&lt;/code&gt;형태로&amp;nbsp;사용했죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS 파일은 누구나 뜯어볼 수 있기 때문에 비밀스러운(?) 주석을 다는 것이 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발하다보면&amp;nbsp;개발에만&amp;nbsp;필요한&amp;nbsp;주석을&amp;nbsp;달아야할&amp;nbsp;때가&amp;nbsp;있는데&amp;nbsp;말이죠.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런&amp;nbsp;불편함을&amp;nbsp;해소하고자&amp;nbsp;Sass에서는 &lt;code&gt;//&lt;/code&gt;형태로&amp;nbsp;인라인&amp;nbsp;주석을&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인라인&amp;nbsp;주석은&amp;nbsp;어디든&amp;nbsp;끼워넣을&amp;nbsp;수&amp;nbsp;있고,&amp;nbsp;컴파일&amp;nbsp;과정에서&amp;nbsp;뿅&amp;nbsp;사라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0sass-vs-css-3.png&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k041k/btryyfJH3zS/1LkSItk4ixckbOqTssCcO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k041k/btryyfJH3zS/1LkSItk4ixckbOqTssCcO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k041k/btryyfJH3zS/1LkSItk4ixckbOqTssCcO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk041k%2FbtryyfJH3zS%2F1LkSItk4ixckbOqTssCcO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;236&quot; data-filename=&quot;0sass-vs-css-3.png&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;509&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdDnkn%2FbtrytjAcVfl%2F8dibLadpTUd9g35iPb29Y1%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6) 출력스타일: 어떤 스타일로 해드릴까요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dart Sass는 두 가지의 출력 스타일을 제공합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;expanded = 펼쳐주세요! (기본값)&lt;/li&gt;
&lt;li&gt;compressed = 압축해주세요!&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;compressed 옵션을 택하면 빈 여백을 가능한 제거하고 전체 스타일시트를 한 줄에 작성해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번 글에서 플래그로 --watch 옵션을 껴넣었던 것처럼, --style 옵션으로 출력 스타일을 제어할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 sass 명령을 내릴 때 아래와 같이 입력해 보세요.&lt;/p&gt;
&lt;pre id=&quot;code_1649147334841&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sass --style=compressed style.scss style.css&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이렇게 꽉꽉 압축시킨 CSS 파일을 얻을 수 있습니다! 알아서 압축도 해주고 좋네요  &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;0sass-4.png&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHXCBE/btryyEP5Z8u/pKTSWWRMv4x6fDG46Ne9f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHXCBE/btryyEP5Z8u/pKTSWWRMv4x6fDG46Ne9f0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHXCBE/btryyEP5Z8u/pKTSWWRMv4x6fDG46Ne9f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHXCBE%2FbtryyEP5Z8u%2FpKTSWWRMv4x6fDG46Ne9f0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;359&quot; data-filename=&quot;0sass-4.png&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;664&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;compressed 옵션일 때는 일반 CSS 주석 &lt;code&gt;/* 주석 */&lt;/code&gt; 도 제거됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;code&gt;!&lt;/code&gt; 를 붙여 강조하면 압축모드에서도 표시되므로, 중요한 정보는 &lt;code&gt;/*! 중요한 주석 */&lt;/code&gt; 형태로 남길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-04-06 at 9.55.27 AM.png&quot; data-origin-width=&quot;883&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YOOiG/btryxbP1c9Q/sO7rEseyM6Kbf3yOAp7E4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YOOiG/btryxbP1c9Q/sO7rEseyM6Kbf3yOAp7E4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YOOiG/btryxbP1c9Q/sO7rEseyM6Kbf3yOAp7E4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYOOiG%2FbtryxbP1c9Q%2FsO7rEseyM6Kbf3yOAp7E4K%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;400&quot; height=&quot;382&quot; data-filename=&quot;Screen Shot 2022-04-06 at 9.55.27 AM.png&quot; data-origin-width=&quot;883&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdDnkn/btrytjAcVfl/8dibLadpTUd9g35iPb29Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdDnkn%2FbtrytjAcVfl%2F8dibLadpTUd9g35iPb29Y1%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;50&quot; height=&quot;12&quot; data-filename=&quot;img.png&quot; data-origin-width=&quot;50&quot; data-origin-height=&quot;12&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에필로그&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS 작성을 좀 더 편하게 해줄 몇 가지 Sass 문법에 대해 알아봤습니다. 역시 편한 게 최고쥬&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글은 mixin, extend, 모듈화처럼 쪼개서 여기저기 가져다쓰는 법에 대해 써보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글 마무리가 제일 어렵네요... 아무튼 긴 스크롤 함께해주셔서 감사합니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #ff1a1a;&quot;&gt;&lt;b&gt;이 시리즈의 다음 글&lt;br /&gt;&lt;/b&gt;&lt;a href=&quot;https://nykim.work/113&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;3. 더 편하게 CSS 다루기 - mixin, extend, 모듈화(import/use)&lt;/a&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;/div&gt;</description>
      <category>Blog/CSS</category>
      <category>css</category>
      <category>SASS</category>
      <author>나나 (nykim)</author>
      <guid isPermaLink="true">https://anneslab.tistory.com/111</guid>
      <comments>https://anneslab.tistory.com/111#entry111comment</comments>
      <pubDate>Tue, 5 Apr 2022 21:15:35 +0900</pubDate>
    </item>
    <item>
      <title>CRA(create-react-app) 환경에서 절대경로 / 전역 Sass 사용하기</title>
      <link>https://anneslab.tistory.com/110</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-04-05 at 10.25.36 PM.png&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;1150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2G0T4/btryy2p6DPA/BPgIlizCBiqdGZQupFFL51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2G0T4/btryy2p6DPA/BPgIlizCBiqdGZQupFFL51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2G0T4/btryy2p6DPA/BPgIlizCBiqdGZQupFFL51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2G0T4%2Fbtryy2p6DPA%2FBPgIlizCBiqdGZQupFFL51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;353&quot; data-filename=&quot;Screen Shot 2022-04-05 at 10.25.36 PM.png&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;1150&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;CRA&lt;/b&gt;&lt;br /&gt;- 리액트 프로젝트를 뚝딱 시작할 수 있게 돕는 보일러 플레이트* (*자주 쓰는 수식, 명령어 등을 미리 입력해 놓는 것) &lt;br /&gt;- 그래서 번거롭게 Babel, ESLint, Webpack 등을 따로 설치할 필요가 없다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CRA(create-react-app)에서 config 건드리기&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;절대경로 설정(alias)을 하려면 Webpack의 config를 건드려야 하지만, CRA로 생성하면 기본적으로 config 파일을 볼 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eject 하면 숨겨져 있던 config 파일을 밖으로 꺼낼 수 있지만, 이거 한 번 하면 못 돌리는 옵션인데.... 웹팩이랑 안 친한데....  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 땐 CRACO(Create React App Configuration Override) 를 사용하자! eject 없이 설정을 건드릴 수 있다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1647407289485&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ yarn add @craco/craco
$ yarn add -D craco-sass-resources-loader&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;설치한 후 config 파일들을 슥슥 만져준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. craco가 명령어를 실행하도록 바꾸기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;json&quot; data-ke-language=&quot;json&quot;&gt;&lt;code&gt;/* package.json 수정 */

{
	...
	&quot;scripts&quot;: {
		&quot;start&quot;: &quot;craco start&quot;,
		&quot;build&quot;: &quot;craco build&quot;,
		&quot;test&quot;: &quot;craco test&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;&lt;b&gt;2. craco.config.js 파일 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 타입스크립트 환경이여서 plugins.options.source 를 tsconfig 로 설정했다. (자바스크립트 환경이라면 jsconfig)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;/* craco.config.js 생성 */

const CracoAlias = require('craco-alias');

module.exports = {
  plugins: [
    {
      plugin: CracoAlias,
      options: {
        source: 'tsconfig',
        tsConfigPath: 'tsconfig.paths.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;&lt;b&gt;3.&amp;nbsp; tsconfig.paths.json 생성 후 경로 설정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 별도 파일로 분리하지 않고 config.js 내에 직접 입력해도 무관!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;/* tsconfig.paths.json 생성 */

{
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;.&quot;,
    &quot;paths&quot;: {
      &quot;@/*&quot;: [&quot;src/*&quot;]
      // 또는 &quot;@components/*&quot;: [&quot;src/components/*&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;&lt;b&gt;4. tsconfig.json 수정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;background-color: #ffffff; color: #232629;&quot;&gt;컴파일하는 데 필요한 루트 파일과 컴파일러 옵션을 지정하는 파일&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - &lt;code&gt;extends&lt;/code&gt;: &lt;span style=&quot;background-color: #ffffff;&quot;&gt;다른 파일의 설정을 상속&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp; - &lt;code&gt;include&lt;/code&gt;: 컴파일 할때 포함할 폴더나 파일&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;json&quot; data-ke-language=&quot;json&quot;&gt;&lt;code&gt;/* tsconfig.json 수정 */

{
  &quot;extends&quot;: &quot;./tsconfig.paths.json&quot;,
  &quot;compilerOptions&quot;: {
    //...
  },
  &quot;include&quot;: [&quot;src&quot;, &quot;tsconfig.paths.json&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;이렇게 하면 @/components/button 또는 @/styles/_mixin.scss 와 같이 불러와 쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 craco.config.js 에서 글로벌로 적용할 스타일을 @/styles/로 지정해 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1647409263758&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const CracoAlias = require('craco-alias');

module.exports = {
  style: {
    sass: {
      loaderOptions: {
        additionalData: `
          @import &quot;@/styles/_variable.scss&quot;;
          @import &quot;@/styles/_mixin.scss&quot;;
        `
      }
    }
  },
  plugins: [
    {
      plugin: CracoAlias,
      options: {
        source: 'tsconfig',
        tsConfigPath: 'tsconfig.paths.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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;덧1) CRA 기반 Storybook에서 전역 변수 이해하게 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1496803.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y8pmF/btrv8Ey9oKp/w5509cO06MK7VWlts0z5o1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y8pmF/btrv8Ey9oKp/w5509cO06MK7VWlts0z5o1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y8pmF/btrv8Ey9oKp/w5509cO06MK7VWlts0z5o1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy8pmF%2Fbtrv8Ey9oKp%2Fw5509cO06MK7VWlts0z5o1%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;40&quot; height=&quot;40&quot; data-filename=&quot;1496803.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와... 어마무시한 삽질!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게만 했더니 스토리북에서는 전역 변수를 이해하지 못해서 여러 방면으로 타일러보다가 (왜 이해하질 못하니ㅠㅠ!!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스토리북의 main.js 에서 sass-loader 에 additionalData 넣는 걸로 해결.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1647420671492&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* .storybook/main.js */

module.exports = {
  //...
  webpackFinal: async (config, { configType }) =&amp;gt; {
    config.module.rules.push({
      test: /\.scss$/,
      use: [
        {
          loader: 'sass-loader',
          options: {
            additionalData: `
            @import &quot;./src/styles/_variable.scss&quot;;
            @import &quot;./src/styles/_mixin.scss&quot;;
		  `	
          }
        }
      ]
    });
    return config;
  }
};&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;이유는 모르겠으나 sass-loader 외에 style-loader 나 css-loader 도 같이 지정하게 되면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;import API from &quot;!../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js&quot;;&quot; 에러가 붐붐쳌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 기본 설정이랑 어딘가에서 충돌이 나는 것 같지만 지금으로선 알 수 없다...  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/storybookjs/storybook/issues/6743#issue-441623299&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(참고)&lt;/a&gt; &lt;a href=&quot;https://github.com/storybookjs/presets/issues/191&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(참고)&lt;/a&gt; &lt;a href=&quot;https://github.com/storybookjs/storybook/issues/11101&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(참고)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;덧2) Vite 환경에서 설정하기&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;CRACO 없이 tsconfig.json, vite.config.js 파일을 설정해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굳이 paths.json 으로 빼지 않고 tsconfig.json 에 경로를 입력해주고, vite.config.ts에 alias를 지정해 준 모습 &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;json&quot; data-ke-language=&quot;json&quot;&gt;&lt;code&gt;/* tsconfig.json */

{
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;.&quot;,
    &quot;paths&quot;: {
      &quot;@/*&quot;: [&quot;src/*&quot;]
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;/* vite.config.ts */

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import * as path from 'path';

// &amp;lt;https://vitejs.dev/config/&amp;gt;
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')
    }
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `
          @import '@/styles/_variable.scss';
          @import '@/styles/_mixin.scss';
        `,
      },
    },
  },
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마무리는 역시...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;sticker.png&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG6p4I/btrv8b4Gs3x/GjaZvzSTD6HyONQ25akzz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG6p4I/btrv8b4Gs3x/GjaZvzSTD6HyONQ25akzz1/img.png&quot; data-alt=&quot;야..얍! Ⓒ LINE+&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG6p4I/btrv8b4Gs3x/GjaZvzSTD6HyONQ25akzz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG6p4I%2Fbtrv8b4Gs3x%2FGjaZvzSTD6HyONQ25akzz1%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;250&quot; height=&quot;207&quot; data-filename=&quot;sticker.png&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;야..얍! Ⓒ LINE+&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category> /JS</category>
      <author>나나 (nykim)</author>
      <guid isPermaLink="true">https://anneslab.tistory.com/110</guid>
      <comments>https://anneslab.tistory.com/110#entry110comment</comments>
      <pubDate>Wed, 16 Mar 2022 15:03:50 +0900</pubDate>
    </item>
    <item>
      <title>[책 제본하기] 나만의 책 만들기 (2021 ver.)</title>
      <link>https://anneslab.tistory.com/109</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cover.jpg&quot; data-origin-width=&quot;3588&quot; data-origin-height=&quot;2245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J1RZg/btrvCmY3R4i/MV9ygklPqH3nsQKBl86Xl0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J1RZg/btrvCmY3R4i/MV9ygklPqH3nsQKBl86Xl0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J1RZg/btrvCmY3R4i/MV9ygklPqH3nsQKBl86Xl0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ1RZg%2FbtrvCmY3R4i%2FMV9ygklPqH3nsQKBl86Xl0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;850&quot; height=&quot;532&quot; data-filename=&quot;cover.jpg&quot; data-origin-width=&quot;3588&quot; data-origin-height=&quot;2245&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프롤로그&lt;/h2&gt;
&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://nykim.work/98&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1편을 작년에 쓰고 2편을 올해 쓰다니...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제로 책 제본을 진행하면서 어떤 식으로 했는지 정리해봤습니다. &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인제본 하실 때 참고하세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 책으로 만들 글을 한데로 묶습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 노션에 적은 일기를 모아서 하나의 텍스트 파일로 취합했어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 사진이나 그림 같은 요소를 얼마나 넣을지, 여백은 얼마나 넓게 잡을지에 따라 분량이 달라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;photo.jpg&quot; data-origin-width=&quot;2618&quot; data-origin-height=&quot;3380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eblJXZ/btrvxjvpqO1/SDKZFrF9CPukRLiKNob95k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eblJXZ/btrvxjvpqO1/SDKZFrF9CPukRLiKNob95k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eblJXZ/btrvxjvpqO1/SDKZFrF9CPukRLiKNob95k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeblJXZ%2FbtrvxjvpqO1%2FSDKZFrF9CPukRLiKNob95k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;440&quot; height=&quot;568&quot; data-filename=&quot;photo.jpg&quot; data-origin-width=&quot;2618&quot; data-origin-height=&quot;3380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;2020년 버전: &lt;b&gt;184,034자&lt;/b&gt; (총 220p / 두께 약 1.8cm)&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;2021년 버전: &lt;b&gt;114,910자&lt;/b&gt; (총 214p / 두께 약 1.8cm)&lt;/p&gt;
&lt;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;보면 글자 수는 다르지만 두께가 비슷한데, 2021년 버전에는 사진과 여백이 더 많이 들어가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 두꺼운 책을 원하면 책 크기를 줄이거나 여백을 많이 넣으시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소 글감을 한곳에 모아두지 않았다면 모으는 것도 일입니다 어후ㅋㅋㅋㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 일기라서 작년에 내가 이런 걸 했다고?!! 하면서 돌이켜보는 재밌는 시간이었어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참... 많이 피곤한 한 해였죠 ಥ_ಥ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제본처마다 파일을 요청하는 방식이 다를 거라 작업 전 어디에 의뢰를 맡길 건지 찾아봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 개인의 취향과 예산에 따라 설정!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 하드커버가 &amp;ldquo;넘나&amp;rdquo; 취향이라 소량 하드커버 제본을 해주는 &lt;a href=&quot;https://booktory.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;북토리&lt;/a&gt;로 결정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;맞춤인쇄&amp;rsquo; 서비스에서 선택 후 파일 업로드만 하면 돼서 편하고, 컨펌 과정도 있어서 좋았어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨펌 과정에서 예상 결과물이 달라서 문의드렸더니 유선 연락 주셔서 뚝딱 해결했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-20 at 12.29.10 PM.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;1464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfN95L/btrvyk1SoKb/sMV0VsTdperRiby3vMkweK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfN95L/btrvyk1SoKb/sMV0VsTdperRiby3vMkweK/img.png&quot; data-alt=&quot;(광고 네버에버 아님!!! 하지만 관계자분들의 연락 기다리겠습니다 후후  )&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfN95L/btrvyk1SoKb/sMV0VsTdperRiby3vMkweK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfN95L%2Fbtrvyk1SoKb%2FsMV0VsTdperRiby3vMkweK%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;440&quot; height=&quot;507&quot; data-filename=&quot;Screen Shot 2022-02-20 at 12.29.10 PM.png&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;1464&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(광고 네버에버 아님!!! 하지만 관계자분들의 연락 기다리겠습니다 후후  )&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; 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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 책 설정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 형태의 책을 만들 것인가! 요구사항 정의 타임!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전 처음 만들 때 이런 걸 설정 안 해놔서ㅋㅋㅋㅋ나중에 엎고 수정 또 수정...하게 되더라고요ㅠㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 좋은 건 &lt;b&gt;가지고 있는 책 중에서 골라서 그와 비슷하게 설정&lt;/b&gt;하는 방법이 제일 좋아요!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집에 프린터기 있으면 인쇄해서 잘라서 책에 꽂아보면 대충 이런 느낌이겠구나 하고 짐작해 볼 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 아래와 같이 설정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;크기&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;128 &amp;times; 182, 일본 문고판(B6) 사이즈
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A5보다 작아서 가방에 컴팩트하게 들어가요&lt;/li&gt;
&lt;li&gt;집에 있던 책 중에서 이 사이즈가 제일 괜찮은 것 같아 결정!&lt;/li&gt;
&lt;li&gt;하드커버 제작 시 커버로 인해 이보다 조금 크게 나옵니다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;종이 크기 참고: &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%A2%85%EC%9D%B4_%ED%81%AC%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;위키피디아&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;형태&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하드커버
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하드커버는 최소 두께가 필요해요! &lt;br /&gt;제본처마다 다를 수 있으나, 대체로 너무 얇은 경우 하드커버 제작이 되지 않는다고 합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;북자켓(책을 덮는 종이 커버) 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내지 구성&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;총 4챕터 구성 (인덱스 페이지 및 챕터 표지 존재)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고할 책이 있으면 그에 따라 구성해 봅니다. &lt;br /&gt;글머리(들어가기) 페이지 추가, 하단에 넘버링과 챕터 제목을 병기 등등...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;뉴플러스 미색 100g
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백색은 사진이 잘 나오고, 미색은 살짝 누래서 읽기 편하다고 해요.&lt;/li&gt;
&lt;li&gt;뉴플러스, 모조지 둘 다 해봤는데 개인적으론 모조지가 더 취향이었어요.&lt;br /&gt;(뉴플러스는 가볍고 매끈한 느낌, 모조지는 부드럽고 차분한 느낌)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;여백: 상/좌/우 15mm + 하 22mm
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;짝수 페이지는 우측 여백 20mm / 홀수 페이지는 좌측 여백 20mm
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저는 펼치기 어려운 점을 고려해서 안쪽 여백을 더 크게 줬습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하단에는 페이지 넘버링 표기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;글자
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;크기: 영문 10.5pt / 일문 9.5pt&lt;/li&gt;
&lt;li&gt;서체: 영문 Minion Pro(본문), Southampton(타이틀) / 일문 Noto Serif JP&lt;/li&gt;
&lt;li&gt;프린터할 수 있으면 직접 인쇄해서 참고용 책에 껴보는 게  제일 좋아요!!!!
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저도 처음에 9pt로 잡았다가 실제로 인쇄해보니 좀 더 큰 게 좋을 것 같아서 .5pt 업했습니다.&lt;/li&gt;
&lt;/ul&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;copy.png&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;1348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mDdhm/btrvxjIYp3G/286ekNvnSPaF5shNHwuNJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mDdhm/btrvxjIYp3G/286ekNvnSPaF5shNHwuNJ1/img.png&quot; data-alt=&quot;대충 이런 모습으로 나옵니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mDdhm/btrvxjIYp3G/286ekNvnSPaF5shNHwuNJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmDdhm%2FbtrvxjIYp3G%2F286ekNvnSPaF5shNHwuNJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;577&quot; data-filename=&quot;copy.png&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;1348&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;blockquote data-ke-style=&quot;style2&quot;&gt;(+) 23.09.27 추가&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;1페이지는 책을 펼쳤을 때 &quot;우측&quot;에 해당합니다!&amp;nbsp; &lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;따라서 마지막 페이지는 책을 펼쳤을 때 &quot;좌측&quot;이 됩니다  &lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;&lt;br /&gt;PDF로 변환 후 두 쪽씩 보기 했을 때 처음에 1페이지만 보이고, 나머지는 2페이지씩 보이는지 확인하시면 돼요.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2023-09-27 at 9.11.15 PM.png&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FgsOJ/btsv7mrAasD/fTdGgk5JK2ox2uQGkcrDX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FgsOJ/btsv7mrAasD/fTdGgk5JK2ox2uQGkcrDX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FgsOJ/btsv7mrAasD/fTdGgk5JK2ox2uQGkcrDX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFgsOJ%2Fbtsv7mrAasD%2FfTdGgk5JK2ox2uQGkcrDX0%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;200&quot; height=&quot;228&quot; data-filename=&quot;Screenshot 2023-09-27 at 9.11.15 PM.png&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;제가 만들었던 책의 처음 부분을 참고용으로 첨부합니다  &lt;/span&gt;&lt;br /&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;page-example.png&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;4558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zHMri/btsv8gYO81U/xDrI1ivXNDsnRWjEN0c49k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zHMri/btsv8gYO81U/xDrI1ivXNDsnRWjEN0c49k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zHMri/btsv8gYO81U/xDrI1ivXNDsnRWjEN0c49k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzHMri%2Fbtsv8gYO81U%2FxDrI1ivXNDsnRWjEN0c49k%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;400&quot; height=&quot;827&quot; data-filename=&quot;page-example.png&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;4558&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;4. 표지 디자인하기 (하드커버)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 재밌지만 제일 어려운 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 책! 하면 그냥 앞표지만 떠올리는데...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표지라고 해서 앞에만 딱 만들면 끝이 아니더라고요....  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞면, 뒷면, 책등, 책날개, 여백 등 모두 이어지는 부분이기 때문에 &lt;b&gt;넓은 캔버스에서 작업한다&lt;/b&gt;고 생각해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.04.19 PM.png&quot; data-origin-width=&quot;2438&quot; data-origin-height=&quot;1126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDZZNW/btrvygY78ly/tG8MaWwdHCMBg8HJbRobxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDZZNW/btrvygY78ly/tG8MaWwdHCMBg8HJbRobxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDZZNW/btrvygY78ly/tG8MaWwdHCMBg8HJbRobxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDZZNW%2FbtrvygY78ly%2FtG8MaWwdHCMBg8HJbRobxK%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;840&quot; height=&quot;388&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.04.19 PM.png&quot; data-origin-width=&quot;2438&quot; data-origin-height=&quot;1126&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 전체적으로 잡아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히, 하드커버의 표지는 접어서 붙이는 방식이기 때문에 표지의&lt;b&gt; 여백이 더 커야 해요.&lt;/b&gt; (여분 20mm)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 바탕색이 있다면 &lt;b&gt;여분에도 바탕색&lt;/b&gt;을 넣어야 합니다. 이미지나 배경색이 있다면 사방 여백까지 넣어주세요!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제본처에서 제공하는 안내사항이 있다면 그에 맞춰 작업합니다. (출처: 북토리)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;북토리1.png&quot; data-origin-width=&quot;1728&quot; data-origin-height=&quot;1130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AdgWS/btrvBlshpcK/RfsJ5UtGZk0dG3Bn1KP4Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AdgWS/btrvBlshpcK/RfsJ5UtGZk0dG3Bn1KP4Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AdgWS/btrvBlshpcK/RfsJ5UtGZk0dG3Bn1KP4Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAdgWS%2FbtrvBlshpcK%2FRfsJ5UtGZk0dG3Bn1KP4Jk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;392&quot; data-filename=&quot;북토리1.png&quot; data-origin-width=&quot;1728&quot; data-origin-height=&quot;1130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;북토리2.png&quot; data-origin-width=&quot;1790&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F5HIz/btrvymlap2t/Whq1TyJJde36pkgES02Jj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F5HIz/btrvymlap2t/Whq1TyJJde36pkgES02Jj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F5HIz/btrvymlap2t/Whq1TyJJde36pkgES02Jj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF5HIz%2Fbtrvymlap2t%2FWhq1TyJJde36pkgES02Jj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;429&quot; data-filename=&quot;북토리2.png&quot; data-origin-width=&quot;1790&quot; data-origin-height=&quot;1280&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-filename=&quot;북토리3.png&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqUDuy/btrvvbSp2wC/K9oppwHphtaEhr2yPKlsk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqUDuy/btrvvbSp2wC/K9oppwHphtaEhr2yPKlsk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqUDuy/btrvvbSp2wC/K9oppwHphtaEhr2yPKlsk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqUDuy%2FbtrvvbSp2wC%2FK9oppwHphtaEhr2yPKlsk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;395&quot; data-filename=&quot;북토리3.png&quot; data-origin-width=&quot;1808&quot; data-origin-height=&quot;1190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 여백까지 고려해 크기를 계산한 다음 그에 맞춰 가이드라인을 그립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;Screen Shot 2022-02-20 at 1.01.02 PM.png&quot; data-origin-width=&quot;2382&quot; data-origin-height=&quot;1380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byqZZA/btrvBleJZUt/KDTcJWAc2IPEjC2OHcJT81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byqZZA/btrvBleJZUt/KDTcJWAc2IPEjC2OHcJT81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byqZZA/btrvBleJZUt/KDTcJWAc2IPEjC2OHcJT81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyqZZA%2FbtrvBleJZUt%2FKDTcJWAc2IPEjC2OHcJT81%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;840&quot; height=&quot;487&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.01.02 PM.png&quot; data-origin-width=&quot;2382&quot; data-origin-height=&quot;1380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.02.15 PM.png&quot; data-origin-width=&quot;2328&quot; data-origin-height=&quot;1170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baQKrg/btrvAaxMh2W/gJ3m3FBw77sNEVy5KkThvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baQKrg/btrvAaxMh2W/gJ3m3FBw77sNEVy5KkThvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baQKrg/btrvAaxMh2W/gJ3m3FBw77sNEVy5KkThvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaQKrg%2FbtrvAaxMh2W%2FgJ3m3FBw77sNEVy5KkThvk%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;840&quot; height=&quot;422&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.02.15 PM.png&quot; data-origin-width=&quot;2328&quot; data-origin-height=&quot;1170&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의할 점!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 하드커버에는 인조(책등 옆에 오목하게 파이는 부분)가 있습니다. 가운데 정렬시 &lt;b&gt;인조를 포함한 범위에서 정렬&lt;/b&gt;해 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 책등 계산이 제일 어려운데 공식은 이렇습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;- (총 페이지수 + 앞뒤 면지 추가 수 * 4) / 2 * (1장 당 용지두께) + 0.5, (하드커버일 시 +5)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;- 참고로 &lt;b&gt;북토리에선 책등을 자동으로 계산&lt;/b&gt;해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 책커버나 북자켓 작업 시, &lt;b&gt;날개가 접히는 부분을 고려한 여백(5mm)&lt;/b&gt;이 필요합니다.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;- 이때 여백에는 앞표지의 내용이 들어가야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추가로, 어도비 일러스트레이터로 작업할 경우 아래 사항도 참고하세요.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;1) &lt;b&gt;CMYK&lt;/b&gt;로 작업 중인지 확인하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 사용된 글자는 &lt;b&gt;아웃라인&lt;/b&gt;을 만들어 저장하고, 필터 등은&lt;b&gt; 래스터화&lt;/b&gt;하기&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;- 글자 깨기 전에 오타 꼭 확인하세요ㅠㅠㅋㅋㅋㅋㅋㅋㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-19 at 4.21.58 PM.png&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M8ctV/btrvAbDrYtL/MK12fwGURnCNK2SLwd7W3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M8ctV/btrvAbDrYtL/MK12fwGURnCNK2SLwd7W3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M8ctV/btrvAbDrYtL/MK12fwGURnCNK2SLwd7W3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM8ctV%2FbtrvAbDrYtL%2FMK12fwGURnCNK2SLwd7W3k%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;296&quot; height=&quot;432&quot; data-filename=&quot;Screen Shot 2022-02-19 at 4.21.58 PM.png&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) PDF 저장 시에는 &lt;b&gt;&amp;lsquo;고화질 인쇄용&amp;rsquo;&lt;/b&gt;을 선택해야 사진이 깨지지 않아요!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-19 at 4.19.05 PM.png&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRrxZZ/btrvwJAvlYF/fZNPp1RWo5zTs9mh7wf4Vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRrxZZ/btrvwJAvlYF/fZNPp1RWo5zTs9mh7wf4Vk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRrxZZ/btrvwJAvlYF/fZNPp1RWo5zTs9mh7wf4Vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRrxZZ%2FbtrvwJAvlYF%2FfZNPp1RWo5zTs9mh7wf4Vk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;713&quot; height=&quot;167&quot; data-filename=&quot;Screen Shot 2022-02-19 at 4.19.05 PM.png&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;167&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 내지 작업하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 어도비 인디자인을 썼습니다. 왜냐면 비싼 플랜 쓰는데... 이걸로 뽕이라도 뽑아야....  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 점은 포토샵/일러 - 인디자인을 자유롭게 왕복하며 사진 보정이 가능하다는 거?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어도비는 &lt;b&gt;7일 무료 체험기간&lt;/b&gt;을 제공하고 있으니 이때 몰아서 작업하시는 걸 추천드려요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-15 at 1.36.49 PM.png&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpZHL9/btrvrzFEseZ/dFiZ21yDuutHBzjLviFpl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpZHL9/btrvrzFEseZ/dFiZ21yDuutHBzjLviFpl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpZHL9/btrvrzFEseZ/dFiZ21yDuutHBzjLviFpl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpZHL9%2FbtrvrzFEseZ%2FdFiZ21yDuutHBzjLviFpl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;176&quot; data-filename=&quot;Screen Shot 2022-02-15 at 1.36.49 PM.png&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;508&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;아니면 굳이 이런 툴이 아닌 한글(hwp) 등의 문서 편집기여도 상관은 없어요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(제 &lt;a href=&quot;https://blog.naver.com/phlox__/220286536918&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;첫번째 책&lt;/a&gt;도 hwp로 만들어졌음다)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이하는 몇 가지 작업 팁!!!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;인디자인 문서 생성&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-15 at 2.38.21 PM.png&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;821&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djPxV0/btrvtBKP24c/5B3PqvMvwUYuYavvvtS4aK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djPxV0/btrvtBKP24c/5B3PqvMvwUYuYavvvtS4aK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djPxV0/btrvtBKP24c/5B3PqvMvwUYuYavvvtS4aK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjPxV0%2FbtrvtBKP24c%2F5B3PqvMvwUYuYavvvtS4aK%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;150&quot; height=&quot;425&quot; data-filename=&quot;Screen Shot 2022-02-15 at 2.38.21 PM.png&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;821&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;- 단위: mm&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Facing Pages(마주보기) 옵션 체크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Primary Text Frame 옵션 체크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Bleed(제본 여백) 사방 3mm씩&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;h4 data-ke-size=&quot;size20&quot;&gt;마스터 편집&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Pages 탭에서 A-Parent를 더블 클릭해서 마스터 편집 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.32.27 PM.png&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOLz9N/btrvtAZvHUz/syOYKMZn3fTMyhCLtL8Ka1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOLz9N/btrvtAZvHUz/syOYKMZn3fTMyhCLtL8Ka1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOLz9N/btrvtAZvHUz/syOYKMZn3fTMyhCLtL8Ka1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOLz9N%2FbtrvtAZvHUz%2FsyOYKMZn3fTMyhCLtL8Ka1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;125&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.32.27 PM.png&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;216&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;- 하단에 페이지 번호를 표시할 텍스트 상자 영역을 그리고 (=T 선택 후 드래그) Current Page Number 를 삽입&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.33.29 PM.png&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz5ZuX/btrvoPPPT8B/chdhFeRbpH4Bvh5lzVnwh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz5ZuX/btrvoPPPT8B/chdhFeRbpH4Bvh5lzVnwh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz5ZuX/btrvoPPPT8B/chdhFeRbpH4Bvh5lzVnwh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz5ZuX%2FbtrvoPPPT8B%2FchdhFeRbpH4Bvh5lzVnwh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;468&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.33.29 PM.png&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 페이지 번호를 특정 숫자부터 시작 (ex. 인덱스 포함해서 계산) 하고 싶은 경우, 특정 페이지 우클릭 - 옵션 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.31.56 PM.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvHQy5/btrvAV8eJsI/ozwSH4ZhAOxqdKS0kObdk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvHQy5/btrvAV8eJsI/ozwSH4ZhAOxqdKS0kObdk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvHQy5/btrvAV8eJsI/ozwSH4ZhAOxqdKS0kObdk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvHQy5%2FbtrvAV8eJsI%2FozwSH4ZhAOxqdKS0kObdk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;259&quot; data-filename=&quot;Screen Shot 2022-02-20 at 1.31.56 PM.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;568&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;- 텍스트 중앙 정렬하려면 Cmd+B 후 Align 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNHHX0/btrvymrT2jt/tRZpXscUoNKfkosNwhenlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNHHX0/btrvymrT2jt/tRZpXscUoNKfkosNwhenlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNHHX0/btrvymrT2jt/tRZpXscUoNKfkosNwhenlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNHHX0%2FbtrvymrT2jt%2FtRZpXscUoNKfkosNwhenlk%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;360&quot; height=&quot;88&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;204&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;- 오버라이드로 특정 페이지에는 마스터 스타일을 적용하지 않을 수 있음 (ex. 페이지 넘버링 제거)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyI5IU/btrvuQudIrH/pHnnowX5lkaBfuMlkncSmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyI5IU/btrvuQudIrH/pHnnowX5lkaBfuMlkncSmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyI5IU/btrvuQudIrH/pHnnowX5lkaBfuMlkncSmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyI5IU%2FbtrvuQudIrH%2FpHnnowX5lkaBfuMlkncSmK%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;320&quot; height=&quot;198&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;488&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;h4 data-ke-size=&quot;size20&quot;&gt;글 편집하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- File - Place로 취합했던 txt 파일을 불러오기 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Type - Show Hidden Chracters로 줄바꿈 부호 표시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;1558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bySlGT/btrvrBwHAvb/6zxnK0L2rS4EUpkNKW4ud0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bySlGT/btrvrBwHAvb/6zxnK0L2rS4EUpkNKW4ud0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bySlGT/btrvrBwHAvb/6zxnK0L2rS4EUpkNKW4ud0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbySlGT%2FbtrvrBwHAvb%2F6zxnK0L2rS4EUpkNKW4ud0%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;200&quot; height=&quot;1558&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;1558&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;- Ctrl+F로 불필요한 문단 삭제하기 (^p^p &amp;rarr; ^p)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dsw6I/btrvt3fmUT5/AHpo3Qm8s85h6J8aNXMKf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dsw6I/btrvt3fmUT5/AHpo3Qm8s85h6J8aNXMKf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dsw6I/btrvt3fmUT5/AHpo3Qm8s85h6J8aNXMKf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDsw6I%2Fbtrvt3fmUT5%2FAHpo3Qm8s85h6J8aNXMKf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;644&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 공통 서식(ex. 날짜 / 타이틀)은 &amp;lsquo;단락 스타일'로 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cINp1M/btrvu2AxabC/ibSC3XYyddfsOgIqF4YSQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cINp1M/btrvu2AxabC/ibSC3XYyddfsOgIqF4YSQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cINp1M/btrvu2AxabC/ibSC3XYyddfsOgIqF4YSQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcINp1M%2Fbtrvu2AxabC%2FibSC3XYyddfsOgIqF4YSQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;652&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;652&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;- 단락 스타일 변경을 단축키로 지정하면 편함!! (Ctrl + 숫자 또는 Alt + 숫자 또는 Ctrl+Alt+숫자)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sHGfQ/btrvwJAvGy0/9QJDcAztul7pFJiE8A6yik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sHGfQ/btrvwJAvGy0/9QJDcAztul7pFJiE8A6yik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sHGfQ/btrvwJAvGy0/9QJDcAztul7pFJiE8A6yik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsHGfQ%2FbtrvwJAvGy0%2F9QJDcAztul7pFJiE8A6yik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;508&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;508&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;- 이 단축키는 Paragraph Style 창이 켜 있어야 먹는 듯?!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bue3JN/btrvvvibUI5/Jiei1AdxWTUKu9ApPGK1ek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bue3JN/btrvvvibUI5/Jiei1AdxWTUKu9ApPGK1ek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bue3JN/btrvvvibUI5/Jiei1AdxWTUKu9ApPGK1ek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbue3JN%2FbtrvvvibUI5%2FJiei1AdxWTUKu9ApPGK1ek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;216&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;524&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 또는 문서에 적용한 스타일로 단락 스타일 재정의 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAmFqR/btrvtALYbZN/Kl5huxac5sU1oHdLhfHqVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAmFqR/btrvtALYbZN/Kl5huxac5sU1oHdLhfHqVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAmFqR/btrvtALYbZN/Kl5huxac5sU1oHdLhfHqVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAmFqR%2FbtrvtALYbZN%2FKl5huxac5sU1oHdLhfHqVk%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;440&quot; height=&quot;230&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;652&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;- Keep Options에서 Start Paragraph를 Next Page나 Next Odd Page로 설정하면 해당 서식이 적용된 단락은 무조건 새 페이지(새 홀수 페이지)에서 시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blA7TP/btrvymlb0D5/bruTuFE0NGibSte5HgnfM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blA7TP/btrvymlb0D5/bruTuFE0NGibSte5HgnfM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blA7TP/btrvymlb0D5/bruTuFE0NGibSte5HgnfM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblA7TP%2Fbtrvymlb0D5%2FbruTuFE0NGibSte5HgnfM0%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;440&quot; height=&quot;498&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;498&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;- 미리보기(단축키 W)를 누르면 실제 인쇄될 화면을 보면서 작업할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.gif&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ovVGr/btrvtAkV5lp/VqIeIb3D0eZbEDBpLNoaL1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ovVGr/btrvtAkV5lp/VqIeIb3D0eZbEDBpLNoaL1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ovVGr/btrvtAkV5lp/VqIeIb3D0eZbEDBpLNoaL1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ovVGr/btrvtAkV5lp/VqIeIb3D0eZbEDBpLNoaL1/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;440&quot; height=&quot;512&quot; data-filename=&quot;10.gif&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;512&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;h4 data-ke-size=&quot;size20&quot;&gt;사진 불러오기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 인디자인에서는 실제 이미지를 가져오는 게 아니라 사진을 가상으로 불러오는 방식이므로, 별도 이미지 폴더를 두고 거기서 불러와 작업하는 방식으로 진행 (폴더 삭제하지 않도록 주의)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 사각형 프레임도구로 이미지가 들어올 영역(프레임)을 만든 다음, 파일 - 가져오기(단축키 Ctrl+D) 또는 드래그 앤 드롭으로 가져오기!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 외부 요소 선택(단축키 V) vs. 내부 요소 선택(단축키 A)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- 내부 요소 선택 후 사진을 움직이면 프레임 내 사진의 위치를 조절&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 작업 성능을 높이려면 Display Performance에서 이미지 화질 변경 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf8jaz/btrvvaTyyw9/5DvG76iLEx15kCOupFvuD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf8jaz/btrvvaTyyw9/5DvG76iLEx15kCOupFvuD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf8jaz/btrvvaTyyw9/5DvG76iLEx15kCOupFvuD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf8jaz%2FbtrvvaTyyw9%2F5DvG76iLEx15kCOupFvuD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;420&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;563&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사진 우클릭 후 Edit with... 로 포토샵에서 보정 가능 (이미지를 불러오는 형식이므로 포토샵에서 저장시 바로 반영)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/84VSr/btrvtASHCuX/dEubAH6fGa8brP0vqnSoz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/84VSr/btrvtASHCuX/dEubAH6fGa8brP0vqnSoz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/84VSr/btrvtASHCuX/dEubAH6fGa8brP0vqnSoz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F84VSr%2FbtrvtASHCuX%2FdEubAH6fGa8brP0vqnSoz1%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;400&quot; height=&quot;480&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사진 상단 우측의 앵커 포인트를 글자에 가져다 끌면 고정 개체로 변경 (해당 텍스트 위치가 변경되면 사진도 함께 따라감)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.gif&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/84xbh/btrvvC3mb19/UeSDGaM1Kk7aE5kkcbZ0bk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/84xbh/btrvvC3mb19/UeSDGaM1Kk7aE5kkcbZ0bk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/84xbh/btrvvC3mb19/UeSDGaM1Kk7aE5kkcbZ0bk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/84xbh/btrvvC3mb19/UeSDGaM1Kk7aE5kkcbZ0bk/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;400&quot; height=&quot;376&quot; data-filename=&quot;13.gif&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;376&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 표지 파일과 본문 파일을 고이 모시고 제본처에 의뢰합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 제가 선택한 옵션은 이렇습니다  &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;1096&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDoMOq/btrvy4kgPt8/DA3xlJkOUC35ENbXZzidYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDoMOq/btrvy4kgPt8/DA3xlJkOUC35ENbXZzidYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDoMOq/btrvy4kgPt8/DA3xlJkOUC35ENbXZzidYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDoMOq%2Fbtrvy4kgPt8%2FDA3xlJkOUC35ENbXZzidYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;815&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;1096&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;며칠 손꼽아 기다리면... 예쁘게 도착합니다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;저는 제작지연으로 인해 배송포함 2주 이상 걸렸고,&amp;nbsp;&lt;/span&gt;지연이 아니었을 땐 일주일 정도 걸렸습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cccover.jpg&quot; data-origin-width=&quot;2818&quot; data-origin-height=&quot;3860&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMeQhx/btrvBknNuYf/k4SNP92RbTLxYtGiCUyjiK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMeQhx/btrvBknNuYf/k4SNP92RbTLxYtGiCUyjiK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMeQhx/btrvBknNuYf/k4SNP92RbTLxYtGiCUyjiK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMeQhx%2FbtrvBknNuYf%2Fk4SNP92RbTLxYtGiCUyjiK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;548&quot; data-filename=&quot;cccover.jpg&quot; data-origin-width=&quot;2818&quot; data-origin-height=&quot;3860&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서부터는 진짜 뱀발. 재작년에는 굉장히 타이트하게 일기를 썼는데요  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대체 왜 그랬을까 싶을 정도로 집착ㅋㅋㅋㅋ해서&amp;nbsp;결국 단 하루도 빠짐없이(!) 일기를 썼는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 나중에는 본질을 잊고 지쳐서 하기 싫어지는 지경에 이르더라고요ㅋㅋㅋㅋㅠㅠ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 작년 하반기부터는 조금 느긋하게 쓰기 시작했습니다.&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;lsquo;기록을 남긴다&amp;rsquo;는 거니까, 이왕하는 거 제가 하고 싶고 즐거운 마음으로 해야죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이번에 인디자인을 써보면서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&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;/p&gt;
&lt;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;cover3.jpg&quot; data-origin-width=&quot;2879&quot; data-origin-height=&quot;3595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n3mmk/btrvt2HGHdS/epOUz5XV1OrN5yY9GMkPu0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n3mmk/btrvt2HGHdS/epOUz5XV1OrN5yY9GMkPu0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n3mmk/btrvt2HGHdS/epOUz5XV1OrN5yY9GMkPu0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn3mmk%2Fbtrvt2HGHdS%2FepOUz5XV1OrN5yY9GMkPu0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;599&quot; data-filename=&quot;cover3.jpg&quot; data-origin-width=&quot;2879&quot; data-origin-height=&quot;3595&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;&lt;b&gt;제본하는 게 진짜 네버에버절대 쉽지 않은 일이지만, &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그럼에도 불구하고 만들어진 내 책을 보는 건 짜릿해! 최고야! 니까 도전해볼 가치 있는 일이라 생각합니다.&lt;br /&gt;(&amp;bull;̀ᴗ&amp;bull;́)و ̑̑ 다들 예쁜 책 만드세요!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Blog/Loves</category>
      <author>나나 (nykim)</author>
      <guid isPermaLink="true">https://anneslab.tistory.com/109</guid>
      <comments>https://anneslab.tistory.com/109#entry109comment</comments>
      <pubDate>Wed, 9 Mar 2022 23:35:14 +0900</pubDate>
    </item>
    <item>
      <title>[ReactJS] 5.   타입스크립트</title>
      <link>https://anneslab.tistory.com/108</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;bg.jpeg&quot; data-origin-width=&quot;5390&quot; data-origin-height=&quot;3719&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bir52w/btrs3TGwOCq/i9TNV3ur3AyKZRisb8wttk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bir52w/btrs3TGwOCq/i9TNV3ur3AyKZRisb8wttk/img.jpg&quot; data-alt=&quot;Abstract vector created by fullvector - www.freepik.com&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bir52w/btrs3TGwOCq/i9TNV3ur3AyKZRisb8wttk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbir52w%2Fbtrs3TGwOCq%2Fi9TNV3ur3AyKZRisb8wttk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;414&quot; data-filename=&quot;bg.jpeg&quot; data-origin-width=&quot;5390&quot; data-origin-height=&quot;3719&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Abstract vector created by fullvector - www.freepik.com&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이 글은 NomadCoders의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nomadcoders.co/react-masterclass&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&amp;lt;React JS 마스터클래스&amp;gt;&lt;/a&gt;의 내용 일부를 정리한 글입니다. 멋진 강의 감사합니다!&lt;br /&gt;&lt;br /&gt;개인 스터디 글로, 맞지 않는 내용이나 더 나은 방법을 공유해 주신다면 복받으실 거예요  ✨&lt;br /&gt;&lt;a href=&quot;http://nykim.work/105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;1) 시작하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://nykim.work/106&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2) 기능 연습 &amp;amp; 3) 앱 만들기&lt;br /&gt;&lt;/a&gt;&lt;a href=&quot;http://nykim.work/107&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;4) styled-components &lt;/a&gt;&lt;br /&gt;5)   타입스크립트 ☀︎&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;5-1. 타입스크립트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1-1. &quot;타입&quot; 스크립트?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 너무나 관대(?)해서 오냐오냐 하고 넘어갈 때가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;const sum = (a,b) =&gt; a+b;&lt;/code&gt; 에서 a와 b는 숫자로 받을 생각이었지만, 여기에 문자열을 넘기더라도 '그런갑다~' 하고 넘어가 버립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, 존재하지 않는 프로퍼티를 읽더라도 &amp;lsquo;값이 없는데? 그럼 undefined 줄게&amp;rsquo; 하고 대충 넘어가버리기 때문에 나중에 엌?? 하는 상황이 생기기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 땐 좀더 깐깐한(이른바 strongly typed)한 언어인 &amp;lsquo;타입스크립트&amp;rsquo;를 써볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트는 자바스크립트를 바탕으로 하기 때문에 거의 유사하지만, 좀 더 강력한 기능들을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검수 선생님을 둬서 DX(개발 경험ㅎㅎ)를 향상시키는 툴이라고 볼 수 있습니다.&lt;/p&gt;
&lt;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://www.typescriptlang.org/play&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.typescriptlang.org/play&lt;/a&gt; 에서 타입스크립트를 시험삼아 두들겨 봅시다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;const sum = (a:number, b:number) =&amp;gt; a+b;
sum(1,2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;code&gt;:&lt;/code&gt; 를 이용해 코드에 타입을 정의하는 방식을 Type Annotation이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이 코드를 타입스크립트가 깐깐하게 검수한 다음, &amp;lsquo;음! 문제 없군!&amp;rsquo;이라 판단하면 평범한 자바스크립트문으로 컴파일해줍니다. 이걸 브라우저가 읽게 되는 거고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, 코드 작성에도 도움이 됩니다. 예를 들면 VS CODE에서는 sum 의 타입이 Number라는 걸 알고 있다면 &lt;code&gt;sum.&lt;/code&gt; 까지 쳤을 때 Number에서 제공하는 API들을 밑에 미리 띄워주므로 쉽게 자동 작성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1-2. 설치하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://create-react-app.dev/docs/adding-typescript/#installation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Adding TypeScript | Create React App&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;# 신규 프로젝트 생성 시 타입스크립트 사용
npx create-react-app {app-name} --template typescript
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;# 기존 프로젝트에 타입스크립트 사용
npm install --save typescript @types/node @types/react @types/react-dom @types/jest

# 자바스크립트 기반 라이브러리인 경우 `@types/styled-components`와 같이 추가로 DefinitelyTyped 설치
# .js 파일은 .tsx 파일로 확장자 변경 후 개발서버 재시작
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-01 at 5.45.38 PM.png&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;978&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L8yNK/btrs3UZAPB2/c1r7F9po5e4NhKPkogAsOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L8yNK/btrs3UZAPB2/c1r7F9po5e4NhKPkogAsOk/img.png&quot; data-alt=&quot;앗 낯설다!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L8yNK/btrs3UZAPB2/c1r7F9po5e4NhKPkogAsOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL8yNK%2Fbtrs3UZAPB2%2Fc1r7F9po5e4NhKPkogAsOk%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;200&quot; height=&quot;418&quot; data-filename=&quot;Screen Shot 2022-02-01 at 5.45.38 PM.png&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;978&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;propTypes를 활용하면 어떤 타입의 props를 받을지 설정해줄 수 있었지만, 코드가 실행된 &amp;ldquo;후&amp;rdquo;에 콘솔에 에러를 찍는 흐름이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 타입스크립트를 쓰면 코드 실행 &amp;ldquo;전&amp;rdquo;에 체크하도록 할 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1-3. props 설정하기&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;bgColor라는 props를 필요로 하는 &amp;lt;Circle/&amp;gt; 이란 컴포넌트가 있다고 해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;/* App.tsx */

import Circle from './Circle';

function App() {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Circle bgColor=&quot;orange&quot; /&amp;gt;
      &amp;lt;Circle bgColor=&quot;green&quot; /&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;/* Circle.tsx */

import styled from 'styled-components';

const Container = styled.div``;

function Circle({bgColor}) {
  return &amp;lt;Container /&amp;gt;;
}

export default Circle;
&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;일반적인 JS 환경이면 별일없이 넘어갈 텐데, 타입스크립트는 &quot;{bgColor}의 타입이 뭔데!!&quot;라며 밑줄을 쫙쫙 긋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &amp;ldquo;얜 이런 모양이야&quot;라고 타입스크립트하고 정해놓은 약속(=인터페이스)를 지정해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;interface&lt;/code&gt; 키워드를 이용해 인터페이스를 만들어 줄 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 인터페이스 이름은 I를 붙이기도 합니다 (ex. 가격정보 &amp;rarr; IPriceData)&lt;/p&gt;
&lt;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;lt;Circle/&amp;gt;에게 넘어오는 각 props의 타입은 CircleProps라고 알려줍시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import styled from 'styled-components';

const Container = styled.div``;

interface CircleProps {
  bgColor: string;
}

function Circle({ bgColor }: CircleProps) {
  return &amp;lt;Container /&amp;gt;;
}

export default Circle;
&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;lt;Circle/&amp;gt;이 넘겨받은 bgColor 란 props를 다시 자식 컴포넌트인 &amp;lt;Container/&amp;gt;에게 넘겨줄 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타일드 컴포넌트인 &amp;lt;Container/&amp;gt; 가 받을 props들도 설정해준 뒤, &lt;code&gt;styled.div&amp;lt;{인터페이스명}&amp;gt;``;&lt;/code&gt; 형태로 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import styled from 'styled-components';

interface CircleProps {
  bgColor: string;
}

interface ContainerProps {
  bgColor: string;
}

const Container = styled.div&amp;lt;ContainerProps&amp;gt;`
  width: 300px;
  height: 300px;
  background-color: ${(props) =&amp;gt; props.bgColor};
  border-radius: 50%;
`;

function Circle({ bgColor }: CircleProps) {
  return &amp;lt;Container bgColor={bgColor} /&amp;gt;;
}

export default Circle;
&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;그럼 이제 bgColor라는 props는 타입스크립트의 검수 대상에 올라왔기에(?) 무사히 내려보낼 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &amp;lt;&amp;gt; 형태로 사용하는 것을 제네릭(Generic)이라 하는데, 타입정의를 매개변수로 넘겨주는 것처럼 사용하고 있습니다.&lt;/p&gt;
&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://joshua1988.github.io/ts/guide/generics.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;타입스크립트 핸드북 :: &lt;span style=&quot;background-color: #ffffff;&quot;&gt;Captain Pangyo&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1-4. Optional Props&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bgColor는 꼭 필요한 Props인데, 그렇다면 반드시 필요하지 않은 Props는 어떻게 설정할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 물음표를 붙여주면 됩니다?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;interface CircleProps {
  bgColor: string;
  borderColor?: string;
}
&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;borderColor는 타입이 &lt;code&gt;string | undefined&lt;/code&gt; 가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;code&gt;|&lt;/code&gt; 연산자를 이용해 타입을 여러 개 연결하는 방식을 유니온 타입이라고 부릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 값은 string 또는 undefined니까 값이 undefined더라도 당황하지 말고 넘어가세요! 하고 타입스크립트에게 말해주는 거죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 지금 CircleProps와 ContainerProps 두 개가 있는데, CircleProps는 선택값으로 받되 ContainerProps에서는 필수값으로 받아봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 border값이 있어야 하긴 하는데, 그 값이 부모에서 넘어올 수도 있고 아닐 수도 있는 상황이란 거죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;interface CircleProps {
  bgColor: string;
  borderColor?: string;
}

interface ContainerProps {
  bgColor: string;
  borderColor: string;
}
&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;그래서 만약 부모(Circle)로부터 넘겨받은 값이 없다면 그냥 bgColor 값이 borderColor 값이 되도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;borderColor={borderColor&amp;nbsp;??&amp;nbsp;bgColor}&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;i&gt;&lt;b&gt;Tip.&amp;nbsp;&lt;/b&gt;&lt;/i&gt;nullish 병합 연산자&lt;br /&gt;&lt;code&gt;a ?? b&lt;/code&gt;에서 a 가 &lt;code&gt;null&lt;/code&gt;또는 &lt;code&gt;undefined&lt;/code&gt;라면 b, 아니라면 a&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// ...
const Container = styled.div&amp;lt;ContainerProps&amp;gt;`
  width: 300px;
  height: 300px;
  background-color: ${(props) =&amp;gt; props.bgColor};
  border: 3px solid ${(props) =&amp;gt; props.borderColor};
  border-radius: 50%;
`;

function Circle({ bgColor, borderColor }: CircleProps) {
  return &amp;lt;Container bgColor={bgColor} borderColor={borderColor ?? bgColor} /&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;또는 { text = &amp;ldquo;Lorem Ipsum&amp;rdquo;} 과 같이 기본값을 설정해 줄 수도 있겠죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;/* Circle.tsx */

import styled from 'styled-components';
interface CircleProps {
  bgColor: string;
  borderColor?: string;
  text?: string;
}
interface ContainerProps {
  bgColor: string;
  borderColor: string;
}

const Container = styled.div&amp;lt;ContainerProps&amp;gt;`
  width: 300px;
  height: 300px;
  background-color: ${(props) =&amp;gt; props.bgColor};
  border: 3px solid ${(props) =&amp;gt; props.borderColor};
  border-radius: 50%;
`;

function Circle({ bgColor, borderColor, text = 'Lorem Ipsum' }: CircleProps) {
  return (
    &amp;lt;Container bgColor={bgColor} borderColor={borderColor ?? bgColor}&amp;gt;
      {text}
    &amp;lt;/Container&amp;gt;
  );
}

export default Circle;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 더 깐깐하게 가고 싶다면 컴포넌트의 타입을 지정해 줄 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function Button({children}: ButtonProps):JSX.Element {
  return &amp;lt;button&amp;gt;{children}&amp;lt;/button&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1-5. State&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;타입스크립트를 사용하고 있다면 리액트의 state 초기값을 보고 알아서 타입을 유추해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;const [value, setValue] = useState(true);&lt;/code&gt; 라면 Boolean 값이 여기 들어오겠거니 알아챕니다 (이것이 알잘딱깔센)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 state값이 undefined나 null이 될 수 있는 등, 여러 타입이 올 수 있다면 따로 타입을 지정해 줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;const [value, setValue] = useState&amp;lt;number | null&amp;gt;(0);&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임의의 폼을 만들어 보죠! 사용자가 인풋에 이름을 입력하고 버튼을 클릭하면, 입력한 내용을 표시해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { useState } from 'react';

function App() {
  const [name, setName] = useState('');
  const onChange = () =&amp;gt; {};

  return (
    &amp;lt;form&amp;gt;
      &amp;lt;input type=&quot;text&quot; value={name} onChange={onChange} /&amp;gt;
      &amp;lt;button&amp;gt;확인&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 입력한 내용을 받아오기 위해 event 객체를 인자로 받아올 건데...&lt;/p&gt;
&lt;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;Screen Shot 2022-02-02 at 4.53.54 PM.png&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pw1GQ/btrs2EJOUHA/bzoFqy8AwmONXfZe8qOMTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pw1GQ/btrs2EJOUHA/bzoFqy8AwmONXfZe8qOMTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pw1GQ/btrs2EJOUHA/bzoFqy8AwmONXfZe8qOMTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPw1GQ%2Fbtrs2EJOUHA%2FbzoFqy8AwmONXfZe8qOMTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;174&quot; data-filename=&quot;Screen Shot 2022-02-02 at 4.53.54 PM.png&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;320&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;잠깐, 이 넘겨받을 e는 타입이 뭐지!? 하고 타입스크립트가 의문을 제기합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 깐깐한 검수 조건에 맞춰 얘도 타입을 지정해줘야겠네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 onChange는 React에서 제공하는 SyntheticEvent 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 브라우저가 제공하는 기본 이벤트가 아니고, 리액트가 다양한 환경에서 일관되게 동작할 수 있도록 만든 이벤트 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 event 객체도 우리가 아는 걔(?)가 아니고 SyntheticBaseEvent 객체입니다.&lt;/p&gt;
&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://reactjs.org/docs/events.html#form-events&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;를 살펴보면 onChange 는 Form Events 임을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이벤트를 발생시킬 요소는 &amp;lt;HTMLInputElement&amp;gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 e 객체의 타입을 &lt;code&gt;React.FormEvent&amp;lt;HTMLInputElement&amp;gt;&lt;/code&gt; 로 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Feb-02-2022 17-15-01.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmFSMn/btrs67DSULT/7dejdgpUEMVvfk0Mw8TsF1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmFSMn/btrs67DSULT/7dejdgpUEMVvfk0Mw8TsF1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmFSMn/btrs67DSULT/7dejdgpUEMVvfk0Mw8TsF1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bmFSMn/btrs67DSULT/7dejdgpUEMVvfk0Mw8TsF1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;388&quot; data-filename=&quot;Feb-02-2022 17-15-01.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;388&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;그러면 e.currentTarget.value 으로 값을 가져올 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 e 객체에서 꺼내 써야 할 것들이 많다면 이렇게 조각조각 낼 수도 있죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const {
  currentTarget: { value }
} = e;

/*
  ① const { } = e;
  ② const { currentTarget } = e;
  ③ const { currentTarget: {value} } = e;
  ④ const { currentTarget: {value, id, innerHTML } } = e;
    console.log(id);
*/&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;onSubmit도 비슷하게 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const onSubmit = (e: React.FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
  e.preventDefault();
  alert(`Hello, ${name}!`);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Feb-02-2022 17-44-20.gif&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DyrmA/btrs7GZ4WSl/pepea1qAWsH5AY4WEPxtKK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DyrmA/btrs7GZ4WSl/pepea1qAWsH5AY4WEPxtKK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DyrmA/btrs7GZ4WSl/pepea1qAWsH5AY4WEPxtKK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/DyrmA/btrs7GZ4WSl/pepea1qAWsH5AY4WEPxtKK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;346&quot; data-filename=&quot;Feb-02-2022 17-44-20.gif&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&lt;b&gt;Tip.&lt;/b&gt;&lt;/i&gt; &lt;code&gt;e.target&lt;/code&gt; vs. &lt;code&gt;e.currentTarget&lt;/code&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.target = 이벤트를 트리거한 요소&lt;/li&gt;
&lt;li&gt;e.currentTarget = 이벤트 리스너가 할당된 요소&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 버블링 등이 발생한 경우, &lt;code&gt;e.target !== e.currentTarget&lt;/code&gt; 이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 e 객체에서 꺼내 써야 할 것들이 많다면 이렇게 조각조각 낼 수도 있죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const {
  currentTarget: { value }
} = e;

/*
  ① const { } = e;
  ② const { currentTarget } = e;
  ③ const { currentTarget: {value} } = e;
  ④ const { currentTarget: {value, id, innerHTML } } = e;
    console.log(id);
*/&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1-6. Theme&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;스타일드 컴포넌트를 위한 타입스크립트 정의는 declarations 파일을 통해 확장할 수 있습니다. &lt;a href=&quot;https://styled-components.com/docs/api#typescript&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;code&gt;styled.d.ts&lt;/code&gt; 란 이름으로 declarations 파일을 생성 후 아래 내용을 붙여넣습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(d.ts 파일은 선언(delcaration)을 통해&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #2c3e50;&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;스타일드 컴포넌트의 DefaultTheme 에게 인터페이스를 지정해주는 거죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;/* styled.d.ts */

// import original module declarations
import 'styled-components';

// and extend them!
declare module 'styled-components' {
  export interface DefaultTheme {
    textColor: string;
	bgColor: string
  }
}&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;import { DefaultTheme } 으로 가져오고, 작성한 테마의 타입으로 지정해준 다음 export 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;/* theme.ts */

import { DefaultTheme } from 'styled-components';

export const lightTheme: DefaultTheme = {
  textColor: '#000',
  bgColor: '#fff'
};

export const darkTheme: DefaultTheme = {
  textColor: '#fff',
  bgColor: '#000'
};&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;lt;ThemeProvider/&amp;gt; 로 감싸고 theme={}로 지정해주면 되겠죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;/* index.tsx */

//...
import { ThemeProvider } from 'styled-components';
import { darkTheme } from './theme';

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;ThemeProvider theme={darkTheme}&amp;gt;
      &amp;lt;App /&amp;gt;
    &amp;lt;/ThemeProvider&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;그러면 props.theme로 가져올 수 있게 되며, 어떤 속성을 어떤 타입으로 써야하는지 명확히 알 수 있습니다.&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;Screen Shot 2022-02-02 at 6.10.09 PM.png&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dT5mPk/btrs7GZ5GMd/ivy0aPXblsxyvubL1KKlM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dT5mPk/btrs7GZ5GMd/ivy0aPXblsxyvubL1KKlM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dT5mPk/btrs7GZ5GMd/ivy0aPXblsxyvubL1KKlM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdT5mPk%2Fbtrs7GZ5GMd%2Fivy0aPXblsxyvubL1KKlM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;291&quot; data-filename=&quot;Screen Shot 2022-02-02 at 6.10.09 PM.png&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;542&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1-7. 타입스크립트 문법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에서 정의할 수 있는 기본 타입은 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Boolean&lt;/li&gt;
&lt;li&gt;Number&lt;/li&gt;
&lt;li&gt;String&lt;/li&gt;
&lt;li&gt;Object&lt;/li&gt;
&lt;li&gt;Array&lt;br /&gt;ex) &lt;code&gt;let arr: [number[] = [1,2,3];&lt;/code&gt;&lt;br /&gt;ex) &lt;code&gt;let arr: Array&amp;lt;number&amp;gt; = [1,2,3];&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Tuple&lt;/li&gt;
&lt;li&gt;Enum&lt;/li&gt;
&lt;li&gt;Any&lt;/li&gt;
&lt;li&gt;Void&lt;/li&gt;
&lt;li&gt;Null&lt;/li&gt;
&lt;li&gt;Undefined&lt;/li&gt;
&lt;li&gt;Never (함수의 끝에 절대 도달하지 않는다는 의미를 지닌 타입)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에서는 함수의 인자도 필수값으로 보며, 정의된 매개변수 값만 받을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니면 ? 를 써서 옵셔널 값으로 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;function sum(a: number, b: number): number {
  return a + b;
}
sum(10); // 에러
&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;REST 문법을 써서 매개변수를 넘기고 싶다면 이렇게 정의할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function sum(a: number, ...nums: number[]):number {
	let total = 0;
	for (let key in nums) {
		total += nums[key]
	}
	return a + total;
}
&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-1-8. 타입스크립트 &amp;amp; CSS Module&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.module.css&lt;/code&gt; 형태로 CSS를 작성하면, 리액트 컴포넌트에서 해당 CSS 파일을 불러올 때 클래스 이름을 &amp;lsquo;고유하게&amp;lsquo; 바꿔줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 머리 아프게 클래스 네이밍을 고민할 필요가 없습니다. (딱 여기서만 쓰일 거니까요!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주관적인 생각으론 styled-components랑 다르게 CSS 스타일링만 별도 관리한다는 느낌이 강했습니다. 조금 더 친숙한 느낌!?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;Button.tsx 생성 후 기본적인 버튼 마크업을 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function Button({children}) {
  return (
    &amp;lt;button type=&quot;button&quot;&amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
}

export default Button;
&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;children을 포함해 버튼이 갖춰야 할 props와 그들의 타입을 인터페이스로 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { ReactNode } from 'react';

interface ButtonProps {
  shape: 'fill' | 'line';
  color?: string;
  disabled?: boolean;
  children: ReactNode;
}

function Button({
  shape = 'fill',
  color,
  disabled = false,
  children
}: ButtonProps): JSX.Element {
  return (
    &amp;lt;button type=&quot;button&quot; disabled={disabled}&amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
}

export default Button;
&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;lsquo;이 버튼을 쓰려면 이렇게 써야겠군&amp;rsquo;이라고 파악할 수 있죠.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shape와 children은 필수값이고, color와 disabled는 옵셔널입니다.&lt;/li&gt;
&lt;li&gt;shape의 기본값은 &amp;lsquo;fill&amp;rsquo;이며, disabled는 기본적으로 false입니다.&lt;/li&gt;
&lt;li&gt;&amp;lt;Button/&amp;gt; 컴포넌트의 타입은 JSX.Element 입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS-in-JS가 아니므로 props를 CSS에게 넘겨줄 수는 없지만, 클래스 형태로 알려줄 수는 있겠네요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스네임을 편하게 작성하기 위해 classnames 라이브러리를 설치해 가져옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import style from './Button.module.scss';
import classNames from 'classnames/bind';
const cx = classNames.bind(style);
&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;이때, scss 파일은 tsx나 ts 같이 익숙한 모듈 파일이 아니여서 타입스크립트한테 검문 당하므로&lt;br /&gt;d.ts 파일에서 &lt;code&gt;declare module '*.scss';&lt;/code&gt; 로 타입 추론을 도와줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;code&gt;cx()&lt;/code&gt; 형태로 조건부 클래스 네이밍이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;cx('btn', 'primary')
cx('btn', { primary: true })
cx('btn', ['primary', 'special'])
&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;먼저 shape를 넘겨줘서 클래스로 fill 또는 line 을 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;className={cx(${shape})}&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 color도 넘겨줍시다. 이때 color는 옵셔널이므로 &amp;amp;&amp;amp; 연산자를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;className={cx(${shape}, color &amp;amp;&amp;amp; ${color})}&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 scss 파일에서 분기 처리해주면 끝!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;scss&quot;&gt;&lt;code&gt;/* Button.module.scss */

button {
  $default: #333;
  $palette: (
    primary: #f25050,
    secondary: #1a58de
  );

  display: block;
  padding: 1rem 2rem;
  font-size: 1.4rem;
  margin: 0.5rem;
  color: #fff;
  background-color: $default;
  border: 0.2rem solid;
  border-radius: 0.2rem;
  border-color: $default;

  &amp;amp;.line {
    color: $default;
    background-color: #fff;
    border-color: $default;
  }

  &amp;amp;:hover {
    opacity: 0.8;
  }

  &amp;amp;:disabled {
    opacity: 0.3;
    cursor: not-allowed;
  }

  @each $name, $value in $palette {
    &amp;amp;.#{$name} {
      &amp;amp;.fill {
        color: #fff;
        background-color: $value;
        border-color: $value;
      }

      &amp;amp;.line {
        color: $value;
        background-color: #fff;
        border-color: $value;
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/react-with-module-sass-ckylz?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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;5-2. 코인 트래커 만들기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-2-1. 시작하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 패키지 설치!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$ npm i react-rotuer-dom&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router.tsx 생성 후 경로를 지정해줍니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;/ &amp;rArr; 코인 목록(Coins)로&lt;/li&gt;
&lt;li&gt;/코인ID &amp;rArr; 해당 코인 상세(Coin)으로&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;/* Router.tsx */

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Coin from './routes/Coin';
import Coins from './routes/Coins';

function Router() {
  return (
    &amp;lt;BrowserRouter&amp;gt;
      &amp;lt;Routes&amp;gt;
        &amp;lt;Route path=&quot;/&quot; element={&amp;lt;Coins /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/:coinId&quot; element={&amp;lt;Coin /&amp;gt;} /&amp;gt;
      &amp;lt;/Routes&amp;gt;
    &amp;lt;/BrowserRouter&amp;gt;
  );
}

export default Router;
&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;그리고 App에 Router를 가져온 뒤 실행시켜 보면...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;/* App.tsx */

import Router from './Router';

function App() {
  return &amp;lt;Router /&amp;gt;;
}

export default App;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Feb-02-2022 20-20-12.gif&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3r4lT/btrs293QyhT/NFked10SJvWK7Z0tg0twDk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3r4lT/btrs293QyhT/NFked10SJvWK7Z0tg0twDk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3r4lT/btrs293QyhT/NFked10SJvWK7Z0tg0twDk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b3r4lT/btrs293QyhT/NFked10SJvWK7Z0tg0twDk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;346&quot; data-filename=&quot;Feb-02-2022 20-20-12.gif&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;이제 url의 &lt;code&gt;/:coinId&lt;/code&gt; 를 통해 어떤 코인을 보려고 하는지 알아봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nykim.work/106&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;전&lt;/a&gt;에 썼던 것처럼 &lt;code&gt;useParams()&lt;/code&gt; 훅으로 받아오면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;/* Coin.tsx */

import { useParams } from 'react-router-dom';

function Coin() {
  const { coinId } = useParams();
  return &amp;lt;h1&amp;gt;Coin&amp;lt;/h1&amp;gt;;
}

export default Coin;
&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;react-router-dom v6 이상인 경우, useParams() 만 쓰더라도 타입이 &lt;code&gt;string | undefined&lt;/code&gt; 일 거라고 알아서 예상해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-2-2. API 받아오기&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;받아올 코인 정보를 state로 관리해 봅시다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;const [coins, setCoins] = useState([]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-03 at 1.47.13 PM.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;744&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sh6lP/btrs2s3nFaN/X3fbacVH79kMKqkMaPsJd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sh6lP/btrs2s3nFaN/X3fbacVH79kMKqkMaPsJd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sh6lP/btrs2s3nFaN/X3fbacVH79kMKqkMaPsJd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsh6lP%2Fbtrs2s3nFaN%2FX3fbacVH79kMKqkMaPsJd0%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;660&quot; height=&quot;320&quot; data-filename=&quot;Screen Shot 2022-02-03 at 1.47.13 PM.png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;744&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; &amp;nbsp; 거참 엄청 깐깐하시네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검수 담당자(=타입스크립트)의 통과를 받기 위해서는, API로 받아온 데이터들의 타입이 어떻게 될지도 미리 알려줘야 합니다. &lt;br /&gt;그리고 배열 형태로 넘겨받을 거란 것도 알려줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;interface ICoin {
  id: string;
  name: string;
  symbol: string;
  rank: number;
  is_new: boolean;
  is_active: boolean;
  type: string;
}

const [coins, setCoins] = useState&amp;lt;ICoin[]&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;이제 useEffect() 를 사용해 최초 한 번 정보를 받아와 setCoins로 coins 안에 넣어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    (async () =&amp;gt; {
      const response = await fetch('https://api.coinpaprika.com/v1/coins');
      const json = await response.json();
      setCoins(json.slice(0, 30));
    })();
  }, []);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불러오는 동안 화면을 가려줄 로딩 커튼도 있으면 좋고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Coins() {
  const [coins, setCoins] = useState&amp;lt;ICoin[]&amp;gt;([]);
  const [loading, setLoading] = useState(true);

  useEffect(() =&amp;gt; {
    (async () =&amp;gt; {
      const response = await fetch('https://api.coinpaprika.com/v1/coins');
      const json = await response.json();
      setCoins(json.slice(0, 30));
      setLoading(false);
    })();
  }, []);
  return (
    &amp;lt;Container&amp;gt;
      &amp;lt;Header&amp;gt;
        &amp;lt;Title&amp;gt;MyCoin&amp;lt;/Title&amp;gt;
      &amp;lt;/Header&amp;gt;
      {loading ? (
        &amp;lt;Loader&amp;gt;Loading...&amp;lt;/Loader&amp;gt;
      ) : (
        &amp;lt;CoinList&amp;gt;
          {coins.map((coin, i) =&amp;gt; (
            &amp;lt;CoinItem key={coin.id}&amp;gt;
              &amp;lt;Link to={`/${coin.id}`}&amp;gt;{coin.name}&amp;lt;/Link&amp;gt;
            &amp;lt;/CoinItem&amp;gt;
          ))}
        &amp;lt;/CoinList&amp;gt;
      )}
    &amp;lt;/Container&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;header&gt;&lt;/header&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글만 있으면 심심하니 이미지도 넣어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1644565144476&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;CoinItem key={coin.id}&amp;gt;
	&amp;lt;Link to={`/${coin.id}`}&amp;gt;
		&amp;lt;Img src={`https://cryptoicon-api.vercel.app/api/icon/${coin.symbol.toLowerCase()}`}/&amp;gt;
    {coin.name}
  &amp;lt;/Link&amp;gt;
&amp;lt;/CoinItem&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 상세화면인 &amp;lt;Coin/&amp;gt;를 작업할 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상세화면에서 코인 정보를 요청할 때, 이미 &amp;lt;Coins/&amp;gt; 에서 받아온 정보인 coin.name이 있으므로 이걸 넘겨줘 봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;Link&amp;gt;의 state에 object 형태로 넘겨줄 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;&amp;lt;Link to={`/${coin.id}`} state={{ name: coin.name }}&amp;gt;&amp;lt;/Link&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;lt;Coin/&amp;gt;에서는 useLocation()으로 Location Object에 접근하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;const { state } = useLocation() as RouteState;&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;lt;Title&amp;gt;{state.name}&amp;lt;/Title&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-03 at 2.34.02 PM.png&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mhnP2/btrs7qwxnuS/uXy61Rsrwb0cCu2hwd6qFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mhnP2/btrs7qwxnuS/uXy61Rsrwb0cCu2hwd6qFK/img.png&quot; data-alt=&quot;console.log(useLocation());&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mhnP2/btrs7qwxnuS/uXy61Rsrwb0cCu2hwd6qFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmhnP2%2Fbtrs7qwxnuS%2FuXy61Rsrwb0cCu2hwd6qFK%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;400&quot; height=&quot;306&quot; data-filename=&quot;Screen Shot 2022-02-03 at 2.34.02 PM.png&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;console.log(useLocation());&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;하지만 이대로 state를 가져다 쓰려면 타입을 알려달란 잔소리를 듣습니다.&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;Screen Shot 2022-02-03 at 2.36.25 PM.png&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CZJyR/btrs71pA5Hh/quEKI1s5WKJCkTwBvWN5Z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CZJyR/btrs71pA5Hh/quEKI1s5WKJCkTwBvWN5Z0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CZJyR/btrs71pA5Hh/quEKI1s5WKJCkTwBvWN5Z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCZJyR%2Fbtrs71pA5Hh%2FquEKI1s5WKJCkTwBvWN5Z0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;159&quot; data-filename=&quot;Screen Shot 2022-02-03 at 2.36.25 PM.png&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;274&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;pre class=&quot;pf&quot;&gt;&lt;code&gt;interface RouteState {
  state: {
    name: string;
  };
}

function Coin() {
  const { state } = useLocation() as RouteState;
  return (
	  &amp;lt;Title&amp;gt;{state.name}&amp;lt;/Title&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;그럼 API 요청 전, state를 통한 coin.name을 전달할 수 있으므로 화면에 재빨리 표시할 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 문제는 &amp;lt;Coin&amp;gt; &amp;rArr; &amp;lt;Coins&amp;gt; 가 아닌 바로 &amp;lt;Coins&amp;gt; 로 접근했을 때는 받아올 coin.name이 없다는 것입니다. &lt;br /&gt;그래서 안전장치가 하나 필요해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;&amp;lt;Title&amp;gt;{state?.name || 'Loading'}&amp;lt;/Title&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Tip.&lt;/b&gt;&lt;/i&gt; 옵셔널 체이닝&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;a?.b&lt;/code&gt; 에서 a가&amp;nbsp;&lt;code&gt;undefined&lt;/code&gt; 또는 &lt;code&gt;null&lt;/code&gt;이면 평가를 멈추고 &lt;code&gt;undefined&lt;/code&gt;를 반환합니다. 이때 a가 선언되어 있지 않으면 에러가 발생합니다.&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://ko.javascript.info/optional-chaining&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;옵셔널 체이닝 &amp;lsquo;?.&amp;rsquo; :: 모던 JavaScript 튜토리얼&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 세부 코인 정보를 불러옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const [info, setInfo] = useState({});
const [priceInfo, setPriceInfo] = useState({});

useEffect(() =&amp;gt; {
    (async () =&amp;gt; {
      const infoData = await (
        await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}`)
      ).json();
      const priceData = await (
        await fetch(`https://api.coinpaprika.com/v1/tickers/${coinId}`)
      ).json();
      setInfo(infoData);
      setPriceInfo(priceData);
			setLoading(false);
    })();
  }, [coinId]); // coinId는 URL을 통해 넘어오므로 변경될 일이 없어 어차피 1번만 실행됨
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 다음에 할 일은... 깐깐한 검수 통과를 위한 타입 지정이죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useState({}) 로 지정했기 때문에 타입스크립트는 info나 priceInfo가 빈 객체일 거라고 생각하지만, &lt;br /&gt;사실 거기엔 뭐뭐가 들어갈 거야 라고 세부사항을 말해줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이렇게 가져올 수 있어요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔로 찍고 &amp;rArr; 우클릭 - &amp;lsquo;Copy object&amp;rsquo; &amp;rArr; JSON을 타입으로 변환해주는 사이트 활용&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://app.quicktype.io/?l=ts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://app.quicktype.io/?l=ts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://json2ts.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://json2ts.com/&lt;/a&gt;&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;0-gif.gif&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wPX0p/btrs3RPdDdN/KbOaKrk8zpAYJr7i0dpMvK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wPX0p/btrs3RPdDdN/KbOaKrk8zpAYJr7i0dpMvK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wPX0p/btrs3RPdDdN/KbOaKrk8zpAYJr7i0dpMvK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/wPX0p/btrs3RPdDdN/KbOaKrk8zpAYJr7i0dpMvK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;555&quot; data-filename=&quot;0-gif.gif&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;748&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;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;interface InfoData {
  id: string;
  name: string;
  //...
}

interface PriceData {
  id: string;
  name: string;
  //...
}

const [info, setInfo] = useState&amp;lt;InfoData&amp;gt;();
const [priceInfo, setPriceInfo] = useState&amp;lt;PriceData&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;Tip.&lt;/b&gt;&lt;/i&gt; VS CODE 단축키&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ctrl + D : 동일한 단어 한번에 선택&lt;/li&gt;
&lt;li&gt;Opt + Up/Down :해당 줄의 코드를 위아래로 위치 변경 (Shift 포함 시 해당 위치로 복제)&lt;/li&gt;
&lt;li&gt;Opt + Shift + I : 각줄의 가장 오른쪽에 커서 포커스&lt;/li&gt;
&lt;li&gt;Opt + Shift + Drag : 각줄의 동일한 위치에 커서 포커스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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-2-3. Nested Routes&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;Coin/&amp;gt; 내에는 2개의 탭이 있어 하나는 가격 정보를, 하나는 차트를 보여준다고 해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 /chart 등을 통해 넘어온다면 바로 차트 탭이 보여지도록 하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러려면 라우트 설정이 필요하겠네요! react-router-dom v6 기준으로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route 안에 하위 Route 를 넣고 Outlet으로 표시하거나 &lt;a href=&quot;https://reactrouter.com/docs/en/v6/getting-started/overview#nested-routes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(참고)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;import { Routes, Route, Outlet } from &quot;react-router-dom&quot;;

function App() {
  return (
    &amp;lt;Routes&amp;gt;
      &amp;lt;Route path=&quot;invoices&quot; element={&amp;lt;Invoices /&amp;gt;}&amp;gt;
        &amp;lt;Route path=&quot;:invoiceId&quot; element={&amp;lt;Invoice /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;sent&quot; element={&amp;lt;SentInvoices /&amp;gt;} /&amp;gt;
      &amp;lt;/Route&amp;gt;
    &amp;lt;/Routes&amp;gt;
  );
}

function Invoices() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Invoices&amp;lt;/h1&amp;gt;
      &amp;lt;Outlet /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function Invoice() {
  let { invoiceId } = useParams();
  return &amp;lt;h1&amp;gt;Invoice {invoiceId}&amp;lt;/h1&amp;gt;;
}

function SentInvoices() {
  return &amp;lt;h1&amp;gt;Sent Invoices&amp;lt;/h1&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;요소 내부에서 후손 Route를 렌더링할 수 있습니다. &lt;a href=&quot;https://reactrouter.com/docs/en/v6/getting-started/overview#descendant-routes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(참고)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;function App() {
  return (
    &amp;lt;Routes&amp;gt;
      &amp;lt;Route path=&quot;/&quot; element={&amp;lt;Home /&amp;gt;} /&amp;gt;
      &amp;lt;Route path=&quot;dashboard/*&quot; element={&amp;lt;Dashboard /&amp;gt;} /&amp;gt;
    &amp;lt;/Routes&amp;gt;
  );
}

function Dashboard() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;Look, more routes!&amp;lt;/p&amp;gt;
      &amp;lt;Routes&amp;gt;
        &amp;lt;Route path=&quot;/&quot; element={&amp;lt;DashboardGraphs /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;invoices&quot; element={&amp;lt;InvoiceList /&amp;gt;} /&amp;gt;
      &amp;lt;/Routes&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;routes/ 폴더 내 Price.tsx와 Chart.tsx를 만들고 중첩시켜 표현해 봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;/* Router.tsx */

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Coin from './routes/Coin';
import Coins from './routes/Coins';
import Price from './routes/Price';
import Chart from './routes/Chart';

function Router() {
  return (
    &amp;lt;BrowserRouter&amp;gt;
      &amp;lt;Routes&amp;gt;
        &amp;lt;Route path=&quot;/&quot; element={&amp;lt;Coins /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/:coinId&quot; element={&amp;lt;Coin /&amp;gt;}&amp;gt;
          &amp;lt;Route path=&quot;price&quot; element={&amp;lt;Price /&amp;gt;} /&amp;gt;
          &amp;lt;Route path=&quot;chart&quot; element={&amp;lt;Chart /&amp;gt;} /&amp;gt;
        &amp;lt;/Route&amp;gt;
      &amp;lt;/Routes&amp;gt;
    &amp;lt;/BrowserRouter&amp;gt;
  );
}

export default Router;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;/* Coin.tsx */

import { useEffect, useState } from 'react';
import { useParams, useLocation, Outlet } from 'react-router-dom';
//...

function Coin() {
  //...

  return (
    &amp;lt;Container&amp;gt;
	    {/* ... */}
	    &amp;lt;div className=&quot;tab&quot;&amp;gt;
	      &amp;lt;Outlet /&amp;gt;
	    &amp;lt;/div&amp;gt;
    &amp;lt;/Container&amp;gt;
  );
}

export default Coin;
&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;이제 각 탭을 클릭하면 /price 또는 /chart URL로 연결되도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Tabs&amp;gt;
  &amp;lt;Tab&amp;gt;
    &amp;lt;Link to={`/${coinId}/price`}&amp;gt;Price&amp;lt;/Link&amp;gt;
  &amp;lt;/Tab&amp;gt;
  &amp;lt;Tab&amp;gt;
    &amp;lt;Link to={`/${coinId}/chart`}&amp;gt;Chart&amp;lt;/Link&amp;gt;
  &amp;lt;/Tab&amp;gt;
&amp;lt;/Tabs&amp;gt;
&amp;lt;Outlet /&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;현재 탭이 활성화되어 있는지 (=URL이 price 또는 chart인지) 확인해 .isActive 클래스를 붙여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;useMatch()&lt;/code&gt; 훅은 경로와 URL이 일치하면 관련 객체를 반환하고, 아니라면 null을 반환합니다. &lt;a href=&quot;https://reach.tech/router/api/useMatch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(참고)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;const priceMatch = useMatch('/:coinId/price');
const chartMatch = useMatch('/:coinId/chart');

console.log(priceMatch);
console.log(chartMatch);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Tabs&amp;gt;
	&amp;lt;Tab isActive={priceMatch !== null}&amp;gt;
	  &amp;lt;Link to={`/${coinId}/price`}&amp;gt;Price&amp;lt;/Link&amp;gt;
	&amp;lt;/Tab&amp;gt;
	&amp;lt;Tab isActive={chartMatch !== null}&amp;gt;
	  &amp;lt;Link to={`/${coinId}/chart`}&amp;gt;Chart&amp;lt;/Link&amp;gt;
	&amp;lt;/Tab&amp;gt;
&amp;lt;/Tabs&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;이제 할 일은 styled-component 안에 &lt;code&gt;props.isActive ?&lt;/code&gt; 로 분기 처리하는 거겠네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아, 타입스크립트가 isActive라는 custom props의 타입을 인지할 수 있도록 알려주는 것도 필요하고요. &lt;a href=&quot;https://styled-components.com/docs/api#using-custom-props&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(참고)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;roboconf&quot;&gt;&lt;code&gt;const Tab = styled.span&amp;lt;{ isActive: boolean }&amp;gt;`
  color: ${(props) =&amp;gt;
    props.isActive ? props.theme.accentColor : props.theme.textColor};
`;
&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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-2-4. React Query&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;a href=&quot;https://react-query.tanstack.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React Query&lt;/a&gt;를 써서 더 쉽고 편하게 리팩토링 해보죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이녀석은 React 애플리케이션에서&amp;nbsp;서버 상태를 가져오고, 캐싱하고, 동기화하고, 업데이트하는 작업 등을 간단하게 만들어 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$ npm i react-query&lt;/code&gt; 로 설치 후 { QueryClient, QueryClientProvider } 를 가져옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;/* index.tsx */

//...
import { QueryClient, QueryClientProvider } from &quot;react-query&quot;;
const queryClient = new QueryClient();

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;QueryClientProvider client={queryClient}&amp;gt;
      &amp;lt;ThemeProvider theme={theme}&amp;gt;
        &amp;lt;App /&amp;gt;
      &amp;lt;/ThemeProvider&amp;gt;
    &amp;lt;/QueryClientProvider&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;lt;Coins/&amp;gt; 에서 코인 정보를 불러와 화면에 보여줄 때까지의 일들을 React Query에게 일임해 봅시다.&lt;/p&gt;
&lt;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;우선 api.ts를 만들어 API fetch와 관련된 기능을 몰아넣고 fetcher 함수로 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;/* api.ts */

export async function fetchCoins(){
  return await (await fetch('&amp;lt;https://api.coinpaprika.com/v1/coins&amp;gt;')).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;lt;Coins/&amp;gt;에서 useQuery 훅을 사용합니다. &lt;a href=&quot;https://react-query.tanstack.com/guides/queries&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;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리에 대한 고유식별자(A unique key)&lt;/li&gt;
&lt;li&gt;promise를 리턴하는 함수&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;/* Coins.tsx */

//...
import { useQuery } from &quot;react-query&quot;;
import { fetchCoins } from &quot;../api&quot;;

function Coins() {
	useQuery('allCoins', fetchCoins);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-06 at 5.58.20 PM.png&quot; data-origin-width=&quot;1078&quot; data-origin-height=&quot;944&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6r0JS/btrs3TGEMoo/X8xxR1vHwgMtLmwKNoqhoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6r0JS/btrs3TGEMoo/X8xxR1vHwgMtLmwKNoqhoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6r0JS/btrs3TGEMoo/X8xxR1vHwgMtLmwKNoqhoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6r0JS%2Fbtrs3TGEMoo%2FX8xxR1vHwgMtLmwKNoqhoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;438&quot; data-filename=&quot;Screen Shot 2022-02-06 at 5.58.20 PM.png&quot; data-origin-width=&quot;1078&quot; data-origin-height=&quot;944&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;useQuery의 리턴값은 쿼리에 대한 정보를 담고 있어요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;data : 쿼리로 넘어온 데이터&lt;/li&gt;
&lt;li&gt;isLoading : 쿼리 데이터가 아직 없고 fetch 중인지&lt;/li&gt;
&lt;li&gt;isError : 쿼리 중 에러가 발생했는지&lt;/li&gt;
&lt;li&gt;isSuccess : 쿼리 완료 후 데이터에 접근할 수 있는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 기존에 만들어뒀던 loading state를 isLoading으로, coin state를 data로 대체할 수 있겠네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;data는 기존대로 30개까지만 불러오고, 쿼리 실패 시 undefined가 될 수 있으므로 옵셔널 체이닝을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;clojure&quot;&gt;&lt;code&gt;{isLoading ? (
  &amp;lt;Loader&amp;gt;Loading...&amp;lt;/Loader&amp;gt;
) : (
	//...
)}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;{data?.slice(0,30).map()}
&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;이때 타입스크립트가 data가 뭐냐며 검수하러 들기 때문에 타입 지정을 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;const { isLoading, data } = useQuery&amp;lt;ICoin[]&amp;gt;('allCoins', fetchCoins);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 fetch API 과정을 짧은 코드로 단축할 수 있고, &lt;br /&gt;React Query가 쿼리 결과값을 캐싱하기 때문에 다시 불러올 필요가 없습니다. (쾌-적)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Query의 Devtools를 설치하면 관련된 내용들을 시각적으로 쉽게 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { ReactQueryDevtools } from 'react-query/devtools'
 
 function App() {
   return (
     &amp;lt;QueryClientProvider client={queryClient}&amp;gt;
       {/* The rest of your application */}
       &amp;lt;ReactQueryDevtools initialIsOpen={false} /&amp;gt;
     &amp;lt;/QueryClientProvider&amp;gt;
   )
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-06 at 9.58.22 PM.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B5ecQ/btrs3SADVGD/5yE3BC5AMtd5iBFR0hHbr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B5ecQ/btrs3SADVGD/5yE3BC5AMtd5iBFR0hHbr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B5ecQ/btrs3SADVGD/5yE3BC5AMtd5iBFR0hHbr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB5ecQ%2Fbtrs3SADVGD%2F5yE3BC5AMtd5iBFR0hHbr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;689&quot; data-filename=&quot;Screen Shot 2022-02-06 at 9.58.22 PM.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1662&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;&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;lt;Coin/&amp;gt; 컴포넌트를 React Query로 정리해 봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 API 호출을 api.tsx에서 관리하고...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const BASE_URL = `https://api.coinpaprika.com/v1`;

export async function fetchCoins() {
  return await (await fetch(`${BASE_URL}/coins`)).json();
}

export async function fetchCoinInfo(coinId: string) {
  return await (await fetch(`${BASE_URL}/coins/${coinId}`)).json();
}

export async function fetchCoinTickers(coinId: string) {
  return await (await fetch(`${BASE_URL}/tickers/${coinId}`)).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;useQuery로 불러옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, unique key를 배열 형태로 지정하고, 타입스크립트에게 혼나지 않게 타입을 알려줍시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아, 그리고 구조분해 할당으로 꺼내온 isLoading과 data의 이름을 각각 다르게 붙여줄 수도 있어요. &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#%EC%83%88%EB%A1%9C%EC%9A%B4_%EB%B3%80%EC%88%98_%EC%9D%B4%EB%A6%84%EC%9C%BC%EB%A1%9C_%ED%95%A0%EB%8B%B9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(참고)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;const { isLoading: infoLoading, data: infoData } = useQuery&amp;lt;InfoData&amp;gt;(
  ['info', coinId],
  () =&amp;gt; fetchCoinInfo(coinId)
);

const { isLoading: tickersLoading, data: tickersData } = useQuery&amp;lt;PriceData&amp;gt;(
  ['tickers', coinId],
  () =&amp;gt; fetchCoinTickers(coinId)
);
&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;ldquo;타입이 string | undefined인 녀석을 string 타입의 파라미터로 지정하려 들다니!&amp;rdquo;하고 시비... 아니, 지적을 하네요.&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;Screen Shot 2022-02-06 at 10.21.28 PM.png&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi1PjR/btrs2dMeu5f/HRRb0vZrTxKwkuIWHKpKkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi1PjR/btrs2dMeu5f/HRRb0vZrTxKwkuIWHKpKkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi1PjR/btrs2dMeu5f/HRRb0vZrTxKwkuIWHKpKkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi1PjR%2Fbtrs2dMeu5f%2FHRRb0vZrTxKwkuIWHKpKkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;139&quot; data-filename=&quot;Screen Shot 2022-02-06 at 10.21.28 PM.png&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;314&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;그 이유는 우리가 useParams 훅을 썼기 때문입니다.&lt;/p&gt;
&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;~과거회상~&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const { coinId } = useParams();&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;react-router-dom v6 이상인 경우, useParams() 만 쓰더라도 타입이 string | undefined 일 거라고 알아서 예상해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;~과거회상~&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;p data-ke-size=&quot;size16&quot;&gt;그러면 그냥 &amp;ldquo;그래그래 이 자리엔 string | undefined 타입이 들어올 거야&amp;rdquo;라고 fetcher 함수 내 파라미터 타입을 수정하거나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;export async function fetchCoinInfo(coinId: string | undefined) {
  return await (await fetch(`${BASE_URL}/coins/${coinId}`)).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;아니면 &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;non-null assertion&lt;/a&gt; 연산자를 써서 피연산자가 null이나 undefined가 아닐 거라고(=항상 값이 할당될 거라고) 주장할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;const { isLoading: infoLoading, data: infoData } = useQuery&amp;lt;InfoData&amp;gt;(
  ['info', coinId],
  () =&amp;gt; fetchCoinInfo(coinId!)
);
&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;추가로 특정 시간마다 리프레쉬 되도록 하려면, useQuery의 세번째 인자로 Object를 넘겨줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;const { isLoading: tickersLoading, data: tickersData } = useQuery&amp;lt;PriceData&amp;gt;(
	['tickers', coinId],
	() =&amp;gt; fetchCoinTickers(coinId),
	{
	  refetchInterval: 5000
	}
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 React Helmet도 써보죠! 얘는 문서의 head를 쉽게 관리할 수 있게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다는 건 &amp;lt;head&amp;gt;의 &amp;lt;title&amp;gt;을 현재 보고 있는 코인 이름으로 바꾸게 하는 것도 가능하단 거죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$ npm i react-helmet&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$ npm i --save-dev @types/react-helmet&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;import { Helmet } from &quot;react-helmet&quot;;

//...
return(
	&amp;lt;Helmet&amp;gt;
	  &amp;lt;title&amp;gt;
	    {state?.name ? state.name : loading ? 'MyCoins' : infoData?.name}
	  &amp;lt;/title&amp;gt;
	&amp;lt;/Helmet&amp;gt;
);
//...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Feb-07-2022 01-40-11.gif&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ptyIE/btrs72PBIKi/T45t27svyPs7jiJRjPCFsk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ptyIE/btrs72PBIKi/T45t27svyPs7jiJRjPCFsk/img.gif&quot; data-alt=&quot;문서의 제목을 바꿔봤습니다 :9&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ptyIE/btrs72PBIKi/T45t27svyPs7jiJRjPCFsk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ptyIE/btrs72PBIKi/T45t27svyPs7jiJRjPCFsk/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;450&quot; height=&quot;534&quot; data-filename=&quot;Feb-07-2022 01-40-11.gif&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;574&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;문서의 제목을 바꿔봤습니다 :9&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5-2-5. 차트 그리기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 &amp;lt;Chart&amp;gt; 컴포넌트 작업을 해줄 차례입니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 차트를 그릴 수 있게 정보를 받아옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;/* api.ts */

export async function fetchCoinHistory(coinId: string | undefined) {
  const endDate = Math.floor(Date.now() / 1000);
  const startDate = endDate - 60 * 60 * 24 * 7;
  return await (
    await fetch(
      `${BASE_URL}/coins/${coinId}/ohlcv/historical?start=${startDate}&amp;amp;end=${endDate}`
    )
  ).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;lt;Chart/&amp;gt;내에서 coinId 값을 받아와야 하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 &amp;lt;Outlet&amp;gt;으로 가져오는 중이여서 useOutletContext 훅으로 props를 전달받아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니면 그냥 &amp;lt;Chart/&amp;gt;에서 useParams로 아이디 값을 알아올 수도 있겠죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;/* Chart.tsx */

import { useQuery } from 'react-query';
import { useParams } from 'react-router-dom';
import { fetchCoinHistory } from '../api';
interface IHistorical {
  time_open: string;
  time_close: string;
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
  market_cap: number;
}

function Chart() {
  const { coinId } = useParams();
  const { isLoading, data } = useQuery&amp;lt;IHistorical[]&amp;gt;(['ohlcv', coinId], () =&amp;gt;
    fetchCoinHistory(coinId)
  );
  return &amp;lt;h1&amp;gt;Chart: {coinId}&amp;lt;/h1&amp;gt;;
}

export default Chart;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차트 라이브러리는 &lt;a href=&quot;https://apexcharts.com&quot;&gt;apexcharts&lt;/a&gt; 를 써봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$ npm install --save react-apexcharts apexcharts&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;capnproto&quot;&gt;&lt;code&gt;import ApexChart from &quot;react-apexcharts&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;/p&gt;
&lt;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;Feb-07-2022 01-42-52.gif&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p75UY/btrs3Vj75sE/RZthiAdyOOwoW57FXwOwX0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p75UY/btrs3Vj75sE/RZthiAdyOOwoW57FXwOwX0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p75UY/btrs3Vj75sE/RZthiAdyOOwoW57FXwOwX0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/p75UY/btrs3Vj75sE/RZthiAdyOOwoW57FXwOwX0/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;586&quot; height=&quot;568&quot; data-filename=&quot;Feb-07-2022 01-42-52.gif&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;568&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;sticker@2x.png&quot; data-origin-width=&quot;370&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n9Uz0/btrs3VqTzhe/JrRiywg7XO68ERADmqUtV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n9Uz0/btrs3VqTzhe/JrRiywg7XO68ERADmqUtV0/img.png&quot; data-alt=&quot;(c) LINE+&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n9Uz0/btrs3VqTzhe/JrRiywg7XO68ERADmqUtV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn9Uz0%2Fbtrs3VqTzhe%2FJrRiywg7XO68ERADmqUtV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;219&quot; data-filename=&quot;sticker@2x.png&quot; data-origin-width=&quot;370&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(c) LINE+&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;---&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022.07 타입스크립트로 담근 칸반 클론 with 노마드코더  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nana-like.github.io/react-iWork/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;https://nana-like.github.io/react-iWork/&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1657007626374&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;iWork&quot; data-og-description=&quot;&quot; data-og-host=&quot;nana-like.github.io&quot; data-og-source-url=&quot;https://nana-like.github.io/react-iWork/&quot; data-og-url=&quot;https://nana-like.github.io/react-iWork/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://nana-like.github.io/react-iWork/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nana-like.github.io/react-iWork/&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;iWork&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nana-like.github.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>Blog/Library</category>
      <author>나나 (nykim)</author>
      <guid isPermaLink="true">https://anneslab.tistory.com/108</guid>
      <comments>https://anneslab.tistory.com/108#entry108comment</comments>
      <pubDate>Sun, 13 Feb 2022 18:04:23 +0900</pubDate>
    </item>
  </channel>
</rss>