<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>프론트엔드 개발자의 기록 공간</title>
    <link>https://ghost4551.tistory.com/</link>
    <description>프론트엔드 위주의 개발 경험 및 기술 정리를 위한 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 20 May 2026 06:44:54 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>[리우]</managingEditor>
    <image>
      <title>프론트엔드 개발자의 기록 공간</title>
      <url>https://tistory1.daumcdn.net/tistory/2444459/attach/c627d437b6bb4f5080832010c64d6acc</url>
      <link>https://ghost4551.tistory.com</link>
    </image>
    <item>
      <title>Vue -&amp;gt; React 마이그레이션 성과 정리</title>
      <link>https://ghost4551.tistory.com/282</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;입사 직후 도메인도 모른 채 맡았던 D'CENT Wallet React 마이그레이션, 그 마지막 회고 글이 올라왔습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;▎Vue 2.7 &amp;rarr; React 19 전환 결과에 대한 성과를 숫자로 정리해봤습니다.  &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;새로운 스택을 익힌 것보다, 팀이 더 빠르고 안전하게 움직일 수 있는 기반을 만드는 일이 얼마나 중요한지 체감한 시간이었습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;옆에서 많이 도와주신 FE 팀원분들께 다시 한번 감사드립니다. &amp;zwj;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;  번들 &amp;amp; 전송&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;- 합계 26.0MB &amp;rarr; 20.9MB (-19%)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;- Gzip 전송 10.0MB &amp;rarr; 5.8MB (-42%)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;- CSS 796KB / 93개 파일 &amp;rarr; 134KB / 2개 파일 (-83%)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;⚙️ CI/CD&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;- 빌드 시간 5.1분 &amp;rarr; 2.4분 (-53%)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;- S3 배포 2.1분 &amp;rarr; 0.3분 (-86%)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;  안전망&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;- 테스트 파일 12개 &amp;rarr; 346개 (&amp;times;28.8)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;- Storybook 스토리 0개 &amp;rarr; 356개 (신설)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;  자세한 내용은 6편 읽기:&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;a href=&quot;https://medium.com/iotrustlab/%EC%9A%B0%EB%A6%AC%EB%8A%94-%EB%8D%94-%EB%B9%A0%EB%A5%B4%EA%B3%A0-%EB%8D%94-%EA%B0%80%EB%B2%BC%EC%9B%8C%EC%A1%8C%EC%8A%B5%EB%8B%88%EB%8B%A4-dcent-%EC%A7%80%EA%B0%91-react-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%B6%84%ED%88%AC%EA%B8%B0-6-fb38f984137c&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://medium.com/iotrustlab/%EC%9A%B0%EB%A6%AC%EB%8A%94-%EB%8D%94-%EB%B9%A0%EB%A5%B4%EA%B3%A0-%EB%8D%94-%EA%B0%80%EB%B2%BC%EC%9B%8C%EC%A1%8C%EC%8A%B5%EB%8B%88%EB%8B%A4-dcent-%EC%A7%80%EA%B0%91-react-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%EB%B6%84%ED%88%AC%EA%B8%B0-6-fb38f984137c&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>일기장</category>
      <category>react migration</category>
      <category>web3</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/282</guid>
      <comments>https://ghost4551.tistory.com/282#entry282comment</comments>
      <pubDate>Mon, 4 May 2026 10:59:42 +0900</pubDate>
    </item>
    <item>
      <title>AI 딸깍으로 수익화가 가능할까?</title>
      <link>https://ghost4551.tistory.com/281</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;요즘 SNS를 보면 AI를 활용해 수익화를 했다는 글을 쉽게 볼 수 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;저 역시 그런 글을 볼 때마다 &amp;ldquo;아, 미리 해볼 걸&amp;hellip;&amp;rdquo; 하고 그냥 넘기곤 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;331&quot; data-start=&quot;311&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그러다 문득 이런 생각이 들었습니다.&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-end=&quot;331&quot; data-start=&quot;311&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;&amp;ldquo;AI를 실무에서 쓰는 개발자인 나도, 정말 &amp;lsquo;딸깍&amp;rsquo;으로 돈을 벌 수 있을까?&amp;rdquo;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;451&quot; data-start=&quot;385&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마침 네이티브 앱 생태계도 경험해보고 싶었기 때문에 &lt;b&gt;간단한 앱을 만들어 직접 수익화를 실험해보기로 했습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;451&quot; data-start=&quot;385&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(프로그래밍 좀비 인프런 강의 영상을 참고했습니다 :)&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h1 data-end=&quot;465&quot; data-start=&quot;458&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기술 스택&lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;529&quot; data-start=&quot;467&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;저는 프론트엔드 개발자로 React를 사용하고 있어&amp;nbsp;&lt;b&gt;React Native&lt;/b&gt;를 선택했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;575&quot; data-start=&quot;531&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;555&quot; data-start=&quot;531&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;iOS / Android 동시 대응 가능&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;575&quot; data-start=&quot;556&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기존 React 경험 활용 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;641&quot; data-start=&quot;577&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;빠른 개발을 위해 &lt;b&gt;Expo&lt;/b&gt;를 사용했고 iOS 시뮬레이터와 Android 에뮬레이터 환경에서 개발했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;696&quot; data-start=&quot;643&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사이드 프로젝트에서는 완성도보다 &lt;b&gt;일단 빠르게 만들어보는 것&lt;/b&gt;을 목표로 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;696&quot; data-start=&quot;643&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;717&quot; data-start=&quot;703&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아이디어 구현과 AI 활용, 그리고 수익화 &lt;/span&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;앱 컨셉은 단순한 &lt;span style=&quot;text-align: start;&quot;&gt; &amp;nbsp;&lt;/span&gt;&lt;b&gt;반응 속도 측정 게임&lt;/b&gt;입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&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-end=&quot;223&quot; data-start=&quot;148&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아이디어와 사용자 시나리오는 &lt;b&gt;Claude와 대화를 통해 구체화&lt;/b&gt;했고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기능 구현 역시 AI의 도움을 받아 빠르게 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;223&quot; data-start=&quot;148&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;+다양한 국가에서 제공하기 위해 다국어 처리까지 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;223&quot; data-start=&quot;148&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(한국, 일본, 중국, 영어, 러시아 지원)&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;223&quot; data-start=&quot;148&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;294&quot; data-start=&quot;225&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개발을 하면서 느낀 점은 단순한 로직 기반 앱의 경우&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;AI를 활용하면 구현 속도가 상당히 빨라진다&lt;/b&gt;는 것이었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;440&quot; data-start=&quot;296&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;440&quot; data-start=&quot;296&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개발이 어느 정도 완료된 뒤에는 &lt;b&gt;수익화 방법&lt;/b&gt;을 고민했고,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Google의 광고 플랫폼인 &lt;b&gt;AdMob을 활용해 광고를 적용&lt;/b&gt;했습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 과정에서는 인프런 강의 &lt;b&gt;350개의 개인 앱을 만들어 월급의 7배 수익을 달성한 방법&lt;/b&gt;을 참고했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;440&quot; data-start=&quot;296&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(프로그래밍 좀비 감사합니다 :)&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1342&quot; data-start=&quot;1270&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;1360&quot; data-start=&quot;1349&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개발자 계정 비용&lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;1382&quot; data-start=&quot;1362&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 첫 번째 현실을 마주했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-end=&quot;1409&quot; data-start=&quot;1384&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Android (Google Play)&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1489&quot; data-start=&quot;1411&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1435&quot; data-start=&quot;1411&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;등록 비용: &lt;b&gt;$25 (1회 결제)&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1489&quot; data-start=&quot;1436&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 개인 개발자는&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;12명 이상의 테스터 + 14일 비공개 테스트&lt;/b&gt; 조건 필요&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1510&quot; data-start=&quot;1491&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Apple App Store&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1529&quot; data-start=&quot;1512&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1529&quot; data-start=&quot;1512&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;$99 / 연간 구독&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1576&quot; data-start=&quot;1531&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;실험 프로젝트였기 때문에&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번에는 &lt;b&gt;Android만 출시&lt;/b&gt;하기로 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1576&quot; data-start=&quot;1531&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;1598&quot; data-start=&quot;1583&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배포 과정에서 겪은 문제&lt;/span&gt;&lt;/h1&gt;
&lt;h2 data-end=&quot;1612&quot; data-start=&quot;1600&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1️⃣ 계정 인증&lt;/span&gt;&lt;/h2&gt;
&lt;p data-end=&quot;1657&quot; data-start=&quot;1614&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Google Play Console 계정 인증 과정이 생각보다 까다로웠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1686&quot; data-start=&quot;1659&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1670&quot; data-start=&quot;1659&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;주민등록등본 제출&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1678&quot; data-start=&quot;1671&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;신원 인증&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1686&quot; data-start=&quot;1679&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;주소 검증&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1702&quot; data-start=&quot;1688&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또한 앱 최초 등록 시에도&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1755&quot; data-start=&quot;1704&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1711&quot; data-start=&quot;1704&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;광고 설정&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1722&quot; data-start=&quot;1712&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;타겟 연령 설정&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1737&quot; data-start=&quot;1723&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개인정보 처리방침 등록&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1755&quot; data-start=&quot;1738&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;앱 아이콘 및 스토어 이미지&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1776&quot; data-start=&quot;1757&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;등 여러 정보를 준비해야 했습니다. 또한&amp;nbsp;앱을 업데이트할 때마다 &lt;b&gt;스토어 심사 과정도 다시 거쳐야 했습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1776&quot; data-start=&quot;1757&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1842&quot; data-start=&quot;1825&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2️⃣ 비공개 테스트 조건&lt;/span&gt;&lt;/h2&gt;
&lt;p data-end=&quot;1858&quot; data-start=&quot;1844&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가장 어려웠던 부분입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1867&quot; data-start=&quot;1860&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개인 개발자는&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1909&quot; data-start=&quot;1869&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1889&quot; data-start=&quot;1869&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;12명 이상의 테스터 확보&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;1909&quot; data-start=&quot;1890&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;14일 이상 테스트 유지&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1924&quot; data-start=&quot;1911&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;조건을 충족해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1966&quot; data-start=&quot;1926&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;지인과 커뮤니티를 통해 인원을 모았는데&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;중간에 조건을 충족하지 못하면 &lt;b&gt;다시 14일을 처음부터 시작해야 합니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2049&quot; data-start=&quot;1998&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;AI로 앱을 만드는 건 빠르지만 &lt;b&gt;스토어 배포 과정은 전혀 &amp;lsquo;딸깍&amp;rsquo;이 아니었습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2049&quot; data-start=&quot;1998&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2049&quot; data-start=&quot;1998&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이렇게 힘든 과정을 이겨내고 배포한 앱을 소개합니다.  &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qbw6A/dJMcadHSdli/I4NfuGMal6yiGO8gznKsZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qbw6A/dJMcadHSdli/I4NfuGMal6yiGO8gznKsZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qbw6A/dJMcadHSdli/I4NfuGMal6yiGO8gznKsZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqbw6A%2FdJMcadHSdli%2FI4NfuGMal6yiGO8gznKsZK%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;257&quot; height=&quot;115&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;2049&quot; data-start=&quot;1998&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://play.google.com/store/apps/details?id=com.minsu.reflexbattle&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://play.google.com/store/apps/details?id=com.minsu.reflexbattle&lt;/a&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h1 data-end=&quot;2060&quot; data-start=&quot;2056&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;느낀점&lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;2085&quot; data-start=&quot;2062&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번 실험을 통해 느낀 점은 명확했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2185&quot; data-start=&quot;2087&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2116&quot; data-start=&quot;2087&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;AI 덕분에 앱 개발 속도는 매우 빨라졌다&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;2154&quot; data-start=&quot;2117&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 &lt;b&gt;수익화는 기술보다 플랫폼 정책과 운영의 영역&lt;/b&gt;이었다&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;2185&quot; data-start=&quot;2155&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;진짜 허들은 &lt;b&gt;코드가 아니라 배포 프로세스&lt;/b&gt;였다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2205&quot; data-start=&quot;2187&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 무엇보다 재미있었던 순간은&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-end=&quot;2247&quot; data-start=&quot;2207&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;2247&quot; data-start=&quot;2209&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;AdMob에서 실제 수익이 발생하는 것을 확인했을 때였습니다.&lt;br /&gt;&lt;/b&gt;&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPX9kS/dJMcajafNYW/NQpvI3QRAAK77mknk4rik1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPX9kS/dJMcajafNYW/NQpvI3QRAAK77mknk4rik1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPX9kS/dJMcajafNYW/NQpvI3QRAAK77mknk4rik1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPX9kS%2FdJMcajafNYW%2FNQpvI3QRAAK77mknk4rik1%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;473&quot; height=&quot;297&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;2293&quot; data-start=&quot;2249&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;br /&gt;금액은 작았지만 &amp;ldquo;내 앱으로 돈이 들어오는 경험&amp;rdquo; 자체가 꽤 흥미로웠습니다. (100$ 이상이되어야 환급가능...  )&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2293&quot; data-start=&quot;2249&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2293&quot; data-start=&quot;2249&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또 하나 느낀 점은 &lt;b&gt;AI 시대에서 개발자의 역할 변화&lt;/b&gt;였습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2293&quot; data-start=&quot;2249&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예전에는 코드 구현 자체에 더 많은 비중이 있었다면,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;지금은 점점 &lt;b&gt;Software Engineer에서 Product Engineer로 역할이 확장되고 있는 느낌&lt;/b&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;603&quot; data-start=&quot;585&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코드를 잘 작성하는 것뿐 아니라,&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;670&quot; data-start=&quot;605&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;630&quot; data-start=&quot;605&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사용자에게 필요한 기능을 빠르게 만들고&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;647&quot; data-start=&quot;631&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가설을 빠르게 검증하고&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;670&quot; data-start=&quot;648&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;실제로 사용되는 제품을 만들어내는 것&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;717&quot; data-start=&quot;672&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;같은 &lt;b&gt;프로덕트 관점의 사고가 점점 더 중요해지고 있다는 생각이 들었습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2293&quot; data-start=&quot;2249&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;2307&quot; data-start=&quot;2300&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마무리 및 향후 계획 &lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;2821&quot; data-start=&quot;2790&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;단순한 궁금증에서 시작한 프로젝트였지만, 꽤 재미있는 경험이었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;236&quot; data-start=&quot;161&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;AI를 활용한 개발 방식부터 앱 스토어 배포 과정, 그리고 실제 수익화까지 직접 경험해보면서 개발자로서 많은 것을 배울 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;262&quot; data-start=&quot;238&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;앞으로는 몇 가지를 더 실험해볼 생각입니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;400&quot; data-start=&quot;264&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;319&quot; data-start=&quot;264&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배포 조건을 조금 더 쉽게 해결할 방법 탐색&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(1인 사업자 등록, 테스트 기기 운영 등)&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;355&quot; data-start=&quot;320&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;서버 및 인프라 공부를 병행해 조금 더 기능이 있는 앱 개발&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;400&quot; data-start=&quot;356&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;수익이 어느 정도 발생한다면 &lt;b&gt;Apple App Store 출시도 도전&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;432&quot; data-start=&quot;402&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;앞으로도 이런 작은 실험들을 계속하면서 AI 시대의 개발 방식을 직접 경험해보려고 합니다.  &lt;/span&gt;&lt;/p&gt;</description>
      <category>일기장</category>
      <category>1인 앱 개발</category>
      <category>1인개발</category>
      <category>AI 수익화</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/281</guid>
      <comments>https://ghost4551.tistory.com/281#entry281comment</comments>
      <pubDate>Wed, 4 Mar 2026 21:25:27 +0900</pubDate>
    </item>
    <item>
      <title>[프론트엔드 성능 최적화 가이드] 정리</title>
      <link>https://ghost4551.tistory.com/280</link>
      <description>&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;/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;비교적 짧게 정리해서 축약된 부분이 많으니 필요시 해당 키워드로 검색해서 따로 찾아보면 좋을 것 같습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소개&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 성능을 결정하는 요소는 크게 &lt;span style=&quot;color: #ee2323;&quot;&gt;로딩 성능&lt;/span&gt;과 &lt;span style=&quot;color: #ee2323;&quot;&gt;렌더링 성능&lt;/span&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;웹페이지에서 사용되는 이미지, css, 파일의 크기가 너무 크면 다운로드하는데 시간이 오래 걸림&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;렌더링 성능에 크게 영향을 주는 것은 자바스크립트 코드이다.&lt;/li&gt;
&lt;li&gt;코드를 얼마나 효율적으로 작성했느냐에 따라 화면이 그려지는 속도와 인터랙션의 자연스러운 정도가 달라진다.&lt;/li&gt;
&lt;li&gt;렌더링 성능을 개선하는 방법은 다양하지만, 브라우저의 동작 원리나 프레임워크의 라이프사이클 등 웹 개발 기본 지식을 이해를 통해 개선할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1장 블로그 서비스&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최적화 기법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 사이즈 최적화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 CDN&lt;/li&gt;
&lt;li&gt;적절한 이미지 사이즈로 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코드 분할 &amp;amp; 지연로딩 (lazy로딩)
&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;페이지/컴포넌트 지연로딩을 위해 lazy 로딩, suspense컴포넌트로 감싸기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;텍스트 압축 (js 파일 코드등을 압축 및 난독화)&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;불필요한 반복문 등의 코드 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lighthouse&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;First Contentful Paint(FCP) : 페이지가 로드될 때 브라우저가 DOM 콘텐츠의 첫 번째 부분을 렌더링하는데 걸리는 시간에 관한 지표 (페이지에 진입하여 첫 콘텐츠가 뜰때까지의 시간)&lt;/li&gt;
&lt;li&gt;Largest Contentful Pating(LCP) : 페이지가 로드될 떄 가장 큰 이미지나 파일 요소가 렌더링되기까지 걸리는 시간&lt;/li&gt;
&lt;li&gt;Time to Interactive(TTI) : 사용자가 페이지와 상호 작용이 가능한 시점까지 걸리는 시간(버튼 클릭 등)&lt;/li&gt;
&lt;li&gt;Cumulative Layout Shift(CLS) : 페이지 로드 과정에서 발생하는 예기치 못한 레이아웃 이동을 측정한 지표(이미지 요소가 늦게 렌더링되면서 기존 UI 요소의 위치나 크기가 순간적으로 변화는 현상)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2장 올림픽 통계 서비스 최적화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 렌더링 과정&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DOM+CSSOM &amp;gt; 렌더 트리 &amp;gt; 레이아웃 (요소의 위치나 크기 계산) &amp;gt; 페인트 &amp;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;리페인트시에는 레이아웃 단계를 건너뜀&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리플로우와 리페인트를 피하는 방법 &amp;rArr; transform, opacity 속성을 이용하면 해당 요소를 별도의 레이어로 분리하고 작업을 &lt;span style=&quot;color: #ee2323;&quot;&gt;GPU에 위임하여 처리&lt;/span&gt;함으로써 레이아웃 단계와 페인트 간계를 건너뛸 수 있다. 이를 &lt;span style=&quot;color: #ee2323;&quot;&gt;하드웨어 가속&lt;/span&gt; 이라고 함&lt;/li&gt;
&lt;li&gt;리플로우와 리페인트를 일으키는 width, height, color 등의 속성이 아닌 transfrom, opacity 속성을 이용하는 것이 GPU에 의해 처리되어 레이아웃 단계와 페인트 단계없이 스타일을 변경할 수 있기 때문에 애니메이션 성능이 더 좋다&lt;/li&gt;
&lt;/ul&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;Suspense 컴포넌트와 lazy함수를 불러와서 사용&lt;/li&gt;
&lt;li&gt;페이지 로드시 당장 필요없는 컴포넌트 코드가 번들에 포함되지 않아, 로드할 파일의 크기가 작아지고 초기 로딩 속도나 자바스크립트의 실행 타이밍이 빨라져서 화면이 더 빨리 표시된다는 장점&lt;/li&gt;
&lt;li&gt;하지만, 해당 컴포넌트 사용시 네트워크를 통해 해당 코드를 새로 로드해야 하며 로드가 완료되어야만 해당 컴포넌트를 사용할 수 있기 때문에 한계가 있다. 즉 해당 컴포넌트를 불러오기까지 약간의 &lt;span style=&quot;color: #ee2323;&quot;&gt;지연이 발생한다는 단점&lt;/span&gt;이 존재
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사전 로딩을 통해 해결 (모듈을 미리 로드하는 기법)&lt;/li&gt;
&lt;li&gt;하지만 어느시점에 모듈을 미리 로드해 둘지 애매함. (예를들어 클릭시 모달이 열리고 해당 컴포넌트 내에서 모듈이 필요해서 모달이 열리기전 모듈을 미리 로드해둔다했을때, 모달 클릭을 안하면 미리 로드하는 의미가 없기때문에)&lt;/li&gt;
&lt;li&gt;이때 고려할 수 있는 타이밍이 두 가지 존재
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 버튼 위로 마우스를 올렸을 때 클릭을 할 가능성이 있다는 것을 염두하고 모듈 로드
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;mouse hover와 같은 자바스크립트 이벤트를 통해 import 함수 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;최초에 페이지가 로드되고 모든 컴포넌트의 마운트가 끝난 시점에 로드
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;useEffect에서 모듈 로드&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
  const component = import('./component/ImageModal')
}, [])
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&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;h3 data-ke-size=&quot;size23&quot;&gt;이미지 사전 로딩&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지는 이미지가 화면에 그려지는 시점, 즉 html 또는 css에서 이미지를 사용하는 시점에 로드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;src가 할당되는 순간 이미지 로드 (data-src로 임의로 제공후 useEffect안에서 src 리소스 제공)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자바스크립트로 이미지를 직접 로드하는 방법 존재 &amp;rArr; 자바스크립트 Image 객체 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1735981105512&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 모달 컴포넌트와 이미지를 미리 로드

useEffect(() =&amp;gt; {
  const component = import('./component/ImageModal')
  
  const img = new Image()
  img.src = 'https://~~'
}, [])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3장 홈페이지 최적화&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이미지 지연 로딩&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 처음으로 사용자에게 보이는 콘텐츠인데 나중에 로드되면, 사용자가 첫 회면에서 오래 머물게 되므로 사용자 경험에 좋지 않다.&lt;/li&gt;
&lt;li&gt;이를 해결하기 위해 이미지 지연 로딩 기법이 존재 &amp;rArr; 뷰포트에 이미지가 표시될 때 이미지 리소를 로드하는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;스크롤 이벤트를 통해 스크롤 위치가 이미지 위치까지 도달했을 때 이미지 로드&lt;/span&gt; &amp;rArr; 스크롤 이벤트가 많이 발생 시키므로 브라우저의 메인 스레드에 무리가 간다. throttle 방식으로 개선할 수 있지만, 좋지 않은 방법&lt;/li&gt;
&lt;li&gt;Web API &lt;span style=&quot;color: #ee2323;&quot;&gt;Intersection Observer&lt;/span&gt; 이용 &amp;rArr; 특정 요소를 관찰(observe)하다가 스크롤 시, 해당 요소가 화면에 들어왔는지 아닌지 알려준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Intersection Observer 상세 보기&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Intersection Observer의 주요 구성 요소&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;root&lt;/b&gt;: 교차 여부를 판단할 기준 요소입니다. null이면 브라우저의 뷰포트를 기준으로 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;rootMargin&lt;/b&gt;: root의 경계를 조정할 수 있는 속성입니다. (CSS margin과 유사하게 작동)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;threshold&lt;/b&gt;: 관찰 대상이 root와 교차하는 비율(0~1 사이)을 지정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;callback&lt;/b&gt;: 조건이 충족되었을 때 실행되는 함수입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Intersection Observer 동작 원리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Intersection Observer의 내부 동작은 다음과 같습니다:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;(1) 브라우저의 Layout 단계와 함께 동작&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저는 화면을 렌더링할 때 &lt;span style=&quot;color: #ee2323;&quot;&gt;Layout(요소의 위치 계산), Paint, Composite&lt;/span&gt; 등의 단계를 거칩니다.&lt;/li&gt;
&lt;li&gt;Intersection Observer는 브라우저의 Layout 단계에서 &lt;b&gt;관찰 대상의 위치와 root의 위치&lt;/b&gt;를 계산합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;(2) 관찰 대상과 root의 교차 여부 확인&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저는 관찰 대상(target)과 root의 &lt;b&gt;Bounding Box&lt;/b&gt;(경계 박스)를 계산합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Bounding Box 계산&lt;/b&gt;: 각 요소의 getBoundingClientRect() 값을 통해 현재 위치를 계산합니다.&lt;/li&gt;
&lt;li&gt;그다음, &lt;b&gt;두 박스의 교차 영역을 구합니다&lt;/b&gt;. 이 교차 영역이 threshold 조건을 만족하는지 판단합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;(3) Intersection Ratio 계산&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Intersection Observer는 교차 영역의 비율을 계산합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예시: 교차 영역의 넓이 &amp;divide; 타겟 요소의 전체 넓이 = Intersection Ratio&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 비율이 threshold 조건을 만족하면 &lt;b&gt;callback 함수&lt;/b&gt;가 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;(4) 조건 충족 시 callback 실행&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관찰 대상이 root와 교차하는 상태(즉, threshold 조건)를 충족하면 Intersection Observer는 &lt;b&gt;callback 함수&lt;/b&gt;를 비동기적으로 호출합니다.&lt;/li&gt;
&lt;li&gt;교차 상태의 변경 여부를 IntersectionObserverEntry 객체를 통해 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Intersection Observer가 성능에 유리한 이유&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;비동기적 처리&lt;/b&gt;: Intersection Observer는 메인 스레드를 차단하지 않고 별도의 계산을 처리합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;레이아웃 재계산 최소화&lt;/b&gt;: 스크롤 이벤트를 지속해서 감지하는 대신 &lt;b&gt;브라우저가 최적화된 시점&lt;/b&gt;에 관찰 영역을 업데이트합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;하드웨어 가속 활용&lt;/b&gt;: 브라우저는 Intersection Observer의 내부 계산을 GPU 레벨에서 최적화할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 간단한 동작 흐름&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Intersection Observer가 생성될 때 &lt;b&gt;root, rootMargin, threshold&lt;/b&gt;가 설정됩니다.&lt;/li&gt;
&lt;li&gt;관찰할 대상(target) 요소를 Observer에 등록합니다.&lt;/li&gt;
&lt;li&gt;브라우저가 &lt;b&gt;Layout 단계&lt;/b&gt;에서 교차 여부를 계산하고 threshold 조건을 확인합니다.&lt;/li&gt;
&lt;li&gt;조건을 만족하면 callback 함수가 실행됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이미지 사이즈 최적화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 사이즈가 크면 다운로드에 많은 시간이 걸린다. 이미지를 사용하는 크기에 맞는 사이즈를 다운받아서 최적화를 할 수 있다.&lt;/li&gt;
&lt;li&gt;비트맵 이미지 포맷중 대표적인 포맷은 PNG, JPG(JPEG), WebP
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PNG : 무손실 압축 방식으로 원본을 훼손 없이 압축&lt;/li&gt;
&lt;li&gt;JPG(JPEG) : 압축 과정에서 정보 손실 발생. 하지만 그만큼 이미지를 더 작은 사이즈로 줄일 수 있다. 고화질이 필요한게 아니라면 일반적으로 사용&lt;/li&gt;
&lt;li&gt;WebP : 무손실 압축과 손실 압축을 모두 제공하는 최신 이미지 포맷. PNG, JPG에 비해 대단히 효율적으로 이미지 압축 가능. WebP 공식문서에 따르면 PNG 대비 26%, JPG 대비 25~34% 더 나은 효율이 있다고 함. 하지만 최신 이미지 파일 포맷이기 때문에 지원하지 않는 브라우저가 있기 때문에 지원율을 잘 확인해서 사용해야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;브라우저 호환성 문제가 존재. WebP로만 이미지 렌더링할 경우 특정 브라우저에서는 제대로 렌더링되지 않을 수 있다. 이런 문제를 해결하려면 단순 img태그만 이미지를 렌더링하면 안되며, picture 태그를 사용해야한다.
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;// webp가 우선적으로 렌더링. 브라우저가 지원하지 않을 경우 jpg 이미지 렌더링
&amp;lt;picture&amp;gt;
  &amp;lt;source srcset=&quot;some-image.webp&quot; type=&quot;image/webp&quot; /&amp;gt;
  &amp;lt;img src=&quot;other-image.jpg&quot; /&amp;gt;
&amp;lt;/picture&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&amp;lt;picture&amp;gt; 태그는 &amp;lt;img&amp;gt; 요소의 다중 이미지 리소스(multiple image resources)를 위한 컨테이너를 정의할 때 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;폰트 최적화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커스텀 폰트가 다운로드 되기전 기본폰트가 보여지다가 커스텀 폰트가 다운로드되어 폰트가 바뀌면서 깜박이는 모습을 개선하기 위해 폰트 최적화가 필요하다.&lt;/li&gt;
&lt;li&gt;폰트의 변화로 발생하는 현상을 FOUT, FOIT라 한다.&lt;/li&gt;
&lt;li&gt;폰트를 최적화하는 방법은 크게 두 가지가 있다. 폰트 적용 시점을 제어하는 방법, 폰트 사이즈를 줄이는 방법&lt;/li&gt;
&lt;li&gt;CSS의 font-display 속성을 이용하면 폰트가 적용되는 시점 제어 가능&lt;/li&gt;
&lt;li&gt;폰트 포맷별 파일크기 EOT &amp;gt; TTF/OTF &amp;gt; WOFF &amp;gt; WOFF2&lt;/li&gt;
&lt;li&gt;WOFF(Web Open Font Format) 웹을 위한 폰트 확장자 포맷 이용. 이 포맷은 TTF 폰트를 압축하여 웹에서 더욱 빠르게 로드하도록 만듦.&lt;/li&gt;
&lt;li&gt;브라우저 호환성 문제가 존재. &lt;span style=&quot;color: #ee2323;&quot;&gt;@font-face&lt;/span&gt; 우선순위 적용을 통해 호환이 안되는 포맷의 경우 대체 포맷의 폰트로 지정&lt;/li&gt;
&lt;li&gt;@font-face { font-family: 폰트명 src : url (경로.woff2) format('woff2'), url (~) format(경로.ttf) format('truetype') }&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;캐시 최적화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 캐시 : 메모리에 저장하는 방식(RAM),&lt;/li&gt;
&lt;li&gt;디스크 캐시 : 파일 형태로 디스크에 저장하는 방식&lt;/li&gt;
&lt;li&gt;http 응답헤더에 Cache-Control 헤더의 유무에 따라 브라우저 캐시가 적용됨. 이는 서버에서 설정되며 옵션에 따라 해당 리소스를 어람나 캐시할지 판단&lt;/li&gt;
&lt;li&gt;Cache-Control
&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;no-cache : 캐시 사용하기 전 서버에 검사 후 사용 (max-age: 0과 동일)&lt;/li&gt;
&lt;li&gt;no-store: 캐시 사용안함&lt;/li&gt;
&lt;li&gt;max-age: 캐시의 유효 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 유효 시간이 만료되면 브라우저는 기존에 캐시된 리소스를 그대로 사용할지, 새로 요청해야하는지 서버에 확인합니다. 이때 변경되지 않았다면 서버에서는 304 상태 코드를 응답으로 보냅니다.&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; 서버에서는 응답 헤더에 있는 Etag값과 최신 리소스의 Etag 값을 비교하여 판단합니다. 같을 경우 304 응답 코드를, 다를 경우 새로운 Etag값과 최신 리소스를 함께 브라우저로 반환합니다.&lt;/li&gt;
&lt;/ul&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;일반적으로 HTML 파일에는 no-cache 설정을 적용합니다. HTML이 캐시되면 캐시된 HTML에서 이전 버전의 자바스크립트나 CSS를 로드하게 되므로 캐시 시간 동안 최신 버전의 웹 서비스를 제공하지 못합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;불필요한 CSS 제거&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Lighthouse Opportunities 섹션의 &amp;ldquo;Reduce unused CSS&amp;rdquo; 항목을 보면 사용하지 않는 CSS를 제거하면 얼마나 줄일 수 있는지 용량을 알려줌&lt;/li&gt;
&lt;li&gt;개발자 도구에서 Coverage 패널에서는 자바스크립트 및 CSS 리소스에서 실제로 실행한느 코드가 얼마나 되는지 비율을 알려줍니다. 따라서 불필요한 코드가 얼마나 있는지 확인 할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PurgeCSS&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용하지 않는 CSS 코드 제거를 위한 툴 중 하나로 파일에 들어있는 모든 키워드를 추출하여 해당 키워드를 이름으로 갖는 CSS 클래스만 남기고 나머지 클래스는 지우는 형식으로 최적화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4장 이미지 갤러리 최적화&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;레이아웃 이동 피하기 &lt;b&gt;CLS(Cumulative Layout Shift)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화면상의 요소 변화로 레이아웃이 갑자기 밀리는 현상을 말합니다. 예를들어 이미지가 늦게 로드될 때 해당영역에 있던 UI 요소들을 밀어내면서 이미지가 화면에 그려지는 현상입니다.&lt;/li&gt;
&lt;li&gt;Lighthouse에서 레이아웃 이동이 얼마나 발생하는지를 나타내는 지표로 CLS 성능 지표 점수가 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CLS는 0부터 1의 값을 가지며, 레이아웃 이동이 발생하지 않은 경우 0, 발생할수록 높은 점수를 가집니다. 권장하는 점수로는 0.1 이하입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Performance 탭의 Experience 섹션을 보면 Layout Shift가 표시되고 해당영역에 커서를 올려놓으면 레이아웃 이동을 유발한 요소를 표시해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;layoutshift.png&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QQIAZ/btsLEb2jjTa/E7jTZbiYCi0FZCqgWLLnW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QQIAZ/btsLEb2jjTa/E7jTZbiYCi0FZCqgWLLnW1/img.png&quot; data-alt=&quot;https://web.dev/articles/debug-layout-shifts?hl=ko&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QQIAZ/btsLEb2jjTa/E7jTZbiYCi0FZCqgWLLnW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQQIAZ%2FbtsLEb2jjTa%2FE7jTZbiYCi0FZCqgWLLnW1%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;604&quot; height=&quot;331&quot; data-filename=&quot;layoutshift.png&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://web.dev/articles/debug-layout-shifts?hl=ko&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사이즈가 미리 정의되지 않은 이미지 요소&lt;/li&gt;
&lt;li&gt;사이즈가 미리 정의되지 않은 광고 요소&lt;/li&gt;
&lt;li&gt;동적으로 삽입된 콘텐츠&lt;/li&gt;
&lt;li&gt;웹 폰트(FOIT, FOUT)&lt;/li&gt;
&lt;/ul&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 크기를 비율로 설정하는 방법은 padding으로 박스를 만든 뒤, 그 안에 이미지를 absolute로 띄우는 방식이 있습니다. (aspect-ratio css속성도 존재)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;div&amp;gt;
  &amp;lt;img src=&quot;..&quot;/&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;div {
  position: relative;
  width: 160px;
  padding-top: 56.25%  /* 16:9 비율 */
}

img {
  postion: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발지식</category>
      <category>프론트엔드 성능 최적화 가이드</category>
      <category>프론트엔드 성능 최적화 가이드 정리</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/280</guid>
      <comments>https://ghost4551.tistory.com/280#entry280comment</comments>
      <pubDate>Sat, 4 Jan 2025 18:01:52 +0900</pubDate>
    </item>
    <item>
      <title>[yapp 24기 지원&amp;amp;활동 후기]</title>
      <link>https://ghost4551.tistory.com/279</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;yapp 24기 활동이 끝난 시점에 늦었지만, 지원하게 된 계기와 지원 과정부터 활동 과정까지의 후기를 남길려고 합니다.&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;현재 저는 프론트엔드 개발자로 약 1년 반 동안 현업에서 일하고 있습니다. 그동안 많은 실수와 성공을 겪으며 성장했지만, 대부분의 직장인들이 그렇듯, 시간이 지나면서 반복적인 업무에 익숙해지게 되었습니다. 특히, 팀의 특성상 레거시 시스템을 다루거나 단순한 작업을 처리하는 경우가 많았습니다. 그 결과, Next.js와 같은 최신 기술을 학습해도 실무에 적용할 기회는 많지 않았습니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;yapp 24기 Web 파트 지원후기&lt;/h2&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;h3 data-ke-size=&quot;size23&quot;&gt;자기소개서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문항이 7개로 회사 지원서보다 많습니다.... 동아리가 너무 빡세요...흙흙&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbecdd;&quot; data-token-index=&quot;0&quot;&gt;1. [공통 질문] 지원자께서 세상의 문제를 해결하기 위해 오너십을 가지고 노력한 경험을 구체적으로 이야기 해주세요. (800자 이내)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 솔직히 제일 어려웠습니다... 세상의 문제라니...지금까지 했던 프로젝트 중 그럴싸해보이는 것을 스토리텔링해서 적었습니다.&lt;span style=&quot;background-color: #fbecdd;&quot; data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbecdd;&quot; data-token-index=&quot;0&quot;&gt;2. [공통 질문] PM, 디자이너와 협업한 프로젝트에서 발생한 이슈 상황을 어떻게 소통해서 해결하셨는지 구체적으로 작성해주세요.(800자 이내)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbecdd;&quot; data-token-index=&quot;0&quot;&gt;3. YAPP 지원동기는 무엇이며 동아리 활동을 통해 얻고싶은 점 또는 기대하는 점에 대해 말씀해주세요.(500자 이내)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbecdd;&quot; data-token-index=&quot;0&quot;&gt;4. 개발 경력 혹은 경험에 대해 작성해 주세요. 프로젝트 이름, 기간, 본인의 역할, 성과를 포함하여 작성해 주세요.(10,000자 이내)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbecdd;&quot; data-token-index=&quot;0&quot;&gt;5. 개발 과정에서 기술적인 어려움을 겪었던 적은 없었나요? 위 프로젝트 중 기억에 남는 것을 하나 선정해 어떠한 과정으로 해당 문제를 해결했는지 설명해주세요.(700자 이내)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbecdd;&quot; data-token-index=&quot;0&quot;&gt;6. 프로젝트를 하면서 팀원과 겪었던 갈등 상황이 있었나요? 있었다면 이를 어떤 방식으로 해결했는지 설명해 주세요.(700자 이내)&lt;/span&gt;&lt;span style=&quot;background-color: #fbecdd;&quot; data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbecdd;&quot; data-token-index=&quot;0&quot;&gt;7. (재직자 or 이직 준비자 선택시) 회사를 다니며 회사 일 외로 열정적으로 참여하고 있거나 했던 활동(대외활동, 공모전, 개인 활동 등)이 있나요 ? 있다면 기간과 함께 활동이유와 본인이 어떤 경험을하고 느꼈는지 이야기 해주세요.(800자 이내)&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;/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;면접은 2대 1로 약 40분 동안 진행되었습니다. 직무와 관련된 질문은 한 운영진이 주도적으로 질문하셨고, 인적성과 관련된 질문은 다른 운영진이 맡아 진행하셨습니다.&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;: 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;&lt;b&gt;기술 질문&lt;/b&gt;: 일반적인 기술 질문으로는 DOM이 렌더링되는 과정, 이벤트 루프, React 의존성 배열, useEffect의 실행 주기 등 기본적인 내용이었습니다. 또한, 이력서와 관련된 질문이 많았는데 제어 컴포넌트와 비제어 컴포넌트의 차이점 및 장단점, 개발한 프로젝트의 설명과 이에 대한 꼬리 질문 등이 이어졌습니다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pxZaq/btsJSaEDOT2/oc6tHQHZJvMLFRyJSpKQtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pxZaq/btsJSaEDOT2/oc6tHQHZJvMLFRyJSpKQtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pxZaq/btsJSaEDOT2/oc6tHQHZJvMLFRyJSpKQtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpxZaq%2FbtsJSaEDOT2%2Foc6tHQHZJvMLFRyJSpKQtk%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;1732&quot; height=&quot;810&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;810&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;팀 빌딩 과정은 마치 사랑의 작대기(?)처럼 매칭이 이루어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 구체적으로 설명하자면, 개발 직군의 경우 운영진들이 사전에 받은 자기소개서, 면접, 경력 등의 정보를 토대로 밸런스를 맞춰 팀을 구성합니다. (예를 들어, 웹 개발자가 6명 있고 3팀이 필요하면, 각 팀에 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;이후 PM과 디자이너들은 서로 어필하는 과정을 거쳐, 1순위부터 n순위까지 선호도를 측정한 후, 그 결과를 바탕으로 운영진들이 PM과 디자이너를 매칭합니다. 이후 PM+디자이너팀이 원하는 개발자 팀(웹팀, 서버팀, 모바일 팀)을 투표하고&lt;br /&gt;반대로 개발자 직군팀도 원하는 PM+디자이너팀을 투표해서 결과를 토대로 최종 팀이 완성됩니다.&lt;/p&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;h2 data-ke-size=&quot;size26&quot;&gt;활동 후기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년 6월부터 9월까지 약 4개월 동안 진행된 과정은 꽤 긴 시간이라고 생각했지만, 실제로 개발에 몰두할 수 있는 기간은 많지 않았습니다. 따라서 프로젝트의 규모에 따라 일정이 여유로울 수도, 빡빡할 수도 있겠다는 생각이 들었습니다. 이 때문에 팀의 리소스와 각 구성원이 할애할 수 있는 시간을 잘 파악해 프로젝트 또는 MVP의 규모를 적절히 조정하는 것이 중요하다고 느꼈습니다.&lt;/p&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;저 역시 팀원들에게 미안한 순간이 많았습니다. 디자인과 서버 작업을 빠르게 진행해주셨지만, 직장인이라는 이유로 하루에 투자할 수 있는 시간이 많지 않았고, 특히 야근이 있는 날은 아예 작업을 손대지 못하는 경우도 있었습니다. 게다가 Next.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;가장 큰 어려움은 같은 직군의 팀원이 프로젝트 중간에 개발을 그만두겠다고 선언하며 잠적한 일이었습니다(!!!). 결국 웹 개발 전반을 혼자 책임져야 했고, 그로 인한 부담감과 일정에 맞추지 못하는 미안함이 컸습니다.&lt;/p&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;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;최우수상(2등)&lt;/span&gt;&lt;/b&gt;을 수상하는 기쁨을 누릴 수 있었습니다. (장하다, MAFOO팀!)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;g.jpeg&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;1512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lst9F/btsJR0B9QHr/E71cXsMeXNHFQ1Y0kKAAm1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lst9F/btsJR0B9QHr/E71cXsMeXNHFQ1Y0kKAAm1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lst9F/btsJR0B9QHr/E71cXsMeXNHFQ1Y0kKAAm1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flst9F%2FbtsJR0B9QHr%2FE71cXsMeXNHFQ1Y0kKAAm1%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;588&quot; height=&quot;1512&quot; data-filename=&quot;g.jpeg&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;1512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저희팀 서비스가 궁금하다면....&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;yapp 소개페이지: &lt;a href=&quot;https://www.yapp.co.kr/project/24th/mafoo&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.yapp.co.kr/project/24th/mafoo&lt;/a&gt;&lt;/p&gt;
&lt;div data-message-id=&quot;8426e758-2c36-422a-b6cc-fff9dafd87ca&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mafoo 서비스: &lt;a href=&quot;https://mafoo.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mafoo.kr/&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;저는 아직까지 기술에 대한 탐구를 더 진행하고 싶은 마음이 컸고, 또 많은 시간 투자를 하기 어려운 상황으로 저는 임시적으로 하차하기로 결정했지만, 남은 팀원들은 계속 진행하기로 했습니다. (현재 프론트 부재로 인원 구하고 있으니 관심있는 분은 연락주세요...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 경험상, 다음과 같은 분들에게 YAPP을 추천드립니다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 서비스를 만들어 꾸준히 개선해나가고 싶으신 분. (활동이 끝난 후에도 프로젝트를 지속하는 경우가 많습니다)&lt;/li&gt;
&lt;li&gt;사용자들의 반응에 즉각적으로 대응해보는 경험을 원하시는 분.&lt;/li&gt;
&lt;li&gt;다양한 직군의 사람들과 교류하며 시야를 넓히고 싶으신 분.&lt;/li&gt;
&lt;li&gt;이력서에 의미 있는 경험을 추가하고 싶으신 분.&lt;/li&gt;
&lt;li&gt;새로운 도전을 경험해보고 싶으신 분.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, 다음과 같은 분들에게는 YAPP을 추천드리지 않습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 활동을 병행하고 있거나, 시간을 많이 투자할 수 없는 상황인 분.&lt;/li&gt;
&lt;li&gt;능동적으로 열정을 쏟아 활동하는 것을 원치 않으시는 분.&lt;/li&gt;
&lt;li&gt;여러 가지 새로운 기술을 공부하며 적용해보고 싶은 분. (YAPP에서는 빠르게 개발해야 하므로 기존에 알고 있는 기술을 주로 활용하게 됩니다)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>일기장</category>
      <category>yapp 24기</category>
      <category>yapp 24기 웹</category>
      <category>yapp 24기 후기</category>
      <category>yapp 지원후기</category>
      <category>ypp 활동후기</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/279</guid>
      <comments>https://ghost4551.tistory.com/279#entry279comment</comments>
      <pubDate>Tue, 1 Oct 2024 16:59:06 +0900</pubDate>
    </item>
    <item>
      <title>[FE_Roadmap] Web Components</title>
      <link>https://ghost4551.tistory.com/278</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Web Components&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;웹 컴포넌트는 그 기능을 나머지 코드로부터 캡슐화하여 재사용 가능한 커스텀 엘리먼트를 생성하고 웹 앱에서 활용할 수 있도록 해주는 다양한 기술들의 모음입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;웹 표준 기술로 모든 브라우저에서 동작하고 플랫폼 간 호환성을 높여줍니다. (미지원 브라우저 제외 및 구버전 브라우저에서는 polyfill을 사용하여 지원 가능)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다양한 프레임워크나 라이브러리에서 Web Components를 사용할 수 있기 때문에, 기술 스택에 구애받지 않고 사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특징&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;Custom elements&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;html tag 자체를 커스텀하게 만들고 browser 사용할 수 있도록 만들어 줍니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;code&gt;customElements.define&lt;/code&gt; 메소드를 이용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Shadow DOM&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다른 DOM과의 분리를 통해 DOM을 캡슐화합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;shadow dom 외부의 js는 접근이 안된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;shadow boundary의 style은 외부로 영향을 미치지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;브라우저 미 지원시 polyfill 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;code&gt;shadowRoot.querySelector()&lt;/code&gt; 메소드를 이용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image-2.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyAxz3/btsHKbsXj5C/gooXDRslr7gAr2KdPYd88K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyAxz3/btsHKbsXj5C/gooXDRslr7gAr2KdPYd88K/img.png&quot; data-alt=&quot;Shadow Dom&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyAxz3/btsHKbsXj5C/gooXDRslr7gAr2KdPYd88K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyAxz3%2FbtsHKbsXj5C%2FgooXDRslr7gAr2KdPYd88K%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;643&quot; height=&quot;296&quot; data-filename=&quot;image-2.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Shadow Dom&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;HTML templates&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 과 &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; 엘리먼트는 렌더링된 페이지에 나타나지 않는 마크업 템플릿을 작성할 수 있게 해줍니다. 그 후, 커스텀 엘리먼트의 구조를 기반으로 여러번 재사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;code&gt;content.cloneNode(true)&lt;/code&gt; 메소드를 이용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Declarative Shadow DOM&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;내용이 다소 많아 잘 정리된 블로그로 대체합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ui.toast.com/posts/ko_20201007&quot;&gt;https://ui.toast.com/posts/ko_20201007&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;lifecycle&lt;/span&gt;&lt;/h4&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class MyCustomElement extends HTMLElement {
  conntectedCallback() {
    console.log(&quot;연결 완료&quot;)
  }
  disconnectedCallback() {
    console.log(&quot;연결 해제 완료&quot;)
  }
  adoptedCallback() {
    console.log(&quot;Element가 다른 page로 이동 하였습니다.&quot;)
  }
  attributeChangedCallback(name, oldValue, newValue) {
    console.log(&quot;name 이 변경 되었습니다.&quot;)
  }

  static get observedAttributes() {
    // 변경을 관찰하고자 하는 attribute를 나열한다.
    // 아래 반환값들이 변경되면 `attributeChangedCallback` callback이 호출된다.
    return [&quot;autofocus&quot;, &quot;disabled&quot;, &quot;form&quot;, &quot;value&quot;]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;connectedCallback&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;요소가 DOM에 추가되면 메서드가 트리거됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리소스를 가져오고, 설정 코드를 실행하거나 템플릿을 렌더링할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;code&gt;react useEffect와 비슷한듯? 대신 렌더링 시점과 DOM 추가 시점이 다른 듯..&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class MyCustomElement extends HTMLElement {
  connectedCallback() {
    console.log(&quot;connected&quot;)
  }
}

customElements.define(&quot;my-custom-element&quot;, MyCustomElement)

const myCustomElement = new MyCustomElement()

document.body.appendChild(myCustomElement)
document.body.appendChild(myCustomElement)

// result:
// 'connected'
// 'connected'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;disconnectedCallback&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;요소가 DOM에서 제거될 때 트리거됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;DOM 이벤트 구독 취소&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;간격 타이머 중지&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;글로벌 또는 애플리케이션 서비스에 등록된 모든 콜백 등록 취소&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;code&gt;react useEffect의 return과 비슷한듯?&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class MyCustomElement extends HTMLElement {
  disconnectedCallback() {
    console.log(&quot;disconnected from the DOM&quot;)
  }
}

customElements.define(&quot;my-custom-element&quot;, MyCustomElement)

document.querySelector(&quot;my-custom-element&quot;).remove() // 'disconnected from the DOM'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;attributeChangedCallback(attrName, oldVal, newVal)&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;name: 속성의 이름을 나타냅니다. oldValue: 속성의 이전 값을 나타냅니다. newValue: 속성의 새 값을 나타냅니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;attributeChangedCallback속성이 추가, 제거, 업데이트 또는 교체되거나 구성 요소 인스턴스가 업그레이드될 때 트리거됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추적할 속성은 static get observedAttributes속성 이름 배열을 반환하는 메서드에 지정됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;code&gt;react 의존성 배열 deps와 비슷한듯?&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;&amp;lt;my-custom-element
  prop1=&quot;foo&quot;
  prop2=&quot;bar&quot;
  prop3=&quot;baz&quot;&amp;gt;
&amp;lt;/my-custom-element&amp;gt;

...
class MyCustomElement extends HTMLElement {
  static get observedAttributes() {
    return [&quot;prop1&quot;, &quot;prop2&quot;, &quot;prop3&quot;]
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log(
      `${name}'s value has been changed from ${oldValue} to ${newValue}`,
    )
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;adoptedCallback()&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;&lt;code&gt;&amp;lt;iframe/&amp;gt;&lt;/code&gt; iframe과 같은 외부 요소에서 사용됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 일이 발생하면 adoptedCallback수명 주기 후크가 트리거됩니다. 이를 사용하여 소유자 문서, 기본 문서 또는 기타 요소와 상호 작용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;실제 사용 예시&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;카운터 구성 요소에는 다음이 포함됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 값을 증가시키는 증가 버튼&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 값을 감소시키는 감소 버튼&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 값을 표시하는 레이블&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot;value&quot; 속성을 통해 기본값을 설정할 수 있어야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;const template = document.createElement(&quot;template&quot;)
template.innerHTML = `
  &amp;lt;button id=&quot;increaseBtn&quot;&amp;gt;+&amp;lt;/button&amp;gt;
  &amp;lt;span id=&quot;label&quot;&amp;gt;&amp;lt;/span&amp;gt;
  &amp;lt;button id=&quot;decreaseBtn&quot;&amp;gt;-&amp;lt;/button&amp;gt;
`

export class CounterComponent extends HTMLElement {
  // define the observedAttributes array
  static get observedAttributes() {
    return [&quot;value&quot;]
  }

  // define getters and setters for attributes
  get value() {
    return this.getAttribute(&quot;value&quot;)
  }

  set value(val) {
    if (val) {
      this.setAttribute(&quot;value&quot;, val)
    } else {
      this.removeAttribute(&quot;value&quot;)
    }
  }

  // DOM 요소 참조 변수
  $increaseButton
  $decreaseButton
  $label

  constructor() {
    super()

    // Shadow DOM 사용
    this.attachShadow({ mode: &quot;open&quot; })
    this.shadowRoot.appendChild(template.content.cloneNode(true))

    // DOM 요소에 대한 참조를 설정합니다.
    this.$increaseButton = this.shadowRoot.querySelector(&quot;#increaseBtn&quot;)
    this.$decreaseButton = this.shadowRoot.querySelector(&quot;#decreaseBtn&quot;)
    this.$label = this.shadowRoot.querySelector(&quot;#label&quot;)
  }
  connectedCallback() {
    // 양쪽 버튼에 이벤트 리스너 추가
    // 리스너의 콜백에 &quot;this&quot;를 바인딩하여 컴포넌트의 스코프를 연결합니다.
    this.$increaseButton.addEventListener(&quot;click&quot;, this._increase.bind(this))
    this.$decreaseButton.addEventListener(&quot;click&quot;, this._decrease.bind(this))
  }
  disconnectedCallback() {
    // 양쪽 버튼에서 이벤트 리스너 제거
    this.$increaseButton.removeEventListener(&quot;click&quot;, this._increase.bind(this))
    this.$decreaseButton.removeEventListener(&quot;click&quot;, this._decrease.bind(this))
  }

  attributeChangedCallback(name, oldValue, newValue) {
    this.$label.innerHTML = newValue
  }

  adoptedCallback() {
    console.log(&quot;I am adopted!&quot;)
  }

  _increase() {
    const value = +this.value
    this.value = String(value)
  }

  _decrease() {
    const value = +this.value
    this.value = String(value)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a href=&quot;https://ultimatecourses.com/blog/lifecycle-hooks-in-web-components&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ultimatecourses.com/blog/lifecycle-hooks-in-web-components&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;브라우저 호환성&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;웹 컴포넌트는 기본적으로 Firefox (버전 63), Chrome, 및 Opera 에서 지원됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Safari 는 많은 웹 컴포넌트 기능을 지원하지만 위 브라우저들만큼은 아닙니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Edge 는 구현 작업중입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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/sY12u/btsHMNpUQyp/vAiKfF84P6oLCImgnV7Wq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sY12u/btsHMNpUQyp/vAiKfF84P6oLCImgnV7Wq1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;910&quot; data-filename=&quot;image-3.png&quot; style=&quot;width: 33.9337%; margin-right: 10px;&quot; data-widthpercent=&quot;34.74&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sY12u/btsHMNpUQyp/vAiKfF84P6oLCImgnV7Wq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsY12u%2FbtsHMNpUQyp%2FvAiKfF84P6oLCImgnV7Wq1%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;986&quot; height=&quot;910&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcb0WS/btsHLNRH4Z6/X3yeSKTFdi11LRbgtkaCpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcb0WS/btsHLNRH4Z6/X3yeSKTFdi11LRbgtkaCpk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;960&quot; data-filename=&quot;image-4.png&quot; style=&quot;width: 32.1664%; margin-right: 10px;&quot; data-widthpercent=&quot;32.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcb0WS/btsHLNRH4Z6/X3yeSKTFdi11LRbgtkaCpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcb0WS%2FbtsHLNRH4Z6%2FX3yeSKTFdi11LRbgtkaCpk%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;986&quot; height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgz6ao/btsHLxVSmv0/KOlS64eW8avoQbTVbmOeD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgz6ao/btsHLxVSmv0/KOlS64eW8avoQbTVbmOeD1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;978&quot; data-filename=&quot;image-5.png&quot; style=&quot;width: 31.5743%;&quot; data-widthpercent=&quot;32.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgz6ao/btsHLxVSmv0/KOlS64eW8avoQbTVbmOeD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgz6ao%2FbtsHLxVSmv0%2FKOlS64eW8avoQbTVbmOeD1%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;986&quot; height=&quot;978&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>개발지식</category>
      <category>Frontend Roadmap</category>
      <category>frontend roadmap study</category>
      <category>web components</category>
      <category>web components란</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/278</guid>
      <comments>https://ghost4551.tistory.com/278#entry278comment</comments>
      <pubDate>Sun, 2 Jun 2024 18:47:14 +0900</pubDate>
    </item>
    <item>
      <title>requestAnimationFrame 사용법</title>
      <link>https://ghost4551.tistory.com/277</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;requestAnimationFrame이 무엇인지 알아보기 전에 다음과 같은 이슈를 만난적이 있나요?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(requestAnimationFrame 개념을 바로 습득하고 활용 사례를 보고 싶다면, requestAnimationFrame 타이틀로 바로 이동하셔도 무방합니다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useEffect 비동기로 인한 이슈&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개요 : 사용자에게 화면을 제공한 후 어떠한 로직을 처리하고 싶어서 useEffect에 해당 로직을 작성했지만,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;화면 표시전에 로직이 실행되어 (컴포넌트 렌더링과 화면에 노출 시점의 간극으로 인해) 문제가 되었던 적이 있나요?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;제가 겪었던 이슈를 말씀드리겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;제가 원했던 경우는 사용자에게 화면을 제공한 후 searchParams값이 있으면 alert 띄우기라는 단순한 조건이었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이를 위해 다음과 같은 로직을 작성했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1e1e; color: #d4d4d4;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;span style=&quot;color: #d4d4d4;&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;pre id=&quot;code_1716712852811&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const [searchParams] = useSearchParams()
  const isAlert = searchParams.has(&quot;keyword&quot;)

  useEffect(() =&amp;gt; {
    if (isAlert) {
      alert(&quot;requestAnimationFrame&quot;)
    }
  }, [isAlert])&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;alert1.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EvZOa/btsHCuyjgFE/6TKot7kZgjaKPNddeCaO81/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EvZOa/btsHCuyjgFE/6TKot7kZgjaKPNddeCaO81/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EvZOa/btsHCuyjgFE/6TKot7kZgjaKPNddeCaO81/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/EvZOa/btsHCuyjgFE/6TKot7kZgjaKPNddeCaO81/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;459&quot; data-filename=&quot;alert1.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;459&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;color: #000000;&quot;&gt;이미지 순서를 보면 다음과 같습니다. 컴포넌트 마운트 -&amp;gt; useEffect 실행 -&amp;gt; alert 실행 -&amp;gt; dom paint과정으로 실제 화면에 렌더링&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;브라우저에서는 alert 함수 실행시 자바스크립트를 blocking합니다.&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;span style=&quot;color: #000000;&quot;&gt;즉 위와 같이 되는 이유는 useEffect는 브라우저가 DOM 업데이트를 완료한 후 실행되지만, 이는 비동기적으로 실행되기 때문에 정확한 보장이 되지 않기 때문입니다.&lt;/span&gt; (&lt;a href=&quot;https://velog.io/@lky5697/unintentional-layout-effect&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;&lt;span style=&quot;color: #000000;&quot;&gt;그럼 해결 방법을 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;흔히 자주들 사용하는 방법은 setTimeout과 같이 비동기 함수에 등록해서 나중에 실행되도록 억지로(?) 맞추는 방법입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716715634121&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  useEffect(() =&amp;gt; {
    if (isAlert) {
      setTimeout(() =&amp;gt; {
        alert(&quot;requestAnimationFrame&quot;)
      }, 1)
    }
  }, [isAlert])&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;2.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/83kRC/btsHBPi4RwO/braBF8MacKldI4Kb0qL9ZK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/83kRC/btsHBPi4RwO/braBF8MacKldI4Kb0qL9ZK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/83kRC/btsHBPi4RwO/braBF8MacKldI4Kb0qL9ZK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/83kRC/btsHBPi4RwO/braBF8MacKldI4Kb0qL9ZK/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;459&quot; data-filename=&quot;2.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;459&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: #000000;&quot;&gt;하지만 setTimeout도 1ms로 설정하면 동일합니다. 이를 맞추기 위해 억지로 시간을 100ms로 늘려보겠습니다.&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;3.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dYXOSX/btsHDzTb6Ef/7kBrzcSuCe7AtkDZdcndL0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dYXOSX/btsHDzTb6Ef/7kBrzcSuCe7AtkDZdcndL0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dYXOSX/btsHDzTb6Ef/7kBrzcSuCe7AtkDZdcndL0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dYXOSX/btsHDzTb6Ef/7kBrzcSuCe7AtkDZdcndL0/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;459&quot; data-filename=&quot;3.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;459&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: #000000;&quot;&gt;억지로 setTimeout 시간을 늘려서 맞추니깐 이번에는 원하는대로 동작합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;과연 이게 올바르게 해결한 것일까요? 보다 똑똑하고 올바르게 해결하는 방법을 소개해드리겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;window.requestAnimationFrame&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;mdn : window.requestAnimationFrame() 메서드는 브라우저에게 수행하기를 원하는 애니메이션을 알리고 다음 리페인트 바로 전에 브라우저가 애니메이션을 업데이트할 지정된 함수를 호출하도록 요청합니다. 이 메서드는 리페인트 이전에 호출할 인수로 콜백을 받습니다&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;requestanimationframe_장점&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;requestAnimationFrame 장점&lt;/span&gt;&lt;/h3&gt;
&lt;p id=&quot;백그라운드_동작_중지&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;백그라운드 동작 중지&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;requestAnimationFrame는 페이지가 비활성화 된 상태이면 페이지 화면 그리기 작업도 브라우저에 의해 일시 중지됨으로 CPU 리소스나 배터리 수명을 낭비하지 않게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;디스플레이_주사율에_맞게_호출_횟수&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;디스플레이 주사율에 맞게 호출 횟수&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;requestAnimationFrame는 모니터 주사율을 그대로 따르게 되어 최적화 되어 있습니다. 이러한 특성 때문에 rAF는 스크롤 이벤트 최적화 기법으로도 쓰이기도 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;requestAnimationFrame 사용법은 콜백 함수 내부에서 재귀 호출 하는 식으로 구성하면 됩니다. 브라우저는 애니메이션 프레임을 출력할 때마다 requestAnimationFrame 에 등록된 콜백 함수들을 비동기로 호출하여, 애니메이션을 부드럽게 출력해줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;왜 재귀로 호출해야하는지 의문이 생길수도 있습니다. (제가 그랬습니다....)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;단일 호출의 경우&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1716719422112&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;requestAnimationFrame(() =&amp;gt; {
  alert(&quot;Hello, Vite + React!&quot;)
})&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 코드에서는 requestAnimationFrame에 전달된 콜백 함수가 브라우저의 다음 애니메이션 프레임이 시작되기 전에 실행됩니다. 이는 일반적으로 현재 프레임이 끝난 후, 다음 프레임이 시작되기 전 시점입니다. 즉, 브라우저가 현재 작업을 완료하고, 렌더링을 준비하기 전에 콜백이 호출됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;재귀적으로 두 번 호출하는 경우&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1716719468986&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;requestAnimationFrame(() =&amp;gt; {
  requestAnimationFrame(() =&amp;gt; {
    alert(&quot;Hello, Vite + React!&quot;)
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 코드에서는 첫 번째 requestAnimationFrame 콜백이 브라우저의 다음 애니메이션 프레임이 시작되기 전에 실행됩니다. 그러나 이 콜백 내에서 또 다른 requestAnimationFrame이 호출되므로, 두 번째 콜백은 그 다음 애니메이션 프레임이 시작되기 전에 실행됩니다. 따라서, 두 번째 콜백은 첫 번째 콜백이 실행된 프레임 다음의 프레임에서 실행됩니다.&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_1716718946256&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  useEffect(() =&amp;gt; {
    if (isAlert) {
      requestAnimationFrame(() =&amp;gt; {
        requestAnimationFrame(() =&amp;gt; {
          alert(&quot;Hello, Vite + React!&quot;)
        })
      })
    }
  }, [isAlert])&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;4.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPgD79/btsHDeuZjcP/82O9LvDKgXk6086nt2wN1k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPgD79/btsHDeuZjcP/82O9LvDKgXk6086nt2wN1k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPgD79/btsHDeuZjcP/82O9LvDKgXk6086nt2wN1k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cPgD79/btsHDeuZjcP/82O9LvDKgXk6086nt2wN1k/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;459&quot; data-filename=&quot;4.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-message-id=&quot;63421ccb-0bea-4678-8976-f817b8a83572&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이제 결과를 보면 하위의 Vite+React 글자가 보이고 난 후 alert를 띄우는 것을 볼 수 있습니다!!!  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 gif를 자세히 보면 첫번째 새로고침에는 vite, react이미지 로고가 보이고 나서 alert를 띄우지만,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두번째 새로고침을 보면 이미지 노출되기전에 alert를 실행시키는 상황도 생깁니다. 이 경우는 다양하게 해결할 수 있지만,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ref를 활용해서 이미지가 dom에 있는 경우를 판단해서 alert를 실행시키면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;requestAnimationFrame의 개념과 이론을 더 자세히 알고 싶으면 아래 레퍼런스를 참고해주시면 될 것 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프레임과 task와 같은 중요한 개념을 상세히 다루고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;출처:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/&quot;&gt;https://inpa.tistory.com/entry/&lt;/a&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-requestAnimationFrame-%EA%B0%80%EC%9D%B4%EB%93%9C&quot;&gt; -requestAnimationFrame-가이드&lt;/a&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[Inpa Dev  &amp;zwj; :티스토리]&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발지식</category>
      <category>requestAnimationFrame</category>
      <category>requestanimationframe 사용법</category>
      <category>useeffect paint전에 실행</category>
      <category>useeffect 비동기 문제</category>
      <category>useeffect 페인트전에 실행</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/277</guid>
      <comments>https://ghost4551.tistory.com/277#entry277comment</comments>
      <pubDate>Sun, 26 May 2024 19:41:56 +0900</pubDate>
    </item>
    <item>
      <title>경북 네트워크형 캠퍼스SW아카데미 취업 준비 특강 후기</title>
      <link>https://ghost4551.tistory.com/275</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;지난번 이티에듀 SW기관을 통해 대덕 SW 마이스터 고등학교 개발자 특강과 더불어 대학생들 대상으로&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개발자 취업 준비 관련한 특강 요청도 받았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번 특강은 경북에서 운영 중인 &quot;경북 네트워크형 SW아카데미&quot;에 소속된 경일대, 대구가톨릭대, 안동대, 금오공대에서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;취업을 준비하고 있는 학생들의 대상으로 &quot;취업 준비 과정&quot;에 대한 특강이었습니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;저도 졸업 후 1년 정도의 기간 동안 취업 준비 과정을 겪었고, 작년에 취업을 성공해서 비교적 기억이 생생하게 남아있었기 때문에&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; 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: #000000; text-align: start;&quot;&gt;현업에서 일하면서 느낀 점을 토대로 조언(조언을 할 처지는 아니지만...)을 첨가해서 2시간 볼륨의 특강 준비를 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;시간이 남으면 어쩌나 걱정이 많았지만, Q&amp;amp;A만 40분을 진행하여 특강 시간을 초과하게 됐습니다.&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;(항상 Q&amp;amp;A 시간이 제일 활발한 것 같습니다ㅎㅎ)&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 style=&quot;color: #000000;&quot;&gt;특강은 온라인으로 진행되었는데, 온라인은 처음이라 빈 화면에 혼자 말하는 게 어색했지만, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;130명가량의 학생들을 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;쏘카 쿠폰의 미끼로 반응을 유도하여 재밌게 강의를 진행했습니다 ㅎㅎ&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개발을 시작하게 되면서 많은 분들에게 도움을 받으면서 성장을 할 수 있었습니다. 그래서 저도 그분들 처럼 개발 생태계에 선한 영향력을 끼치고 싶다는 생각이 들었는데, 이런 기회들을 통해 조금이나마 실현할 수 있게 되어서 뿌듯했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;경북.jpeg&quot; data-origin-width=&quot;1525&quot; data-origin-height=&quot;843&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvQeIK/btsFQXu1nFh/hGdZxgDamKBymuLKtecA0k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvQeIK/btsFQXu1nFh/hGdZxgDamKBymuLKtecA0k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvQeIK/btsFQXu1nFh/hGdZxgDamKBymuLKtecA0k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvQeIK%2FbtsFQXu1nFh%2FhGdZxgDamKBymuLKtecA0k%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;1525&quot; height=&quot;843&quot; data-filename=&quot;경북.jpeg&quot; data-origin-width=&quot;1525&quot; data-origin-height=&quot;843&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>일기장</category>
      <category>개발자 취업 준비 특강</category>
      <category>경북 네트워크형 캠퍼스SW아카데미</category>
      <category>경북 네트워크형 캠퍼스SW아카데미 특강</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/275</guid>
      <comments>https://ghost4551.tistory.com/275#entry275comment</comments>
      <pubDate>Sat, 16 Mar 2024 00:02:51 +0900</pubDate>
    </item>
    <item>
      <title>대덕SW마이스터 고등학교 개발자 특강 후기</title>
      <link>https://ghost4551.tistory.com/274</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;지난달 22일에 대덕 SW 마이스터고등학교에 개발자 특강을 다녀온 후기를 남기겠습니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;지난번 &lt;a href=&quot;https://ghost4551.tistory.com/266&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미래산업과학 고등학교 개발자 특강&lt;/a&gt; 후 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;관련 자료를 보고 &lt;a href=&quot;https://www.etedu.co.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이티에듀 SW 교육기관&lt;/a&gt;에서도 대덕 SW대상으로 특강 요청이 왔습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그냥 후기로 남겼던 글인데 다른 곳에서 연락 올지는 상상도 못했지만 좋은 기회라 생각하고 흔쾌히 수락했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그럼 특강하고 온 것에 대한 간략한 후기를 남겨보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;우선, 이번에도 it 특성화고 대상으로 진행되어 비슷하게 준비를 해갔습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다만, 이번에는 고등학교에 막 입학한 신입생 대상이었기 때문에 개발에 대해서 어떻게 접근해 보면 좋을지,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어떻게 공부를 시작하면 좋을지에 초점을 맞추어서 준비를 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 흔히 학생들이 많이 하는 리그오브레전드 게임을 통해 이 게임을 사용자가 이용하기 위해서는&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어떤 개발자들이 필요하고 또 어떤 일을 통해 서비스가 이루어지는지 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&quot;실제 사례를 통해 보는 개발과정&quot;&lt;/b&gt;을 소개하면서 개발 원리에 대해서 간략하게 설명을 진행했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dv7AuT/btsFGpLQK8L/uh0mgl0sW6n2WhziJqK4I0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dv7AuT/btsFGpLQK8L/uh0mgl0sW6n2WhziJqK4I0/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;현직 개발자 특강 사진 (7).jpg&quot; width=&quot;779&quot; height=&quot;584&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dv7AuT/btsFGpLQK8L/uh0mgl0sW6n2WhziJqK4I0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdv7AuT%2FbtsFGpLQK8L%2Fuh0mgl0sW6n2WhziJqK4I0%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;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XjvL2/btsFFnOZ1Yi/jEyJjvNtHHQoxBBbVzVjM1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XjvL2/btsFFnOZ1Yi/jEyJjvNtHHQoxBBbVzVjM1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;현직 개발자 특강 사진 (10).jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XjvL2/btsFFnOZ1Yi/jEyJjvNtHHQoxBBbVzVjM1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXjvL2%2FbtsFFnOZ1Yi%2FjEyJjvNtHHQoxBBbVzVjM1%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;4032&quot; height=&quot;3024&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;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 강의 중간중간 개발 용어들을 말할 때가 많았는데, 컴퓨터 경험이 없는 친구들도 많았기에&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;최대한 예시를 통해 쉽게 풀어서 설명하기 위해서 노력했습니다. (이 과정에서 말이 꼬이고 버벅대긴 했지만 다행히 잘 들어주셨습니다 ㅎㅎ..) 또한, 신입생이다 보니 질문이 없을지 알았는데 그래도 꽤 질문을 많이 해주셔서 나름 뿌듯했었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특강 진행 관련해서 짤막한 &lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;기사자료도 나와서 첨부하고 특강 후기는 여기서 마치겠습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.eduplusnews.com/news/articleView.html?idxno=10844&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.eduplusnews.com/news/articleView.html?idxno=10844&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709963508186&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[에듀플러스]2024 대덕SW마이스터고 신입생 학교 적응 캠프 개최&amp;hellip;신입생 팀빌딩&amp;middot;진로특강&amp;middot;졸업&quot; data-og-description=&quot;대덕소프트웨어(SW)마이스터고 신입생 64명이 학교 적응 캠프에 참여했다. 캠프를 통해 신입생들이 학교에 대한 자긍심을 높이고 학생들간 친밀한 관계를 형성해 학교생활에 보다 잘 적응 할 수 &quot; data-og-host=&quot;www.eduplusnews.com&quot; data-og-source-url=&quot;https://www.eduplusnews.com/news/articleView.html?idxno=10844&quot; data-og-url=&quot;https://www.eduplusnews.com/news/articleView.html?idxno=10844&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cfPK62/hyVumUSzUC/6iDBkcF8TDbt4mpkoCs7Z0/img.jpg?width=600&amp;amp;height=450&amp;amp;face=0_0_600_450,https://scrap.kakaocdn.net/dn/bnd1Bi/hyVxzrulbo/mex6KdrCmFt9QDt1rUb0I0/img.jpg?width=600&amp;amp;height=450&amp;amp;face=0_0_600_450,https://scrap.kakaocdn.net/dn/7p76V/hyVupjKs31/CUm1TSJKlXPnXV2ZkkXG0K/img.jpg?width=600&amp;amp;height=450&amp;amp;face=0_0_600_450&quot;&gt;&lt;a href=&quot;https://www.eduplusnews.com/news/articleView.html?idxno=10844&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.eduplusnews.com/news/articleView.html?idxno=10844&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cfPK62/hyVumUSzUC/6iDBkcF8TDbt4mpkoCs7Z0/img.jpg?width=600&amp;amp;height=450&amp;amp;face=0_0_600_450,https://scrap.kakaocdn.net/dn/bnd1Bi/hyVxzrulbo/mex6KdrCmFt9QDt1rUb0I0/img.jpg?width=600&amp;amp;height=450&amp;amp;face=0_0_600_450,https://scrap.kakaocdn.net/dn/7p76V/hyVupjKs31/CUm1TSJKlXPnXV2ZkkXG0K/img.jpg?width=600&amp;amp;height=450&amp;amp;face=0_0_600_450');&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;[에듀플러스]2024 대덕SW마이스터고 신입생 학교 적응 캠프 개최&amp;hellip;신입생 팀빌딩&amp;middot;진로특강&amp;middot;졸업&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;대덕소프트웨어(SW)마이스터고 신입생 64명이 학교 적응 캠프에 참여했다. 캠프를 통해 신입생들이 학교에 대한 자긍심을 높이고 학생들간 친밀한 관계를 형성해 학교생활에 보다 잘 적응 할 수&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.eduplusnews.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;</description>
      <category>일기장</category>
      <category>개발자 특강</category>
      <category>대덕 sw마이스터고</category>
      <category>대덕sw마이스터고 개발자 특강</category>
      <category>이티에듀</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/274</guid>
      <comments>https://ghost4551.tistory.com/274#entry274comment</comments>
      <pubDate>Sat, 9 Mar 2024 14:55:55 +0900</pubDate>
    </item>
    <item>
      <title>[FE_Roadmap] writing css &amp;amp; testing your apps</title>
      <link>https://ghost4551.tistory.com/273</link>
      <description>&lt;h1 id=&quot;writing-css&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;0&quot;&gt;writing css&lt;/h1&gt;
&lt;h3 id=&quot;tailwind-css%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;Tailwind CSS란 무엇인가?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;4&quot;&gt;Utility-First를 지향하는 CSS 프레임워크입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;5&quot;&gt;Utility-First: 미리 세팅된 유틸리티 클래스를 활용하여 HTML 코드 내에서 스타일링&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;6&quot;&gt;CSS의 각 속성들을 클래스에 직관적으로 표현함으로써 효율적으로 사용할 수 있게 됩니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;7&quot;&gt;스타일링에 필요한 대부분의 속성들이 클래스 형태로 사전에 정의되어 있고 사용자는 클래스들을 조합해서 사용하면 됩니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;8&quot;&gt;SSR에 대해 추가 설정이 필요하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;tailwind-css%EC%9D%98-%EC%9E%A5%EC%A0%90&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;10&quot; data-ke-size=&quot;size23&quot;&gt;Tailwind CSS의 장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Utility-First의 편리함과 빠른 개발&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;button class=&quot;py-2 px-4 rounded-lg shadow-md text-white bg-blue-500&quot;&amp;gt;
  Click me
&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;20&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;20&quot;&gt;스타일 코드도 HTML 코드 안에 있기 때문에 HTML와 CSS 파일을 별도로 관리할 필요가 없습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;21&quot;&gt;태그의 클래스명을 고민할 필요가 없습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;22&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;22&quot;&gt;BEM, OOCSS 등의 방법론이 나올 정도로 클래스명 짓는 일은 까다롭습니다. 이런 고민들이 필요없고 styled-components들의 컴포넌트 이름을 짓는 고민도 필요없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 쉽고 자유로운 커스텀 Tailwind CSS는 다른 프레임워크들에 비해 기본 스타일 값을 디테일한 부분까지 쉽게 커스텀이 가능합니다. 커스텀을 할 때 기본 스타일 값을 수정하는 방식이기에 디자인 일관성도 해치지 않습니다. 덕분에 디자인 시스템이나 다크 모드 구현도 간편합니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const fontSize = {
  10: [&quot;10px&quot;, { lineHeight: &quot;1.25&quot; }],
  12: [&quot;12px&quot;, { lineHeight: &quot;1.5&quot; }],
  14: [&quot;14px&quot;, { lineHeight: &quot;1.43&quot; }],
} as const&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;3. Styled Components와 호환 가능&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;37&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;37&quot;&gt;Tailwind와 Styled Components를 함께 사용하기 위해선 Tailwind, Twin.macro를 각각 설정해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;tailwind-css%EC%9D%98-%EB%8B%A8%EC%A0%90&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;39&quot; data-ke-size=&quot;size23&quot;&gt;Tailwind CSS의 단점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 클래스명 러닝 커브&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;43&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;43&quot;&gt;각 스타일의 클래스명을 익히는데 다소 시간이 소요됩니다. 따라서 지속적으로 찾아보면서 익숙해지는 수밖에 없습니다..&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;44&quot;&gt;그래도 대부분의 클래스명이 기존 CSS 속성이나 속성값과 비슷한 경우가 많고 자동완성을 지원하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Intelli Sense 플러그인&lt;/b&gt;이 있어서 금방 익숙해집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. JavaScript 코드 사용 불가&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;48&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;48&quot;&gt;클래스명을 분기 처리하여 동적으로 스타일링을 설정할 수는 있지만 styled-components와 같이 JavaScript 변수 값에 따라 가로 길이를 설정하는 등의 구현은 가능하기는 하지만 무척 번거로운 설정이 필요합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;49&quot;&gt;아래와 같은 방법으로 동적 스타일링을 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;html xml&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;{{ error ? 'text-red-600' : 'text-green-600' }}&quot;&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;3. HTML와 CSS 코드 혼재&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;57&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;57&quot;&gt;HTML와 CSS의 관심사가 분리되지 않아서 코드가 길어지고 가독성이 떨어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 id=&quot;testing-your-apps&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;59&quot;&gt;testing your apps&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;63&quot; data-ke-size=&quot;size16&quot;&gt;개발에서 테스트 프레임워크를 활용하여 테스트 코드를 작성해야하는 이유는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;65&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;65&quot;&gt;코드 품질 향상: 테스트 코드는 소프트웨어의 품질을 향상시키는데 중요한 역할을 합니다. 테스트 코드를 작성함으로써 코드의 신뢰성을 높이고 버그를 미리 발견할 수 있습니다. 이로 인해 사용자에게 더 안정적인 애플리케이션을 제공할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;66&quot;&gt;버그 식별: 테스트 코드를 사용하면 코드 변경이나 리팩토링 후에도 기존 기능이 올바르게 작동하는지 확인할 수 있습니다. 새로운 기능을 추가할 때 기존 기능에 영향을 미치는 버그를 식별하고 예방할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;67&quot;&gt;유지보수 용이성: 테스트 코드를 작성하면 코드를 이해하고 수정하는 데 도움이 됩니다. 새로운 개발자가 프로젝트에 참여하거나 팀 내 다른 개발자가 코드를 유지보수해야 할 때, 테스트 코드를 통해 코드의 의도를 파악하기 쉬워집니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;68&quot;&gt;자동화된 테스트 수행: 테스트 프레임워크를 사용하면 테스트를 자동화하여 반복적인 테스트 작업을 자동으로 수행할 수 있습니다. 이렇게 하면 효율성이 향상되며, 수동으로 테스트를 수행할 필요가 없어집니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;69&quot;&gt;리팩토링 지원: 코드를 개선하고 개선하기 쉽게 만듭니다. 테스트 코드가 있는 경우 리팩토링을 수행하더라도 코드가 여전히 예상대로 작동하는지 확인할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;70&quot;&gt;팀 협업과 품질 통제: 여러 개발자가 협업하는 프로젝트에서 테스트 코드는 코드 변경 사항이 다른 부분에 미치는 영향을 검증하는데 도움이 됩니다. 이로 인해 팀 내에서 품질을 일관되게 유지할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;71&quot;&gt;CI/CD 통합: 지속적 통합 (Continuous Integration, CI) 및 지속적 배포 (Continuous Deployment, CD) 파이프라인에서 테스트 코드는 자동화된 빌드 및 배포 과정 중에 애플리케이션의 안정성을 보장하는 데 중요한 역할을 합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;72&quot;&gt;비용 절감: 테스트 코드를 작성하고 유지 관리하면 애플리케이션에 버그가 발생하는 확률이 감소하므로, 버그를 해결하는 비용이나 사용자에게 서비스 중단을 제공하는 비용을 줄일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;74&quot; data-ke-size=&quot;size16&quot;&gt;요약하면, 테스트 프레임워크를 사용하여 테스트 코드를 작성하는 것은 소프트웨어 개발 프로세스를 개선하고 효율성을 높이며, 안정적이고 신뢰성 있는 애플리케이션을 개발하는 데 필수적입니다.&lt;/p&gt;
&lt;h3 id=&quot;jest&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;76&quot; data-ke-size=&quot;size23&quot;&gt;Jest&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;78&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;78&quot;&gt;Jest는 JavaScript용 테스트 프레임워크입니다. Vue, Angular, React, Node 등 널리 사용되는 대부분의 JavaScript 프레임워크를 지원합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;79&quot;&gt;Jest는 또 다른 JavaScript 테스트 프레임워크인 Jasmine을 기반으로 구축되었습니다. 그러나 Jest는 mocking 대한 기본 지원과 같이 Jasmine에 없는 일부 기능을 제공합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;80&quot;&gt;Jest는 테스트 범위 내에 존재하지 않는 객체에 대해 시뮬레이션 테스트를 실행할 수 있는 모의 지원이 내장되어 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;81&quot;&gt;Jest는 또한 서로 다른 두 테스트의 출력을 비교하려는 경우에 유용한 스냅샷 기능을 제공하며, 병렬 테스트를 지원 하므로 일련의 테스트를 완료하는 데 걸리는 시간을 획기적으로 줄일 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;82&quot;&gt;Jest를 사용하면 최소한의 설정이나 구성으로 테스트를 실행할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;jest%EC%9D%98-%EC%9E%A5%EC%A0%90&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;84&quot; data-ke-size=&quot;size23&quot;&gt;Jest의 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;86&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;86&quot;&gt;단순성 : Jest는 표준 JavaScript 코드에 대한 구성이 필요 없이 작동하도록 설계되었습니다. 즉, 대부분의 코드베이스에 대해 아주 약간의 설정만으로 테스트 실행을 시작할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;87&quot;&gt;광범위한 프레임워크 지원 : 위에서 언급했듯이 Jest는 거의 모든 인기 있는 JavaScript 개발 프레임워크와 작동합니다. JavaScript 코드를 어떻게 작성하든 Jest가 테스트를 위해 이를 지원할 가능성이 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;88&quot;&gt;강력한 문서화 및 지원 : 2011년부터 존재해 왔으며 대부분의 역사 동안 Meta에서 적극적으로 지원해 온 테스트 프레임워크로서 Jest는 많은 추종자를 얻었습니다. 고품질 문서를 ​​제공하며 Jest 작업에 대한 커뮤니티 지원을 쉽게 찾을 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;89&quot;&gt;간단히 말해서, 쉬운 모킹 구현과 같은 일부 고급 기능에 대한 액세스도 제공하는 간단한 JavaScript 테스트 프레임워크를 찾고 있다면 Jest가 좋은 솔루션입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;vitest&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;91&quot; data-ke-size=&quot;size23&quot;&gt;Vitest&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;93&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;93&quot;&gt;Vitest는 JavaScript 단위 테스트 프레임워크입니다. 이는 JavaScript 기반 웹 애플리케이션을 관리하고 구축하는 데 도움이 되는 도구인 Vite를 보완하기 위해 만들어졌습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;94&quot;&gt;Vitest를 사용하여 Vite를 통해 관리하는 코드를 테스트하는 경우 Vitest와 Vite 간의 긴밀한 통합 덕분에 대부분의 테스트 설정이 자동으로 수행됩니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;95&quot;&gt;Vitest는 대부분의 Jest API 와 호환됩니다 . 즉, Jest용으로 개발된 대부분의 테스트는 Vitest를 사용하여 실행할 수도 있으며 변경이 거의 필요하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;vitest%EC%9D%98-%EC%9E%A5%EC%A0%90&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;97&quot; data-ke-size=&quot;size23&quot;&gt;Vitest의 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;99&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;99&quot;&gt;Vitest는 최신 테스트 프레임워크에서 기대할 수 있는 거의 모든 기능을 제공한다는 점에서 Jest와 유사합니다. 그러나 주요 특징은 속도와 (Vite와 함께 사용되는 경우) 단순성입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;100&quot;&gt;테스트 프레임워크로서 Vitest의 가장 중요한 기능은 속도에 중점을 둔 것입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;101&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;101&quot;&gt;일부 Vitest 테스트는 Jest를 사용한 테스트보다 4배 이상 빠르게 실행된 것으로 나타났습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;102&quot;&gt;Vitest의 또 다른 장점은 Vite와의 긴밀한 통합으로 간단한 테스트 설정 및 관리 경험이 가능하다는 것입니다. Vite를 통해 애플리케이션 코드를 관리하는 경우 테스트를 생성할 때 이미 개발한 대부분의 구성, 플러그인 및 기타 요소를 재사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;jest-vs-vitest&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;104&quot; data-ke-size=&quot;size23&quot;&gt;Jest vs Vitest&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;106&quot; data-ke-size=&quot;size16&quot;&gt;속도&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;108&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;108&quot;&gt;Jest 테스트가 Vite 테스트보다 빠르게 실행되는 상황이 있을 수 있지만, 어떤 프레임워크가 더 빠른 테스트 결과를 가져올지 결정해야 한다면 Vitest가 훨씬 더 안전한 옵션이 될 것입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;109&quot;&gt;그러나 Jest에 비해 Vitest의 속도 이점이 실제로 중요한 고려 사항인지 여부는 실행 중인 테스트 수와 실행 위치에 따라 다릅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;111&quot; data-ke-size=&quot;size16&quot;&gt;모듈 관리&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;113&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;113&quot;&gt;Jest와 Vitest의 차이점 중 하나는 기본적으로 지원하는 모듈 관리 시스템입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;114&quot;&gt;Jest는 CommonJS와 함께 작동하도록 설계되었습니다. Vitest는 JavaScript 모듈 작업에 대한 보다 현대적인 접근 방식인 ECMAScript 모듈(ESM)과 호환되도록 설계되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;116&quot; data-ke-size=&quot;size16&quot;&gt;Vitest가 적절한 사용 사례&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;118&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;118&quot;&gt;Vite로 개발된 프로젝트&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;119&quot;&gt;광범위한 테스트가 필요하거나 테스트 속도가 중요한 프로젝트&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;121&quot; data-ke-size=&quot;size16&quot;&gt;Jest가 적절한 사용 사례&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;123&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;123&quot;&gt;개발자가 특정 빌드 도구(즉, Vite)에 얽매이거나 사용하도록 권장하기를 원하지 않는 JavaScript 프로젝트&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;124&quot;&gt;테스트 속도가 우선순위가 낮은 프로젝트&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;125&quot;&gt;광범위한 문서 및 커뮤니티 지원에 대한 액세스가 중요한 프로젝트&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발지식</category>
      <category>frontend roadmap study</category>
      <category>testing your apps</category>
      <category>writing css</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/273</guid>
      <comments>https://ghost4551.tistory.com/273#entry273comment</comments>
      <pubDate>Sat, 2 Mar 2024 00:52:50 +0900</pubDate>
    </item>
    <item>
      <title>[FE_Roadmap] Pick a Framework</title>
      <link>https://ghost4551.tistory.com/272</link>
      <description>&lt;h3 id=&quot;%EC%9B%B9-%EA%B0%9C%EB%B0%9C-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EB%A5%BC-%EC%84%A0%ED%83%9D%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-15%EA%B0%80%EC%A7%80-%EC%A4%91%EC%9A%94%ED%95%9C-%EA%B3%A0%EB%A0%A4-%EC%82%AC%ED%95%AD&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;웹 개발 프레임워크를 선택하기 위한 15가지 중요한 고려 사항&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;4&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;4&quot;&gt;&lt;b&gt;목적 및 사용 맥락&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;5&quot;&gt;개발하는 웹 사이트 or 웹 애플리케이션의 목적에 따라 선택해야 합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;6&quot;&gt;프로젝트의 목적과 선택한 프레임워크에 대한 이유에 대해서 스스로 묻고 대답해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;7&quot;&gt;라이선스
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;8&quot;&gt;소프트웨어를 사용, 배포 및 수정할 수 있는 법적 계약이므로 중요합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;9&quot;&gt;상용 목적으로 사용할 수 있는지, 등에 대한 조건이 있는지 확인해야 합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;10&quot;&gt;다른 소프트웨어와 배포와 호환되는지 확인해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;11&quot;&gt;문서화
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;12&quot;&gt;개발자가 프레임워크 기능을 이해하고, 문제를 해결하기 위해서는 공식 문서는 필수적입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;13&quot;&gt;예제, 치침, 자습서, 샘플 코드 등을 제공하는 설명서를 찾아야합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;14&quot;&gt;문서가 불분명하거나 오랜된 정보라면 프레임워크를 효율적으로 배우고 학습히기 어려울 뿐만 아니라, 프레임워크를 걷어내야하는 과정이 생길 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;15&quot;&gt;확장성
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;16&quot;&gt;비즈니스가 성장하고 확장함에 따라 웹 애플리케이션도 증가된 트래픽과 데이터 볼륨을 처리할 수 있어야 합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;17&quot;&gt;확장 불가능한 웹 애플리케이션은 빠르게 과부하되고 느려질 수 있으며, 좋지 않은 사용자 경험으로 인해 재정적 손실을 초래할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;18&quot;&gt;&lt;b&gt;학습 곡선&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;19&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;19&quot;&gt;새로운 프레임워크를 배우는 데 필요한 시간과 노력은 프로젝트 기간에 영향을 미칠 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;20&quot;&gt;학습 곡선이 높을 수록 능숙해지는데 시간이 오래 걸리므로 프로젝트가 지연될 수 있고, 이는 코드 품질에도 영향을 미칠 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;21&quot;&gt;웹 개발이 처음이라면 Bootstrap와 같은 프레임워크로 시작하는 것이 좋습니다. -&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;즉 처음부터 개념적인 원리를 익히기 위해서는 학습 곡선이 매우 높으므로, 프레임워크를 통해 사용법을 익히고 원리를 익히는게 좋다고 생각합니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;22&quot;&gt;&lt;b&gt;성능&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;23&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;23&quot;&gt;웹 사이트 또는 웹 앱의 속도와 응답성이 사용자 경험에 영향을 미치기 때문에 중요합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;24&quot;&gt;웹 사이트의 성능은 검색 엔진 순위에도 영향을 미치기 때문에 중요합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;25&quot;&gt;웹 개발 프레임워크를 선택하기 위한 고려 사항으로 프로그래밍 언어, 랜더링, 코드 최적화, 빌드, 타사 라이브러리 종속성과 같은 요소를 확인해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;26&quot;&gt;라이브러리
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;27&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;27&quot;&gt;제공되는 라이브러리는 개발을 단순화하고 가속화하는데 사용할 수 있는 사전 구축된 모듈 또는 기능의 모음입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;28&quot;&gt;ex) 반복적인 코드 작성을 피할 수 있는 라이브러리가 있는 프레임워크를 채택할 가능성이 더 큷니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;29&quot;&gt;타사에 대한 강력한 라이브러리 지원이 있는 프레임워크를 선택하는 것도 중요합니다. -&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;하나의 프로젝트에서 다양한 프레임워크, 라이브러리를 사용하기 때문에 호환도 중요합니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;30&quot;&gt;&lt;b&gt;커뮤니티&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;31&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;31&quot;&gt;프레임워크로 작업할 때 적절한 문서가 있더라도 버그 및 오류가 발생할 수 있습니다.이를 빠르고 효율적으로 해결하기 위해서는 커뮤니티가 중요합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;32&quot;&gt;&lt;b&gt;해당 기술에 관련된 다양한 정보를 나누고 습득할 수 있는 또 다른 문서의 집합이라고 생각합니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;33&quot;&gt;설치 용이성
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;34&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;34&quot;&gt;설치 용이성은 생산성과 작업 흐름에 상당한 영향을 미칠 수 있습니다. 복잡한 설치 프로세스는 특히 프레임워크를 처음 사용하는 개발자에게는 성가시고 시간이 많이 소요될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;35&quot;&gt;보안 (서버)
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;36&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;36&quot;&gt;보안은 웹 개발에서 최우선 순위입니다. 따라서 프레임워크가 기본적인 보안 기능을 제공하는지 확인해야합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;37&quot;&gt;SSL 지원, 웹 공격에 대한 보호, 안전한 암호 저장 등등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;38&quot;&gt;&lt;b&gt;호환성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;39&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;39&quot;&gt;사용하려는 프레임워크가 웹 서버 및 운영 체제와 호환되는지 확인해야합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;40&quot;&gt;웹 개발 프레임워크를 고려해야 할 요소는 다음과 같습니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-line=&quot;41&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: lower-alpha;&quot; data-line=&quot;41&quot;&gt;브라우저 간 호환성 : chrome, safari, firefox등&lt;/li&gt;
&lt;li style=&quot;list-style-type: lower-alpha;&quot; data-line=&quot;42&quot;&gt;교차 플랫폼 호환성: window, mac, ios, android 등&lt;/li&gt;
&lt;li style=&quot;list-style-type: lower-alpha;&quot; data-line=&quot;43&quot;&gt;타사 라이브러리 호환성 : 웹 개발에 사용되는 타사 라이브러리와 호환되는지 확인해야합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;44&quot;&gt;&lt;b&gt;업데이트&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;45&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;45&quot;&gt;빠르게 변화하는 디지털 세계에서 프레임워크는 최신 웹 기술과의 관련성과 호환성을 유지하기 위해 정기적으로 업데이트되어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>개발지식</category>
      <category>frontend roadmap study</category>
      <category>Pick a Framework</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/272</guid>
      <comments>https://ghost4551.tistory.com/272#entry272comment</comments>
      <pubDate>Sun, 18 Feb 2024 17:28:28 +0900</pubDate>
    </item>
    <item>
      <title>[FE_Roadmap] Module Bundlers</title>
      <link>https://ghost4551.tistory.com/271</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 1.44em; letter-spacing: -1px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;In this article&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/Users/rio/Desktop/socar-github/roadmap-study/buildTools/Rio/moduleBundlers.md#Webpack&quot;&gt;Webpack&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/Users/rio/Desktop/socar-github/roadmap-study/buildTools/Rio/moduleBundlers.md#ESBuild&quot;&gt;ESBuild&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/Users/rio/Desktop/socar-github/roadmap-study/buildTools/Rio/moduleBundlers.md#Vite&quot;&gt;Vite&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/Users/rio/Desktop/socar-github/roadmap-study/buildTools/Rio/moduleBundlers.md#webpack-vs%20vite&quot;&gt;webpack vs vite&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&quot;module-bundlers&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;0&quot;&gt;Module Bundlers&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;2&quot;&gt;JavaScript 모듈을 브라우저에서 실행할 수 있는 파일로 번들링 하는 데 사용되는 프론트엔드 개발 도구로 즉, 여러 개의 모듈 파일들을 하나의 파일로 번들링하는 도구입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;%ED%8A%B9%EC%A7%95&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;6&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;6&quot;&gt;모듈화된 코드를 하나의 파일로 번들링할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;8&quot;&gt;여러 개의 모듈 파일들을 하나의 파일로 번들링함으로써, 웹 페이지의 로딩 속도를 개선할 수 있습니다. 이는 HTTP 요청의 수를 줄이고, 브라우저가 파일을 다운로드하는 시간을 단축시킵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;10&quot;&gt;다양한 모듈 시스템을 지원합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;12&quot;&gt;CommonJS, AMD, ES6 모듈 시스템 등 다양한 모듈 시스템을 지원합니다. 이는 다양한 프로젝트에 적용할 수 있으며, 기존 모듈 시스템과 호환성을 유지할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;14&quot;&gt;다양한 기능을 제공합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;16&quot;&gt;모듈 번들러는 다양한 기능을 제공합니다. 예를 들어, Tree Shaking, Code Splitting, Dynamic Import 등이 있습니다. 이러한 기능을 활용하여 번들 파일의 크기를 최적화하고, 필요한 모듈만 로드할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;18&quot;&gt;간단한 설정으로 사용할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;20&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;20&quot;&gt;대부분의 모듈 번들러는 간단한 설정만으로 사용할 수 있습니다. 이는 초보자도 쉽게 접근할 수 있다는 장점이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;22&quot;&gt;재사용성이 높아집니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;24&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;24&quot;&gt;모듈화된 코드는 재사용성이 높아집니다. 이는 코드의 유지보수성과 생산성을 높일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;26&quot;&gt;빌드 시스템과 연계하여 사용할 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;28&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;28&quot;&gt;모듈 번들러는 대부분 빌드 시스템과 연계하여 사용됩니다. 이는 빌드 자동화를 통해 개발 생산성을 높일 수 있습니다. 이러한 모듈 번들러의 특징과 장점은 웹 개발에서 모듈화를 구현하는 데 필수적인 도구 중 하나입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot; data-line=&quot;31&quot;&gt;종류
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;33&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;33&quot;&gt;Rollup은 주로 라이브러리 개발에 사용되며, Tree Shaking이라는 기능을 통해 불필요한 코드를 제거하여 번들 파일의 크기를 최적화할 수 있습니다. Parcel은 간단한 설정만으로 번들링을 처리할 수 있어 초보자에게 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;webpack&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;35&quot; data-ke-size=&quot;size26&quot;&gt;Webpack&lt;/h2&gt;
&lt;h3 id=&quot;%EC%9E%A5%EC%A0%90&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;37&quot; data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;39&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;39&quot;&gt;다양한 기능을 제공하며, 대부분의 프로젝트에 적용할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;40&quot;&gt;다양한 로더(loader)와 플러그인(plugin)을 제공하여 코드 번들링에 대한 다양한 옵션을 제공합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;41&quot;&gt;Hot Module Replacement 기능을 제공하여, 코드 수정 시 브라우저 새로고침 없이 변경된 모듈만 적용할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;42&quot;&gt;코드 스플리팅(Code Splitting) 기능을 제공하여, 필요한 모듈만 로드하여 번들 파일의 크기를 줄일 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;43&quot;&gt;대규모 프로젝트에서 사용할 때에도 안정적인 성능을 보입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;%EB%8B%A8%EC%A0%90&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;45&quot; data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;47&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;47&quot;&gt;복잡한 설정이 필요할 수 있습니다. 특히, 처음 사용하는 경우에는 설정에 대한 이해도가 필요합니다. 초기 빌드 시간이 오래 걸릴 수 있습니다. 대규모 프로젝트에서는 초기 빌드 시간이 상당히 길어질 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;49&quot;&gt;번들 파일 크기가 큰 경우가 있습니다. 특히, 코드 스플리팅을 적용하지 않으면 모든 코드를 하나의 번들 파일로 묶어서 배포하므로 파일 크기가 매우 커질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;esbuild&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;51&quot; data-ke-size=&quot;size26&quot;&gt;ESBuild&lt;/h2&gt;
&lt;h3 id=&quot;ec9ea5eca090-1&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;53&quot; data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;55&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;55&quot;&gt;Go 언어로 작성된 빠른 모듈 번들러입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;56&quot;&gt;높은 성능과 작은 번들 파일 크기를 제공합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;57&quot;&gt;JavaScript, TypeScript, JSX, CSS 등 다양한 형식의 모듈을 지원합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;58&quot;&gt;코드 번들링을 빠르게 처리하며, 개발 환경에서 빠른 빌드 속도를 보여줍니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;59&quot;&gt;대부분의 경우, 별도의 설정 없이 간단하게 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;eb8ba8eca090-1&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;61&quot; data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;63&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;63&quot;&gt;아직은 Webpack에 비해 덜 다양한 로더와 플러그인을 제공합니다. 이러한 기능들이 ESBuild에 추가되면 더욱 강력한 도구가 될 것입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;64&quot;&gt;상대적으로 최신 기술이기 때문에, 아직은 범용성과 안정성 측면에서 Webpack보다는 미숙할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;65&quot;&gt;대규모 프로젝트에서는 Webpack과 비교하여 약간 더 느릴 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;vite&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;67&quot; data-ke-size=&quot;size26&quot;&gt;Vite&lt;/h2&gt;
&lt;h3 id=&quot;ec9ea5eca090-2&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;69&quot; data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;71&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;71&quot;&gt;Vue.js 개발을 위한 빠른 개발 환경 도구입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;72&quot;&gt;개발 환경에서는 번들링 대신 원본 파일을 사용하여 빠른 빌드 속도를 제공합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;73&quot;&gt;Hot Module Replacement 기능을 제공하여, 코드 수정 시 브라우저 새로고침 없이 변경된 모듈만 적용할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;74&quot;&gt;개발 환경과 빌드 환경의 차이를 최소화하여 개발과 배포의 차이를 줄입니다. Vue.js 개발 환경에서 사용할 때 높은 생산성을 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;eb8ba8eca090-2&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;77&quot; data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;79&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;79&quot;&gt;Vue.js 개발을 위한 전용 도구이기 때문에, 다른 프레임워크나 라이브러리와 함께 사용하기에는 어려울 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;80&quot;&gt;기존 Webpack 프로젝트를 Vite로 마이그레이션하는 것은 어려울 수 있습니다. 특히, Webpack의 많은 플러그인들은 Vite에서 지원되지 않을 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;81&quot;&gt;빌드 시간이 Webpack보다는 빠르지만, ESBuild보다는 느릴 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;83&quot; data-ke-size=&quot;size16&quot;&gt;이러한 Webpack, ESBuild, Vite는 각각의 특징과 장점을 가지고 있으며, 프로젝트의 요구사항에 따라 선택하여 사용할 수 있습니다. 예를 들어, 대규모 프로젝트에서는 Webpack이 안정적인 성능을 보여주지만, 빠른 개발 환경을 필요로 하는 경우에는 Vite를 선택할 수 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;webpack-vs-vite&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;85&quot; data-ke-size=&quot;size26&quot;&gt;webpack vs vite&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;87&quot; data-ke-size=&quot;size16&quot;&gt;Webpack은 번들링할 때마다 전체 애플리케이션을 다시 컴파일하므로, 번들링 시간이 오래 걸릴 수 있습니다. 반면에 Vite는 ESM 기반 개발 서버를 사용하여, 모듈별로 개별적으로 빌드하여 캐시에 저장합니다. 이를 통해 개발 서버의 빠른 성능을 보장하면서 번들링 속도를 향상시킵니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;89&quot; data-ke-size=&quot;size16&quot;&gt;또한 Vite는 HMR(Hot Module Replacement)을 기본적으로 지원하기 때문에, 코드 변경이 발생했을 때 브라우저 새로고침 없이 즉시 반영됩니다. 이는 개발 생산성을 크게 향상시킬 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;91&quot; data-ke-size=&quot;size16&quot;&gt;따라서, React 프로젝트를 개발할 때, 번들링 속도나 개발 생산성 등의 측면에서 Vite를 선택하는 것이 좋습니다. 그러나 Webpack은 다양한 로더와 플러그인을 제공하고, 강력한 기능과 확장성을 가지고 있으므로, 특정한 상황이나 요구사항에 따라 선택할 수도 있습니다.&lt;/p&gt;</description>
      <category>개발지식</category>
      <category>frontend roadmap study</category>
      <category>Module Bundlers</category>
      <category>webpack vs vite</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/271</guid>
      <comments>https://ghost4551.tistory.com/271#entry271comment</comments>
      <pubDate>Tue, 13 Feb 2024 20:52:58 +0900</pubDate>
    </item>
    <item>
      <title>[1년 차 개발자의 2023년 회고]</title>
      <link>https://ghost4551.tistory.com/270</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;2023.01.09 쏘카에 첫 출근했던 게 엊그제 같은데 벌써 1년이라는 시간이 흘렀습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;지난 1년간 좋은 팀원들과 동료들 덕분에 많은 경험을 할 수 있었고, 개발 실력뿐만 아니라 다양한 역량에서 성장할 수 있었습니다. 그중에서 생각이 바뀌게 된 부분과 많은 성장을 했다고 느낀 부분을 정리해 보려고 합니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;[협업과 커뮤니케이션]&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;대학생 시절부터 여러 대내외 개발 동아리를 활동하면서 커뮤니케이션과 협업활동의 중요성을 알았기 때문에 이런 점에서는 별로 어려움이 없을 것 같았습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;하지만, 개발자들끼리 프로젝트를 수행했던 동아리 활동들과 달리, 회사에서는 사업부서, 기획자, 디자이너 등 서비스에 관련된 여러 사람들, 팀과 프로젝트를 수행합니다. 그렇기 때문에 서비스(도메인)에 대한 이해도가 가장 중요했습니다. 그래서 내가 이해하고 있는 부분, 모르는 부분을 명확하게 알고 있어야 했습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;하지만, 초반에는 이런 것들에 대해 잘 몰랐고, 주어진 요구사항에 대한 개발만 수행하면 된다고 생각했기에 별로 무관심했습니다. 또한 회의 도중에 질문을 하기란 쉽지가 않았습니다. 이런 상황들 때문에 개발하면서 놓치고 있던 점들이 종종 발생했고, 그래서 새로 개발하거나 추가적인 커뮤니케이션 비용이 발생하는 문제점들이 많았습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;지금도 부족하고 별거 아니지만 아래와 같은 방법이 도움 됐습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;- 큰 그림을 파악하기 위해 피그마, 기획 문서, 명세서 훑어보기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;- 어드민이 있다면 어드민 기능 사용해 보고 훑어보기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;- 이해하고 있는지 모호할 경우 이해한 것이 맞는지 설명을 통해 검증받기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;- 모르는 것이 있을 때 잘 정리해서 질문하기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;- 개발적인 요소를 설명할 때 모두가 이해할 수 있도록 전문적인 용어를 피하고, 비유를 통해 쉽게 설명하기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;(도움이 되었던 유튜브 동영상을 남기자면...)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;커뮤니케이션 잘하는 개발자의 4가지 습관 | 인프콘2023&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;[주니어 시절 성장과 고민들 | 인프콘2023 (영상 20분부터...)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;[능동적으로 일하기]&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;능동적으로 일하기 위해서는 경험과 능력이 바탕이 되어야 한다고 생각했습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;그래서 어떤 의견이나 방법을 내세울 때도 적절한 의견인지 방법인지 여러 번 고민하다가 결국 말을 못 하고 지나간 경험이 있습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;또한, 이렇게 하면 더 좋지 않을까 하는 생각이 들어도, 이렇게 정해진 이유가 있겠지, 이렇게 일하는 이유가 있겠지 하면서 스스로 합리화하는 과정도 있었습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;그래서 주어진 대로, 흘러가는 대로 일을 했고, 문제가 있을 때는 이거 문제가 있는데 어떻게 하면 될까요?라고 수동적인 태도로 일을 하게 됐습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;하지만 적극적으로 참여하고 능동적으로 일하는 팀원과 동료들을 보면서 잘못 생각하고 일하고 있구나 느끼게 됐습니다. 그래서 책임감을 가지고 나름 적극적으로 의견을 내세우면서 일을 하게 됐고, 문제가 있을 때는 적절한 해결법을 제시하면서 능동적인 태도로 일을 하기 위해 노력했습니다. 능동적인 태도 덕분에 일을 더 효율적이게 할 수 있었습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;어떻게 능동적으로 일할 수 있는지 생각해 보면 경험상 이런 방법이 있었던 것 같습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;- 기획, 개발 시에 UI, UX에서 이렇게 하면 사용자가 더 좋지 않을까? 하는 생각이 들면 아이디어를 제시해서 좋은 제품을 만들기 위해 디자이너와 함께 고민하기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;- 기획에서 주어진 개발 방향성이 어렵다면 안된다고 말하기보다는 다른 해결 방법을 찾아 제안을 통해 문제 해결하기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;- 서버 API 명세서를 잘 이해할 수 있도록 사전에 구조에 대해 논의하기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;- 코드 리뷰를 잘하고, 중복 코드를 줄이기 위한 규칙이나 방법을 논의하기&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;혼자 일을 했다면 변화 없는 1년이라는 세월을 보냈겠지만, 팀원들과 동료들 덕분에 좋은 방향성을 가질 수 있었고 성장할 수 있었던 것 같습니다.  &amp;zwj;♂️&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;사실 2023년에는 취업에 대한 보상 심리로 공부를 안 했던 것 같은데, 2024년에는 기술적 역량도 쌓기 위해 공부도 하고, 개발 트렌드도 읽고, 기술 블로그도 다시 작성할 수 있도록 노력하겠습니다.  &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>일기장</category>
      <category>1년차 개발자 회고</category>
      <category>프론트엔드 1년차 회고</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/270</guid>
      <comments>https://ghost4551.tistory.com/270#entry270comment</comments>
      <pubDate>Sun, 7 Jan 2024 22:43:00 +0900</pubDate>
    </item>
    <item>
      <title>[FE_Roadmap] CSS Architecture&amp;amp;CSS Preprocessors</title>
      <link>https://ghost4551.tistory.com/269</link>
      <description>&lt;h1 id=&quot;css-architecture&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;0&quot;&gt;CSS Architecture&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;CSS는 자유도가 높은만큼 까다롭고 어렵습니다. 이러한 특성 때문에 종종 기대했던 결과가 나오지 않는 경우가 많습니다. CSS를 계획적으로 구축되지 않았다면 프로젝트가 확장될 때 깨지거나 무너질 가능성이 매우 높습니다. 따라서 견고한 CSS 아키텍처의 기반은 프로젝트가 확장될 때 가장 중요한 요소가 됩니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;대표적인 예&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;10&quot;&gt;협업 시 사람마다 다른 코드 구조 및 정의&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;11&quot;&gt;서로 다른 네이밍 규칙&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;12&quot;&gt;CSS 우선순위 문제&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;13&quot;&gt;복잡해지는 선택자 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;CSS Architecture 종류&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;17&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;17&quot;&gt;OOCSS(객체 지향 CSS)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;18&quot;&gt;BEM(블록 요소 수정자)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;19&quot;&gt;SMACSS(Scalable and Modular Architecture for CSS)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;oocss-object-oriented-css&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;21&quot; data-ke-size=&quot;size23&quot;&gt;OOCSS (Object Oriented CSS)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;23&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;23&quot;&gt;객체 지향에 따라서 고안된 설계 방식&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;24&quot;&gt;구조와 외형을 분리
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;25&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;25&quot;&gt;구조 : width, height, border, padding, margin....&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;26&quot;&gt;외형 : color, border-color, font-color, background-color...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;html xml&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;btn common-skin tel&quot;&amp;gt;tel&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;btn common-skin email&quot;&amp;gt;email&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;.btn{공통 스타일 정의}
.common-skin{공통 스타일 정의}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;38&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;38&quot;&gt;컨테이너와 내용을 분리
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;39&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;39&quot;&gt;위치에 의존하지 않는 스타일 정의&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;40&quot;&gt;어떤 태그라도 동일한 외형 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;html xml&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;h3 class=&quot;sub-title&quot;&amp;gt;...&amp;lt;/h3&amp;gt;
&amp;lt;span class=&quot;sub-title&quot;&amp;gt; ... &amp;lt;/span&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;.sub-title {
  font-size: 16px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;53&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;53&quot; data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;55&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;55&quot;&gt;공통된 부분을 정의해서 재사용이 가능. =&amp;gt; 코드의 재사용으로 코드 양이 줄어듬&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;56&quot;&gt;구조적 상황에 관계없이 동일한 클래스라면 동일한 스타일을 기대할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;58&quot; data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;60&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;60&quot;&gt;공통된 클래스가 많기 때문에 수정이 발생할 시 멀티클래스를 사용해야 함&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;61&quot;&gt;멀티클래스가 많아짐에 따라 유지보수에 어려움 =&amp;gt; 중첩된 스타일링 문제&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;62&quot;&gt;코드의 가독성이 떨어짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;bemblock-element-modifier&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;64&quot; data-ke-size=&quot;size23&quot;&gt;BEM(Block, Element, Modifier)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;66&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;66&quot;&gt;Block, Element, Modifier로 나누어서 클래스명을 기술하는 방법&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;67&quot;&gt;block(전체를 감싸고 있는 블록 요소), element(내부 요소)ㅡ modifier(기능/수정)으로 정의하고 block과 element는 더블 언더스코어로, modifier는 더블 하이픈으로 연결하여 정의하는 방법론&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;css&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;.header__search p {
  /* 태그 변경시 전체적으로 수정이 발생하여 비효율적 */
}
.header__search--form {
  /* 수정이 발생해도 무방 */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;78&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;78&quot; data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;80&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;80&quot;&gt;직관적인 클래스 명으로 마크업 구조를 직접 보지 않아도 구조의 파악이 쉬움&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;82&quot; data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;84&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;84&quot;&gt;클래스명이 상대적으로 길어질 수밖에 없는 구조이기 때문에 코드가 길어지고 복잡해지는 단점&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;85&quot;&gt;기존 마크업에서 새롭게 기능이 추가되었을 경우 클래스명 재수정이 불편&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;smacss-scalable-and-modular-architecture-for-css&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;87&quot; data-ke-size=&quot;size23&quot;&gt;SMACSS (Scalable and Modular Architecture for CSS)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;89&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;89&quot;&gt;CSS에 대한 확장형 모듈식 구조&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;90&quot;&gt;CSS의 프레임워크가 아닌 하나의 스타일 가이드
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;91&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;91&quot;&gt;Base - 기본 규칙
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;92&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;92&quot;&gt;각 브라우저의 기본 스타일 (default.css, reset.css), 요소 element 스타일의 기본 정의 값&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;93&quot;&gt;Layout - 레이아웃 규칙
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;94&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;94&quot;&gt;큰 틀의 레이아웃, 요소를 배치, 구별하는데 적용&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;95&quot;&gt;주요 컴포넘트 : header, footer, aside, container, content 등&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;96&quot;&gt;하위 컴포넘트 : list, item, form 등&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;97&quot;&gt;클래스명은 접수사 i-, layout-명시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;98&quot;&gt;Module - 모듈 규칙
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;99&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;99&quot;&gt;페이지에서 재사용 가능한 요소 : 버튼, 배너, 아이콘, 박스 요소 등&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;100&quot;&gt;각 모듈은 독립성을 가지게 스타일 선언 : 재사용이 가능하게 id, 태그 선택자는 사용하지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;101&quot;&gt;State - 상태 규칙
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;102&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;102&quot;&gt;요소의 상태변화를 표현하는 요소 : 툴팁, 아코디언 등&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;103&quot;&gt;active나 disable 등이며 suffix &quot;is-&quot;나 &quot;s-&quot;를 붙여서 사용&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;104&quot;&gt;모듈과 레이아웃 모두 적용 가능&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;105&quot;&gt;Theme - 테마 규칙&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;106&quot;&gt;사용자가 선택 가능하도록 스타일을 재선언하여 사용.&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;107&quot;&gt;Theme는 전반적인 Look and feel을 정의하며 suffix &quot;theme-&quot;를 붙인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;109&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;109&quot; data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;111&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;111&quot;&gt;클래스명을 통한 예측의 용이성&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;112&quot;&gt;재사용을 통한 코드의 간결화&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;113&quot;&gt;확장의 용이성&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;115&quot; data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;117&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;117&quot;&gt;카테고리를 나누는 기준이 작성사에 따라서 불분명해질 수 있음&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;118&quot;&gt;코드를 나누어서 작성해야 하기 때문에 CSS를 사용하기 번거로움&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;119&quot;&gt;잘못 사용하면 오히려 의도와 다르게 더 복잡해지고 번거로워지는 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;121&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;%EC%B4%9D-%EC%A0%95%EB%A6%AC&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;121&quot; data-ke-size=&quot;size23&quot;&gt;총 정리&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;123&quot; data-ke-size=&quot;size16&quot;&gt;각 방법론은 모두 같은 지향점을 갖고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;125&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;125&quot;&gt;코드의 재사용성 향상&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;126&quot;&gt;쉬운 유지보수 및 확장성&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;127&quot;&gt;클래스명 만으로도 의미와 예측 가능하도록 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;129&quot; data-ke-size=&quot;size16&quot;&gt;또한 각각의 방법론은 장점과 단점이 있기 떄문에 무엇이 절대적으로 좋다라는 것은 없는 것 같습니다. 그래서 개인적인 취향이나 함께 협업하는 동료들과 어떤 방법으로 맞춰나갈지 규칙만 잘 정한다면 어떤 것을 사용해도 상관없다고 생각합니다. 방법론은 방법론일뿐이기 때문에 프로젝트와 상황에 따라서 일관성 있게 사용하여 사용성을 높이고 프로젝트에서 동일한 코드를 유지하는 게 더 중요하다고 생각합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;133&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://whales.tistory.com/33&quot;&gt;https://whales.tistory.com/33&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://hackernoon.com/css-architecture-style-guides-for-frontend-developers-lj28332a&quot;&gt;https://hackernoon.com/css-architecture-style-guides-for-frontend-developers-lj28332a&lt;/a&gt;&lt;/p&gt;
&lt;h1 style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;136&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 id=&quot;css-preprocessors&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;136&quot;&gt;CSS Preprocessors&lt;/h1&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;138&quot; data-ke-size=&quot;size16&quot;&gt;CSS의 기술적 방법론&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;140&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;140&quot;&gt;웹에서는 표준 CSS만 동작할 수 있다. 하지만 CSS를 사용해본 사람들은 CSS가 얼마나 불편한지 느꼈을 것입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;141&quot;&gt;이러한 CSS를 똑똑하게 사용할 수 있도록 도와주는 도구인 CSS Preprocessor(CSS 전처리기)가 탄생했습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;142&quot;&gt;종류에는 Sass, Less, Stylus가 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;143&quot;&gt;위에 언급한 것처럼 웹에서는 CSS만 동작하기 때문에 CSS Preprocessor로 우선 작성하고, CSS로 컴파일해서 사용합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;144&quot;&gt;여기에서는 가장 성숙도가 높고 기능이 많은 Sass(SCSS)를 다루겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;146&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;148&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;148&quot;&gt;재사용성 - 공통 요소 또는 반복적인 항목을 변수 또는 함수로 작성&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;149&quot;&gt;개발 편이성 - 중첩, 상속, 함수 등을 통해 구조화된 코드로 편리하게 개발 가능&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;150&quot;&gt;직관성 - 위의 두가지를 통해 중복되는 코드, 무분별한 className을 줄일 수 있어서 직관적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;152&quot; data-ke-size=&quot;size16&quot;&gt;단점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;154&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;154&quot;&gt;기능이 많이 들어있어서 용량이 크다. (컴파일 과정에 시간이 소요된다.)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;155&quot;&gt;SCSS를 CSS로 변환하는 과정을 알기 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EB%B0%A9%EB%B2%95&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;157&quot; data-ke-size=&quot;size23&quot;&gt;컴파일 방법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;159&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;159&quot;&gt;node, npm, sass가 설치되어 있어야합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;160&quot;&gt;터미널에서 sass &quot;input.scss&quot; &quot;output.css&quot; 형식으로 컴파일할 scss 파일과 결과로 나타낼 css파일 형식을 지정해줍니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;161&quot;&gt;폴더일 경우 &quot;input폴더&quot; : &quot;output폴더&quot;로 나타낸다. ex) scss 폴더안에 있는 scss 파일들을 css폴더로 컴파일 하고싶을땐 터미널에 &quot;sass scss:css&quot; 로 입력 해주면 됩니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;162&quot;&gt;scss파일이 변결될 때 마다 자동으로 컴파일 해주기 위해서는 sass --watch scss:css 로 지정해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/elGULd/btsCc5CSi5x/yMBsQRNMF7VTD2qg79h5k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/elGULd/btsCc5CSi5x/yMBsQRNMF7VTD2qg79h5k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/elGULd/btsCc5CSi5x/yMBsQRNMF7VTD2qg79h5k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FelGULd%2FbtsCc5CSi5x%2FyMBsQRNMF7VTD2qg79h5k1%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;1328&quot; height=&quot;502&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;165&quot; data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;167&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;167&quot;&gt;중첩문 사용&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;168&quot;&gt;변수 선언 : $변수명 으로 선언 및 사용 가능하다. js와 같이 변수 범위도 존재한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;169&quot;&gt;!default : 이미 변수로 선언된 $변수값이 있다면 그것을 사용하고 없다면 지정한 값으로 사용한다. -삼항연산자와 비슷&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;170&quot;&gt;보간법 : 변수와 문자열 함께 사용 (템플릿 리터럴과 유사)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;171&quot;&gt;조건문, 비교연산자 : @if, @else if, @else로 사용가능&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;172&quot;&gt;함수 정의 : @mixin 변수명{ 스타일 속성 } 사용 : @include 변수명으로 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;scss&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot;&gt;&lt;code&gt;//scss
@mixin large-font($size: 25px) {
  font-size: $size;
  font-weight: bold;
  color: orange;
}

.box {
  width: 100px;
  @include large-font(30px);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;188&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;188&quot;&gt;반복문 : JS에서 처럼 list, map 반복문이 가능하다. @use &quot;sass:필요한 모듈이름&quot; 으로 내장모듈을 선언해서 사용하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;scss&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot;&gt;&lt;code&gt;//scss
@use &quot;sass:list&quot;;

$sizes: 20px, 40px 80px;
$fruits: (
  apple: &quot;A&quot;,
  banana: &quot;B&quot;,
);

//list
@each $size in $sizes {
  //반복문 요소 index 추출 방법 1부터 시작
  $index: list.index($sizes, $size);
  .icon-#{$index} {
    height: $size;
  }
}

//map
@each $key, $value in $fruits {
  .#{$key} {
    content: $value;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;217&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;217&quot; data-ke-size=&quot;size16&quot;&gt;sass의 내장 모듈들&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;219&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;219&quot;&gt;sass:math모듈은 숫자 에 대해 작동하는 함수를 제공합니다 .&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;220&quot;&gt;sass:string모듈을 사용하면 문자열을 쉽게 결합, 검색 또는 분할할 수 있습니다 .&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;221&quot;&gt;sass:color모듈은 기존 색상을 기반으로 새로운 색상을 생성하므로 색상 테마를 쉽게 구축할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;222&quot;&gt;sass:list모듈을 사용하면 목록 의 값에 액세스하고 수정할 수 있습니다 .&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;223&quot;&gt;sass:map모듈을 사용 하면 지도 의 키와 관련된 값 등을 조회할 수 있습니다 .&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;224&quot;&gt;sass:selector모듈은 Sass의 강력한 선택기 엔진에 대한 액세스를 제공합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;225&quot;&gt;sass:meta모듈은 Sass의 내부 작업에 대한 세부 정보를 노출합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;227&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sass-lang.com/documentation/modules&quot;&gt;https://sass-lang.com/documentation/modules&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://ghost4551.tistory.com/154&quot;&gt;https://ghost4551.tistory.com/154&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://ghost4551.tistory.com/155&quot;&gt;https://ghost4551.tistory.com/155&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1702819252055&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[CSS Preprocessor ] Sass(SCSS) (2)&quot; data-og-description=&quot;❗❗ 데브코스 35일차 (09.17) 어제에 이어 Sass(SCSS)에 대해 학습을 진행했다. 본 글은 SCSS를 중심으로 문법을 소개할 예정이다. SCSS를 학습하면서 느낀점은 멍청했던(?) CSS를 JS처럼 하나의 언어처럼&quot; data-og-host=&quot;ghost4551.tistory.com&quot; data-og-source-url=&quot;https://ghost4551.tistory.com/155&quot; data-og-url=&quot;https://ghost4551.tistory.com/155&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cZ9tcx/hyUPKz8ab5/tUJJM0ffQ8sZyVKANEElWk/img.png?width=800&amp;amp;height=303&amp;amp;face=0_0_800_303,https://scrap.kakaocdn.net/dn/dJ6HVW/hyULSs50j0/YOBAjQU6KexxwqlcI3kEBk/img.png?width=800&amp;amp;height=303&amp;amp;face=0_0_800_303,https://scrap.kakaocdn.net/dn/cszBgL/hyULWa8BB1/jpGjl8Enn6gdTmbWxpejU1/img.png?width=906&amp;amp;height=344&amp;amp;face=0_0_906_344&quot;&gt;&lt;a href=&quot;https://ghost4551.tistory.com/155&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ghost4551.tistory.com/155&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cZ9tcx/hyUPKz8ab5/tUJJM0ffQ8sZyVKANEElWk/img.png?width=800&amp;amp;height=303&amp;amp;face=0_0_800_303,https://scrap.kakaocdn.net/dn/dJ6HVW/hyULSs50j0/YOBAjQU6KexxwqlcI3kEBk/img.png?width=800&amp;amp;height=303&amp;amp;face=0_0_800_303,https://scrap.kakaocdn.net/dn/cszBgL/hyULWa8BB1/jpGjl8Enn6gdTmbWxpejU1/img.png?width=906&amp;amp;height=344&amp;amp;face=0_0_906_344');&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;[CSS Preprocessor ] Sass(SCSS) (2)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;❗❗ 데브코스 35일차 (09.17) 어제에 이어 Sass(SCSS)에 대해 학습을 진행했다. 본 글은 SCSS를 중심으로 문법을 소개할 예정이다. SCSS를 학습하면서 느낀점은 멍청했던(?) CSS를 JS처럼 하나의 언어처럼&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ghost4551.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>개발지식</category>
      <category>CSS Architecture</category>
      <category>CSS Preprocessors</category>
      <category>Frontend Roadmap</category>
      <category>frontend roadmap study</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/269</guid>
      <comments>https://ghost4551.tistory.com/269#entry269comment</comments>
      <pubDate>Sun, 17 Dec 2023 22:25:24 +0900</pubDate>
    </item>
    <item>
      <title>[FE_Roadmap] Package Managers</title>
      <link>https://ghost4551.tistory.com/268</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Package Managers&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;컴퓨터의 운영 체제를 위해 일정한 방식으로 컴퓨터 프로그램의 설치, 업그레이드, 구성, 제거 과정을 자동화하는 소프트웨어 도구들의 모임이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;npm&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;npm(node package manager)은 자바스크립트 패키지 매니저입니다. Node.js에서 사용할 수 있는 모듈들을 패키지화하여 모아둔 저장소 역할과 패키지 설치 및 관리를 위한 CLI(Command line interface)를 제공합니다. Node.js 생태계의 앱스토어나 플레이스토어 같은 역할을 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;npm 레지스트리에는 640,000개가 넘는 패키지가 포함되어 있으며, 패키지는 의존성(dependencies) 및 버전을 추적할 수 있도록 구성됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;package.json&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;package.json은 프로젝트 정보와 의존성(dependencies)을 관리하는 문서입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이미 작성된 package.json 문서는 어느 곳에서도 동일한 개발 환경을 구축할 수 있게 해줍니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;package-lock.json&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;package.json이 동일한 개발 환경 구축을 위한 정보를 가지고 있지만, 다양한 경우에 의해 동일한 개발 환경 구축에 문제가 발생할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;상위 모듈에서 사용하는 하위 모듈 중 일부가 버전이 변경될 경우 결과가 다르게 나타날 수 도 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이를 방지하기 위해 npm으로 node_modules의 구성 트리 또는 package.json을 수정하는 모든 작업에 대해 package-lock.json이 자동으로 생성됩니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모든 작업에 대해 자동 생성하므로 의존성 업데이트와 같은 버전 변경에 대해서도 동일한 모듈 트리를 생성할 수 있게 됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;최종적으로 위의 정보를 기준으로 로컬 폴더 node_modules 폴더에 설치가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;단점&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;일관적이지 않은 패키지 버전&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;시멘틱 버저닝으로 인한 버전 문제&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;lsl&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;code&gt;시멘틱 버저닝이란 간단히 말해 1.2.3 처럼 버전을 세 가지 숫자가 들어갈 수 있는 자리로 구분하고, 각각의 자리에 현재 버전이 이전 버전과 어떤 관계가 있는지 암시하도록 하는 방법입니다. 예를들어 eslint&quot;: &quot;^8.27.0 모듈을 설치할 경우 가장 앞 숫자인
가장 앞 숫자인 메이저 버전을 제외한 두 자리 (마이너, 패치) 버전까지는 변경을 허용할 수 있다는 의미입니다. 메이저 버전인 8이 변경되지 않는 범위에서는 8.17.10 버전이나, 8.18.0 버전 등이 모두 사용될 수 있습니다.&lt;/code&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;순차적인 설치로 인한 속도 문제&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Yarn v1&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 문제를 해결하기 위해 Facebook에서 만든 Yarn(Yet Another Resource Negotiator)은 자바스크립트 패키지 매니저가 나왔습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;yarn.lock 파일을 통해 패키지 버전 잠금을 지원하여 프로젝트에서 의존하는 모든 패키지를 어느 환경에서든 항상 동일한 버전으로 설치할 수 있게 만들어줬습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동시에 여러 패키지를 설치를 통해 속도가 개선되었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재 npm &amp;amp; Yarn v1&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현재는 서로 비슷한 수준의 속도와 npm에서도 shrinkwrap 옵션등을 통해 서로 비슷한 수준입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;node_modules 기반으로 매우 큰 공간을 차지합니다. 간단한 프로젝트도 수백 메가바이트의 node_modules 폴더가 필요합니다. 또한 많은 I/O 작업이 필요합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;447&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lve8A/btsBHs6KgXe/RUIp1jsw3OVEx8qu5XX2p0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lve8A/btsBHs6KgXe/RUIp1jsw3OVEx8qu5XX2p0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lve8A/btsBHs6KgXe/RUIp1jsw3OVEx8qu5XX2p0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flve8A%2FbtsBHs6KgXe%2FRUIp1jsw3OVEx8qu5XX2p0%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;540&quot; height=&quot;296&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;447&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;NPM 및 Yarn v1에서는 중복해서 설치되는 node_modules를 아끼기 위해 끌어올리기(Hoisting) 기법을 사용합니다. 이 때문에 유령 의존성 문제가 발생합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lAsnv/btsBFhZF5JL/mBerKzRfFyl2wPK5j2tc71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lAsnv/btsBFhZF5JL/mBerKzRfFyl2wPK5j2tc71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lAsnv/btsBFhZF5JL/mBerKzRfFyl2wPK5j2tc71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlAsnv%2FbtsBFhZF5JL%2FmBerKzRfFyl2wPK5j2tc71%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;696&quot; height=&quot;309&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Yarn Berry&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Yarn Berry는 Node.js를 위한 새로운 패키지 관리 시스템으로 2020년 1월 25일부터 정식 버전(v2)가 출시되어, 현재는 Babel과 같은 큰 오픈소스 레포지토리에서도 채택하고 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Yarn Berry는 기존의 &amp;ldquo;깨져 있는&amp;rdquo; NPM 패키지 관리 시스템을 혁신적으로 개선합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Yarn Berry는 node_modules를 생성하지 않습니다. 대신 .yarn/cache 폴더에 의존성의 정보가 저장되고, .pnp.cjs 파일에 의존성을 찾을 수 있는 정보가 기록됩니다. .pnp.cjs를 이용하면 디스크 I/O 없이 어떤 패키지가 어떤 라이브러리에 의존하는지, 각 라이브러리는 어디에 위치하는지를 바로 알 수 있습니다. (Plug&amp;rsquo;n&amp;rsquo;Play (PnP))&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;/* react 패키지 중에서 */
[&quot;react&quot;, [
  /* npm:17.0.1 버전은 */
  [&quot;npm:17.0.1&quot;, {
    /* 이 위치에 있고 */
    &quot;packageLocation&quot;: &quot;./.yarn/cache/react-npm-17.0.1-98658812fc-a76d86ec97.zip/node_modules/react/&quot;,
    /* 이 의존성들을 참조한다. */
    &quot;packageDependencies&quot;: [
      [&quot;loose-envify&quot;, &quot;npm:1.4.0&quot;],
      [&quot;object-assign&quot;, &quot;npm:4.1.1&quot;]
    ],
  }]
]],&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Yarn PnP 시스템에서 각 의존성은 Zip 아카이브로 관리됩니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;node_modules 디렉토리 구조를 생성할 필요가 없기 때문에 설치가 빠르다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;패키지는 버전마다 하나의 Zip 아카이브만을 가지기 때문에 중복해서 설치되지 않습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Yarn PnP은 의존성을 압축 파일로 관리하기 의존성을 Git으로 관리하여 사용한다면 &lt;code&gt;zero install&lt;/code&gt;의 장점을 누릴 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 Yarn Berry에서 의존성을 버전 관리에 포함하는 것을 Zero-Install이라고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;code&gt;토스팀 이렇게 Yarn Berry를 도입함으로써 JavaScript 의존성을 효율적이고 안전하게 다룰 수 있었습니다. 오래 걸리던 CI 속도를 60초 이상 단축하기도 했습니다.&lt;/code&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;pnpm&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;pnpm은 2017년에 만들어졌으며, npm의 설정을 바꿀 필요 없이 바로 사용가능하며, 속도와 안정성 등 다양한 기능 향상이 이루어지는 대체품으로, npm만 있다면 바로 사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;npm 또는 Yarn 클래식을 통해 의존성을 설치할 때, 모든 패키지는 모듈 디렉토리의 루트로 호이스트됩니다. 결과적으로, 소스 코드는 프로젝트에 의존성으로 추가되지 않은 의존성에 접근할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;npm &amp;amp; yarn v1 유령 의존성&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wdhwg/btsBDKujVX0/dLs32YP0BeKzMyekxK8kI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wdhwg/btsBDKujVX0/dLs32YP0BeKzMyekxK8kI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wdhwg/btsBDKujVX0/dLs32YP0BeKzMyekxK8kI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwdhwg%2FbtsBDKujVX0%2FdLs32YP0BeKzMyekxK8kI0%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;628&quot; height=&quot;279&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;454&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: #000000;&quot;&gt;pnpm은 symlink를 사용하여 프로젝트의 직접적인 의존성만을 모듈 디렉토리의 루트로 추가합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;489&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pDqgQ/btsBF1hJMnJ/TTxsdwnrhR2B7aDnrJktV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pDqgQ/btsBF1hJMnJ/TTxsdwnrhR2B7aDnrJktV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pDqgQ/btsBF1hJMnJ/TTxsdwnrhR2B7aDnrJktV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpDqgQ%2FbtsBF1hJMnJ%2FTTxsdwnrhR2B7aDnrJktV1%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;1021&quot; height=&quot;489&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;489&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: #000000;&quot;&gt;pnpm은 이러한 호이스트 방식 대신, 다른 dependencies를 해결하는 전략인 content-addressable storage를 사용했습니다. 따라서 모든 버전의 dependencies은 해당 폴더에 물리적으로 한번만 저장되므로, single source of truth를 구성하고, 상당한 디스크 공간을 절약할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;참고&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://heropy.blog/2018/02/18/node-js-npm&quot;&gt;https://heropy.blog/2018/02/18/node-js-npm&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://usage.tistory.com/147&quot;&gt;https://usage.tistory.com/147&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://toss.tech/article/node-modules-and-yarn-berry&quot;&gt;https://toss.tech/article/node-modules-and-yarn-berry&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://pnpm.io/ko/motivation&quot;&gt;https://pnpm.io/ko/motivation&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발지식</category>
      <category>Frontend Roadmap</category>
      <category>frontend roadmap study</category>
      <category>Package Managers</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/268</guid>
      <comments>https://ghost4551.tistory.com/268#entry268comment</comments>
      <pubDate>Sat, 9 Dec 2023 23:42:36 +0900</pubDate>
    </item>
    <item>
      <title>[FE_Roadmap] Web Security Knowledge</title>
      <link>https://ghost4551.tistory.com/267</link>
      <description>&lt;h1 id=&quot;https&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;0&quot;&gt;HTTPS&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k1NbW/btsz6h8gPNe/tABFV3Ick7o0lQHudC9KS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k1NbW/btsz6h8gPNe/tABFV3Ick7o0lQHudC9KS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k1NbW/btsz6h8gPNe/tABFV3Ick7o0lQHudC9KS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk1NbW%2Fbtsz6h8gPNe%2FtABFV3Ick7o0lQHudC9KS1%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;867&quot; height=&quot;510&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;6&quot;&gt;월드 와이드&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;웹에서 정보를 주고 받을 수 있는 프로토콜&lt;/b&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;7&quot;&gt;클라이언트와 서버 사이에 이루어지는 요청/응답(request/response) 프로토콜&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;8&quot;&gt;HTTP는 암호화하지 않은 평문 데이터 전송이므로,데이터 노출 및 변조의 위험이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;HTTPS&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;12&quot;&gt;HTTPS는 TLS/SSL인터넷 암호화 통신 프로토콜을 사용하여 암호화를 통해 평문 데이터 문제 해결한다. (무결성 보장)
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;13&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;13&quot;&gt;TLS 는 SSL의 업데이트 버전이라고 이해&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;14&quot;&gt;서버에서 HTTPS 프로토콜 사용을 위해 SSL 인증서를 CA기관으로부터 발급 받는다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;15&quot;&gt;80번 port를 사용하는 http와 달리 https는 443번 port를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;17&quot; data-ke-size=&quot;size16&quot;&gt;HTTPS 동작 과정&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;19&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;19&quot;&gt;클라이언트(브라우저)가 서버로 최초 연결 시도를 함&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;20&quot;&gt;서버는 SSL 인증서를 브라우저에게 넘겨줌&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;21&quot;&gt;브라우저는 인증서의 유효성을 검사하고(CA기관의 공개키로 확인) 세션키를 발급함&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;22&quot;&gt;브라우저는 세션키를 보관하며 추가로 서버의 공개키로 세션키를 암호화하여 서버로 전송함&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;23&quot;&gt;서버는 개인키로 암호화된 세션키를 복호화하여 세션키를 얻음&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;24&quot;&gt;클라이언트와 서버는 동일한 세션키를 공유하므로 데이터를 전달할 때 세션키로 암호화/복호화를 진행함&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;content-security-policy&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;26&quot;&gt;Content Security Policy&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;28&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;28&quot;&gt;CSP는 XSS 및 데이터 삽입 공격 을 비롯한 특정 유형의 공격을 감지하고 완화하는 데 도움이 되는 추가 보안 계층이다. 이러한 공격은 데이터 도용에서 사이트 훼손, 맬웨어 배포에 이르기까지 모든 것에 사용된다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;29&quot;&gt;스크립트를 실행 가능한 화이트리스트를 만들어 화이트리스트 도메인에서 로드된 스크립트만 실행한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;30&quot;&gt;CSP는 스크립트, 스타일 시트, 이미지, 글꼴, 개체, 미디어 (오디오, 비디오), 프레임 및 양식 작업을 포함한 모든 동적 원본에 대해 허용된 원본을 지정할 수 있다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;31&quot;&gt;CSP 는 Content-Security-Policy HTTP 헤더를 통해 설정된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1699620576149&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Content-Security-Policy: default-src 'self'; img-src *; media-src example.org example.net; script-src userscripts.example.com

웹 사이트 관리자는 웹 응용 프로그램 사용자가 자신의 콘텐츠에 모든 원본의 이미지를 포함할 수 있도록 허용하지만 오디오 또는 비디오 미디어는 신뢰할 수 있는 공급자로 제한하고 모든 스크립트는 신뢰할 수 있는 코드를 호스팅하는 특정 서버로만 제한하려고 한다.​&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;39&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;39&quot;&gt;일반적인 CSP는 URL 허용 목록을 기반으로 하기 때문에 우회가 가능하다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;40&quot;&gt;엄격한 CSP를 통해 조금 더 안전하게 사용할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;41&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;41&quot;&gt;임시값 기반 CSP : 런타임 시 임의의 숫자를 생성하여 CSP에 포함하고 페이지의 모든 스크립트 태그와 연결한다. 공격자는 해당 스크립트에 대한 올바른 임의의 숫자를 추측해야 하기 때문에 페이지에 악성 스크립트를 포함하고 실행할 수 없습니다. 이것은 숫자를 추측할 수 없고 런타임 시 모든 응답에 대해 새로 생성된 경우에만 작동한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;42&quot;&gt;해시 기반 CSP : 모든 인라인 스크립트 태그의 해시가 CSP에 추가됩니다. 각 스크립트에는 다른 해시가 있습니다. 공격자는 페이지에 악성 스크립트를 포함하고 실행할 수 없습니다. 해당 스크립트의 해시가 CSP에 있어야 하기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;corscross-origin-resource-sharing&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;44&quot;&gt;CORS(Cross-Origin Resource Sharing)&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6NtxK/btsz6hHclKv/tnnzJV6SmdclVYB1DNYZSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6NtxK/btsz6hHclKv/tnnzJV6SmdclVYB1DNYZSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6NtxK/btsz6hHclKv/tnnzJV6SmdclVYB1DNYZSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6NtxK%2Fbtsz6hHclKv%2FtnnzJV6SmdclVYB1DNYZSk%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;957&quot; height=&quot;675&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;48&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;48&quot;&gt;교차 출처 리소스 공유는 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;브라우저&lt;/b&gt;에 알려준다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;49&quot;&gt;CORS의 에러가 생기는 이유는 SOP를 위반한 에러이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;50&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;50&quot;&gt;SOP(same-origin policy, 동일 출처 정책) : 출처가 동일한 프로토콜, 포트번호, 도메인에서만 자원을 사용가능 하도록 하는 보안정책이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;51&quot;&gt;즉, CORS 에러는 요청 브라우저에서 다른 도메인(프로토콜, 포트번호)을 가지는 서버로 요청이 갈때 브라우저에서 발생하는 보안정책이다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;52&quot;&gt;CSRF(사이트 간 요청 위조), XSS(교차 사이트 스크립팅)을 보안할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;54&quot; data-ke-size=&quot;size16&quot;&gt;CORS를 사용하는 요청&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;56&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;56&quot;&gt;XMLHttpRequest 또는 Fetch API&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;57&quot;&gt;웹 글꼴 (@font-face)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;58&quot;&gt;WebGL (Web Graphics Library)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;59&quot;&gt;캔버스에 그린 이미지/비디오 프레임 (drawImage())&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;60&quot;&gt;이미지의 CSS 모양 (CSS Shapes from images)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;62&quot; data-ke-size=&quot;size16&quot;&gt;CORS 원리&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;64&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;64&quot;&gt;단순 요청(Simple requests)
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;65&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;65&quot;&gt;단순 요청은 서버에 API를 요청하면 서버는 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에 보낸다. 브라우저는 Access-Control-Allow-Origin 헤더를 확인해서 CORS 동작을 처리한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;66&quot;&gt;요청 메서드(method)는 GET, HEAD, POST 만 가능하다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;67&quot;&gt;Accept, Accept-Language, Content-Language, Content-Type를 제외한 헤더를 사용하면 안 됩니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;68&quot;&gt;Content-Type 헤더는 application/x-www-form-urlencoded, multipart/form-data, text/plain 만 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;69&quot;&gt;프리플라이트 요청(preflighted)
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;70&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;70&quot;&gt;Preflight 요청은 서버에 예비 요청을 보내서 안전한지 판단한 후 본 요청을 보내는 방법이다&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;71&quot;&gt;먼저 OPTIONS 메서드를 통해 다른 도메인의 리소스로 HTTP 요청을 보내 실제 요청이 전송하기에 안전한지 확인 합니다. cross-origin 요청은 유저 데이터에 영향을 줄 수 있기 때문에 이와같이 미리 전송한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;owasp-security-risks&quot; style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;73&quot;&gt;OWASP Security Risks&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;75&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;75&quot;&gt;OWASP(The Open Web Application Security Project)는 오픈소스 웹 애플리케이션 보안 프로젝트이다. 주로 웹에 관한 정보노출, 악성 파일 및 스크립트, 보안 취약점 등을 연구하여 웹 애플리케이션 취약점 중에서 빈도가 많이 발생하고, 보안상 영향을 크게 줄 수 있는 것들 10가지를 선정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;77&quot; data-ke-size=&quot;size16&quot;&gt;2017 기준 10가지 종류&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;79&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;79&quot;&gt;Injection&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;80&quot;&gt;Broken authentication&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;81&quot;&gt;Sensitive data exposure&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;82&quot;&gt;XML external entities (XXE)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;83&quot;&gt;Broken access control&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;84&quot;&gt;Security misconfigurations&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;85&quot;&gt;Cross site scripting (XSS)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;86&quot;&gt;Insecure deserialization&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;87&quot;&gt;Using components with known vulnerabilities&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;88&quot;&gt;Insufficient logging and monitoring&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;90&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트 측 보안 방법&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;92&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;92&quot;&gt;innerHTML 대신 innerText 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;93&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;93&quot;&gt;innerHTML 속성은 문자열 자체를 수정할 수 있기 때문에, 악의를 가진 해커가 script 태그를 사용해 JavaScript 코드를 작성한 뒤 실행되도록 만들 수 있다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;94&quot;&gt;HTML5에서 innerHTML과 함께 삽입된 script 태그가 실행되지 않도록 지정했지만, 우회가 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;95&quot;&gt;eval() 함수 사용 x&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1699620652143&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(eval('2 + 2')); // Expected output: 4&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-line=&quot;100&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot; data-line=&quot;100&quot;&gt;eval()은 인자로 받은 코드를 caller의 권한으로 수행하는 위험한 함수입니다. 악의적인 영향을 받았을 수 있는 문자열을 eval()로 실행한다면, 당신의 웹페이지나 확장 프로그램의 권한으로 사용자의 기기에서 악의적인 코드를 수행하는 결과를 초래할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;92&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;101&quot;&gt;XML 또는 JSON을 동적으로 작성 x&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;102&quot;&gt;클라이언트에 의존 x (사용자가 클라이언트측 로직을 제어할 수 있음. 우회하거나 코드 중단점 설정, 플러그인 등을 통해 제어가능)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;103&quot;&gt;클라이언트에 중요한 정보 보관 x&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;104&quot;&gt;클라이언트에서 암호화 수행 x&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;105&quot;&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;107&quot; data-ke-size=&quot;size16&quot;&gt;https&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;109&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;109&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/98&quot;&gt;https://mangkyu.tistory.com/98&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;110&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Glossary/SSL&quot;&gt;https://developer.mozilla.org/ko/docs/Glossary/SSL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;112&quot; data-ke-size=&quot;size16&quot;&gt;cors&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;114&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;114&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/HTTP/CORS&quot;&gt;https://developer.mozilla.org/ko/docs/Web/HTTP/CORS&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;115&quot;&gt;&lt;a href=&quot;https://beomy.github.io/tech/browser/cors/&quot;&gt;https://beomy.github.io/tech/browser/cors/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;117&quot; data-ke-size=&quot;size16&quot;&gt;csp&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;119&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;119&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;120&quot;&gt;&lt;a href=&quot;https://web.dev/i18n/ko/strict-csp/&quot;&gt;https://web.dev/i18n/ko/strict-csp/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;122&quot; data-ke-size=&quot;size16&quot;&gt;OWASP&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #171717; text-align: start;&quot; data-line=&quot;124&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;124&quot;&gt;&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/AJAX_Security_Cheat_Sheet.html&quot;&gt;https://cheatsheetseries.owasp.org/cheatsheets/AJAX_Security_Cheat_Sheet.html&lt;/a&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot; data-line=&quot;125&quot;&gt;&lt;a href=&quot;https://sucuri.net/guides/owasp-top-10-security-vulnerabilities-2020/&quot;&gt;https://sucuri.net/guides/owasp-top-10-security-vulnerabilities-2020/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발지식</category>
      <category>Frontend Roadmap</category>
      <category>frontend roadmap study</category>
      <category>Web Security Knowledge</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/267</guid>
      <comments>https://ghost4551.tistory.com/267#entry267comment</comments>
      <pubDate>Fri, 10 Nov 2023 21:51:50 +0900</pubDate>
    </item>
    <item>
      <title>미래산업과학고등학교 개발자 특강</title>
      <link>https://ghost4551.tistory.com/266</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;지인의 소개로 미래산업과학고등학교에 개발자 특강을 다녀왔습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;고등학생들에게 특강은 처음이라 무엇을&amp;nbsp;준비해야 하는지&amp;nbsp;막막했지만,&amp;nbsp;IT특성화고&amp;nbsp;특성상&amp;nbsp;IT관련&amp;nbsp;대학 진학을 생각하는 학생들이 많았기 때문에, 제가 고등학생 시절 대학 입시를 준비하면서 겪었던 고민들을 정리하기 시작했습니다. 또한, 사전 질문지를 통해 학생들이 어떤 고민과 질문이 있는지 미리 파악했습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;정리한 고민들을 토대로 내가 &quot;고등학생으로 돌아간다면&quot;이라고 생각하고 &quot;나는 이렇게 했을 것 같다&quot;라는 주제로 준비했습니다. 또한, 쏘카에 몸을 담고 있어서 회사 소개와 더불어 쏘카에서 어떤 일을 하고 어떤 식으로 일하는지 예시를 통해 현업에 대해서도 소개했습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;특강을 진행하면서 학생들의 반응이 조용하길래 &quot;내가 준비한 것이 도움이 되지 않은가?&quot; 의문점이 들었지만, 쉬는 시간과 특강 종료 후, 일부 학생들이 PPT에 포함된 내용을 물어보고, 자신들의 고민과 궁금한 점을 여기저기 질문하기 시작했습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;적어도 이 친구들에게는 도움이 될 수 있었구나 하고 내심 뿌듯한 마음을 안고 특강을 마무리할 수 있었습니다. (벌써부터 소프트웨어를 만들고, 팀 프로젝트를 하는 친구들을 보면서 많은 자극도 느낄 수 있었습니다.)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;아직 부족한 것이 많지만, 더 많은 분들에게 도움이&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;될 수 있도록 꾸준히 노력하겠습니다.&lt;/span&gt;&lt;/span&gt;&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/dhMYjX/btszwc5Qvb3/hGwCvqhj6xofdqxKb066Nk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhMYjX/btszwc5Qvb3/hGwCvqhj6xofdqxKb066Nk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot; data-filename=&quot;특강1.jpeg&quot; data-widthpercent=&quot;57.12&quot; style=&quot;width: 56.456%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhMYjX/btszwc5Qvb3/hGwCvqhj6xofdqxKb066Nk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdhMYjX%2Fbtszwc5Qvb3%2FhGwCvqhj6xofdqxKb066Nk%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;1440&quot; height=&quot;1081&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doHGa8/btszvcL1Id0/80EWH6ZZE1vEysClu7uvd0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doHGa8/btszvcL1Id0/80EWH6ZZE1vEysClu7uvd0/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1440&quot; data-filename=&quot;특강2.jpeg&quot; data-widthpercent=&quot;42.88&quot; style=&quot;width: 42.3812%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doHGa8/btszvcL1Id0/80EWH6ZZE1vEysClu7uvd0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoHGa8%2FbtszvcL1Id0%2F80EWH6ZZE1vEysClu7uvd0%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;1440&quot; height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;특강.jpeg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buSyyV/btszwcY40ZX/QddnQIcZqY6banKxzndPu1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buSyyV/btszwcY40ZX/QddnQIcZqY6banKxzndPu1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buSyyV/btszwcY40ZX/QddnQIcZqY6banKxzndPu1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuSyyV%2FbtszwcY40ZX%2FQddnQIcZqY6banKxzndPu1%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;1440&quot; height=&quot;1080&quot; data-filename=&quot;특강.jpeg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>일기장</category>
      <category>개발자 특강</category>
      <category>미래산업과학고등학교</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/266</guid>
      <comments>https://ghost4551.tistory.com/266#entry266comment</comments>
      <pubDate>Mon, 30 Oct 2023 20:28:45 +0900</pubDate>
    </item>
    <item>
      <title>[React 무한 스크롤 구현하기]  (feat. typeScript, useHook)</title>
      <link>https://ghost4551.tistory.com/264</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;무한 스크롤은 많은 데이터를 세분화하여 필요시에 데이터를 요청해서 받아오면서 성능을 극대화한다. react+typeScript에서 무한 스크롤을 구현하는 방법을 useHook으로 관리하여 사용하는 법을 소개할 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;h-tag-5&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Intersection Observer API&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Intersection Observer API는 대상 요소가 상위 요소 또는 최상위 문서의 뷰포트 와 교차하는 변경 사항을 비동기식으로 관찰하는 방법을 제공합니다. 따라서 Scroll 이벤트를 걸어서 스크롤 할 때마다 함수가 실행되는 스크롤 이벤트보다 훨씬 성능이 좋다.&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;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;파일 구성&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useObserver (무한스크롤 useHook)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt; Item (무한스크롤하는 개별 요소) &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Main (Item List를 생성하는 상위 컴포넌트)&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;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useObserver.ts&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666423399363&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useState } from 'react'

const MaxPAge = 10 // 페이지 최대 사이즈

const useObserver = () =&amp;gt; {
  const [page, setPage] = useState(1) // pagination을 위한 변수
  const [isFetching, setIsFetching] = useState(true) // Loading처리를 위한 변수
  const [lastIntersecting, setLastIntersecting] =
    useState&amp;lt;HTMLDivElement | null&amp;gt;(null) // 구독할 타켓 정보

  //observer 콜백함수
  const onIntersect: IntersectionObserverCallback = (entries, observer) =&amp;gt; {
    entries.forEach((entry) =&amp;gt; {
      // 뷰 포트에 마지막 요소가 들어올 때,
      if (entry.isIntersecting) {
        // 페이지 최대가 넘어가지 않을 때
        if (page &amp;lt; MaxPAge) {
          // page값에 1을 더하여 새 fetch 요청을 보내게됨
          setPage((prev) =&amp;gt; prev + 1)
          setIsFetching(true)
        }
        // 기존 타겟을 unobserve한다.
        observer.unobserve(entry.target)
      }
    })
  }

  useEffect(() =&amp;gt; {
    if (!lastIntersecting) return
    //observer 인스턴스를 생성한 후 구독
    const observer = new IntersectionObserver(onIntersect, { threshold: 0.5 })
    //observer 생성 시 observe할 target 요소는 배열의 마지막 타켓으로 지정
    observer.observe(lastIntersecting)

    return () =&amp;gt; observer &amp;amp;&amp;amp; observer.disconnect()
  }, [lastIntersecting])

  // 사용할 hook state값 내보내기
  return { page, setPage, isFetching, setIsFetching, setLastIntersecting }
}

export default useObserver&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 필요한 state 값 정의하기&lt;/span&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;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;무한 스크롤 발생 시 api 요청에 넣을 page state를 정의한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;로딩 처리 UI가 필요하다면 isFetching state를 정의한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;무한 스크롤을 발생시키는 조건인 타켓의 정보를 가진 lastIntersection state를 정의한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 옵저버 타켓(lastIntersection)을 의존성을 넣은 useEffect hook을 만들어준다.&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;생성자를 호출하고 임계값이 한 방향 또는 다른 방향으로 교차할 때마다 실행할 콜백 함수를 전달하여 교차 관찰자를 만듭니다. 두 번째 인자로 부가적인 옵션을 지정할 수 있습니다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;observer.onserve(target)을 통해 관찰자를 생성한 후에는 대상 요소를 지정한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;마지막으로 return을 통해 후속처리 작업을 진행해 준다. (등록한 관찰자 제거)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; color: #000000;&quot;&gt;3. 콜백 함수를 정의한다.&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;onIntersect 함수는 관찰자 대상이 지정한 임계값 (threshold)을 충족할 때 호출된다. 콜백은&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry&quot;&gt;IntersectionObserverEntry&lt;/a&gt;&lt;/span&gt;객체 목록과 관찰자를 수신합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;뷰 포트에 마지막 요소가 들어오면 처리할 작업을 지정한다. (여기에서는 최대 10번만 무한 스크롤 발생을 위해 10보다 작을 경우에만 페이지 요소를 증가시켜 api에 해당 page 요소를 함께 보내도록 작업)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마지막으로 앞에 지정했던 타켓을 관찰자 대상에서 제거한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Main&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666432023284&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Main = () =&amp;gt; {
  const [list, setList] = useState&amp;lt;TItem[]&amp;gt;([])
  const { page, setPage, isFetching, setIsFetching, setLastIntersecting } =
    useObserver() // 정의한 observer에서 useHook가져오기

  useEffect(() =&amp;gt; {
    ;(async () =&amp;gt; {
      const data = await getCharacterAPI(page)
      data &amp;amp;&amp;amp; setCharacterList([...list, ...data])
    })()
  }, [page])

  return (
    &amp;lt;&amp;gt;
      {list?.map((data, idx) =&amp;gt; (
        &amp;lt;div ref={setLastIntersecting}&amp;gt; // 구독할 대상
          &amp;lt;Item data={data}/&amp;gt;
        &amp;lt;/div&amp;gt;
      ))}
      // 로딩 처리
      {isFetching &amp;amp;&amp;amp; (
        &amp;lt;div style={{ textAlign: 'center' }}&amp;gt;
          &amp;lt;Spinner /&amp;gt;
        &amp;lt;/div&amp;gt;
      )}
    &amp;lt;/&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. List를 순회하면서 하위 컴포넌트를 생성해 주고 ref로 구독할 element를 정의한다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. page가 변경될 때 api 요청을 위해 useEffect hook을 이용하여 page state를 의존성으로 넣어준다.&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;기존 데이터에 새로 받아온 데이터를 누적시켜서 정의해 준다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;무한 스크롤은 서버의 데이터가 존재하지 않으면 무한 스크롤을 더 이상 발생시키면 안 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;따라서 서버에서 마지막 데이터를 알려주는 정보가 있다면 반환된 api 정보를 가지고 마지막 데이터라면 어떠한 상태를 두고 false 값으로 지정하여 더 이상 무한 스크롤을 발생시키지 않도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1666432488373&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const [lastPage, setLastPage] = useState(true)
  ...
  ...

  
 if (entry.isIntersecting) {
    // 페이지 최대가 넘어가지 않을 때
    if (lastPage) {
      // page값에 1을 더하여 새 fetch 요청을 보내게됨
      setPage((prev) =&amp;gt; prev + 1)
      setIsFetching(true)
    }
    // 기존 타겟을 unobserve한다.
    observer.unobserve(entry.target)
 }
 
return { ..., setLastPage }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1666432770552&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Main

useEffect(() =&amp;gt; {
;(async () =&amp;gt; {
  const data = await getCharacterAPI(page)
  if(data.last) setLastPage(false) // api에서 마지막 데이터를 반환 받은 경우
  data &amp;amp;&amp;amp; setCharacterList([...list, ...data])
})()
}, [page])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 페이지 정보임을 나타내는 state를 하나 더 정의하여 옵저버를 통해 page state 바꾸기 전에 체크한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 setLastPage를 함께 상태를 내보내서 Main에서 api를 통해 반환된 정보를 통해 setLastPage(false) 로 상태를 업데이트하여 사용하면 된다.&lt;/p&gt;</description>
      <category>개발지식</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/264</guid>
      <comments>https://ghost4551.tistory.com/264#entry264comment</comments>
      <pubDate>Sat, 22 Oct 2022 19:04:48 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크캠프] 키오스크 프로젝트</title>
      <link>https://ghost4551.tistory.com/263</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4번째 프로젝트도 2주간 진행됐다. 주제는 카페 키오스크 주문 서비스였는데, 운동을 좋아해서 헬스 키오스크 컨셉으로 프로젝트를 진행했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로젝트 주요 기능은 키오스크 주문 시스템처럼 카테고리 별 상품이 존재하고, 상품을 클릭하면 상품 정보와 함께 부가적인 옵션과 수량을 선택할 수 있다. 선택된 상품은 장바구니 리스트에 보이고 최종적으로 결제를 누르면 결제 내역과 함께 결제가 진행된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;프로젝트&amp;nbsp;후기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번 프로젝트에는 큰 제약이나 요구사항이 없었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프론트는 React + TypeScript, 백엔드는 Nest.js + TypeORM으로 개발을 진행했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;환경&amp;nbsp;세팅&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번 프로젝트에서는 React, Nest를 사용하는데, 별도로 배포를 진행했어야 했기 때문에 프로젝트 폴더를 어떻게 구상할까 고민하다가 다른 동료들과 토론 과정에서 모노레포라는 것을 알게 됐다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프론트, 백엔드 둘다 공통적인 node모듈을 사용하기 때문에 최상위에서 공통으로 관리하면 효율적이라 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또한 prettier 설정도 전역으로 관리할 수 있어서 편리하다고 판단했다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모노레포란 버전 관리 시스템에서 두 개 이상의 프로젝트 코드가 동일한 저장소에 저장되는 소프트웨어 개발 전략&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 해당 관련 자료를 찾아보면서 구성을 했다. 제일 간단한 yarn workspace를 이용해서 모노레포를 구축했고, 간단한 환경 설정과 함께 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://github.com/minsu-zip/monorepo-react-backend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;보일러 플레이트&lt;/a&gt;&lt;span style=&quot;color: #000000;&quot;&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;개발 (&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://github.com/minsu-zip/web-kiosk-parkminsu&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;레포주소&lt;/a&gt;&lt;/span&gt;)&lt;/b&gt;&lt;/span&gt;&lt;/h2&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/pJSvT/btrL33wBLnj/wKpsnsttsKUSGYQQVdAG20/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pJSvT/btrL33wBLnj/wKpsnsttsKUSGYQQVdAG20/img.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;501&quot; data-is-animation=&quot;true&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pJSvT/btrL33wBLnj/wKpsnsttsKUSGYQQVdAG20/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpJSvT%2FbtrL33wBLnj%2FwKpsnsttsKUSGYQQVdAG20%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;800&quot; height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bci3Ev/btrL5R3cl8T/khhWcKmv9xWJwRQ5EPoHj0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bci3Ev/btrL5R3cl8T/khhWcKmv9xWJwRQ5EPoHj0/img.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;501&quot; data-is-animation=&quot;true&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bci3Ev/btrL5R3cl8T/khhWcKmv9xWJwRQ5EPoHj0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbci3Ev%2FbtrL5R3cl8T%2FkhhWcKmv9xWJwRQ5EPoHj0%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;800&quot; height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌) 상품 리스트&amp;nbsp; 우) 상품 정보&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0qpKh/btrL3RXt3qJ/kdTfFwNasGnYW4fUJjf870/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0qpKh/btrL3RXt3qJ/kdTfFwNasGnYW4fUJjf870/img.gif&quot; data-alt=&quot;상품 장바구니&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0qpKh/btrL3RXt3qJ/kdTfFwNasGnYW4fUJjf870/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b0qpKh/btrL3RXt3qJ/kdTfFwNasGnYW4fUJjf870/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;501&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상품 장바구니&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;개발&amp;nbsp;이슈&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결제 관련 기능은 구현하지 못했다. 처음 프로젝트 기획 당시 컴포넌트 재활용을 신경써보자는 생각에 아토믹 디자인 패턴을 생각하여 바텀-업 방식으로 개발을 진행했다. 하지만 확장성을 위한 props나 컴포넌트 설계에 너무 많은 시간이 소요되어 중간 과정에서 이를 포기하고, 탑-다운 방식으로 개발로 진행을 바꿨다. 이 과정에서 컴포넌트 설계가 꼬이면서 컴포넌트 리팩토링 하는데 많은 시간이 소요됐다. 그리고, 이번 프로젝트 목표가 테스트 코드를 작성해 보자는 목표를 가지고 있어서 마지막 하루는 테스트 코드를 학습하고 적용했다. 핑계라면 핑계인 이와 같은 이유들 때문에 기능 개발은 전부 하지 못했지만, 내가 생각한 목표를 이룰 수 있었다.&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;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;테스트 코드&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;컴포넌트가 정상적으로 렌더링 되고 있는지, 사용자 행동에 따른 컴포넌트가 정상적으로 수행되는지 판단하기 위해 테스트 코드와 API 데이터 모킹 작업을 수행했다. (해당 내용은 별도로 정리했기 때문에 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://ghost4551.tistory.com/261&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기에서&lt;/a&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;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;협업&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개인 프로젝트로 진행했지만, 여러 명에서 협업을 진행하고 싶어서 같은 조로 짜인 분들과 협업을 진행했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;erd, 기능 명세, 자료 등을 같이 작성하면서 협업을 진행했고, 코드 리뷰를 통해 서로의 성장을 도모했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(협업 자료는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://flawless-oxygen-0b4.notion.site/3-980964d7d7f649f8914c4ccc2dcc9597&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기에서&lt;/a&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 data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;아토믹 디자인&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;도입 이유 : 현재 프로젝트에서도 아토믹 디자인 패턴을 기반하여 작성하게 된다면, 버튼, 모달, 메뉴 정보 등 재사용할 것이 많다고 판단해서 간략하게 도입을 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어려웠던 점&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;재사용성을 위해 props를 어디까지 허용하고 줘야 하는지 판단하기가 힘들었다. 버튼 컴포넌트를 활용하여 이곳저곳에서 사용되는데 디자인과 기능이 제각각 다른데 큰 버튼, 작은 버튼, 부가적인 기능을 하는 버튼 등의 목적에 따라 다 나누자니, 재활용에 장점이 잃는 것 같고, 그렇다고 props로 전부 해결하자니 너무 로직이 더러워져서 무엇이 더 좋을지 고민하는데 많은 시간이 할애됐다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;그래서 아토믹 디자인 패턴을 잠시 접어두고 상단 페이지부터 개발하기 시작했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;처음에는 개발 속도가 조금 높았지만, 시간이 갈수록 똑같은 컴포넌트를 이곳저곳에서 사용되는 문제점이 있었다. 그리고 구조 설계를 생각 없이 하다 보니, props drilling이 많이 일어나게 됐다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&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;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이미지 hover시 변경 이벤트&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이미지에 마우스를 hover 할 경우 다른 이미지로 바뀌고 떼었을 경우 원래 이미지로 돌아오는 로직을 구상했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 이미지 컴포넌트에 moveOver, moveOut 함수를 통해 이미지를 변경했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 이렇게 할 경우 hover 시 url이 변경됨에 따라 이미지 네트워크가 지속적으로 새로 요청하는 현상이 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 이를 개선하고자 다른 동료들과 토론을 했고, 토론 과정에서 여러 가지 해결 방법이 나왔다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;처음부터 두 개의 이미지를 보여주는데 z-index로 구분을 지어서 나타내는 방법의 제안이 있었다. 하지만 이렇게 할 경우 hover를 하지 않더라도 모든 이미지를 불러와야 하는 단점이 있었다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;끊임없는 토론 과정에서 단순하게 css로 처리하는 방법에 대한 의견이 나왔다. emotion를 사용하고 있었기 때문에 props로 전달하여 hover 시 해당 속성의 값을 바꾸는 식으로 문제를 해결할 수 있었다. 이를 통해 hover 시 단 한 번만 두 번째 이미지가 요청되고 이후에는 캐싱 된 이미지를 사용해 네트워크 비용을 절감할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;토론 과정에서 똑같은 결과지만 다양한 접근법이 있다는 것을 알 수 있었다. 그리고 토론을 통해 점점 향상된 결과물이 될 수 있었다. 이를 통해 혼자 문제를 해결하는 것보다 함께 해결하는 것이 더 나은 결과를 만들어낼 수 있음을 깨달을 수 있었던 계기였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(사실상 단순한 문제와 해결법이지만, 당시에는 그런 생각이 나지 않았고 이를 다른 분들과 해결하는 과정이 재밌었기에 작성했다.)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;5~6주차 전체&amp;nbsp;회고&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개인 프로젝트지만, 협업을 주도하여 같이 erd, 기능 명세를 고민하고 작성하는 경험이 재밌었고, 실무적인 활동을 하는 느낌을 받아서 매우 좋았다. 또 각자 잘하는 부분이 있었기에 동료들의 장점을 활용하여 개발할 수 있어서 편했다 ㅎㅎ.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 이번 프로젝트를 기획할 때, 아토믹 디자인을 생각하며 바텀-업으로 개발하는 것과 테스트 코드를 작성하는 것이 목표였다. 테스트 코드는 목표한 바를 이뤘지만, 아토믹은 잘 활용하지 못해서 매우 아쉬웠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;타입스크립트 또한 많이 사용해 보지 않아서 많은 문제를 겪었지만 해결하는 과정에서 타입스크립트와 조금이나마 친해질 수 있어서 좋았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;+ 뜬금없이 나타난 Nest.js 때문에 고생을 많이했다. 학습하기 제일 어려웠다. (스프링의 객체지향 관점으로 만들어졌기 때문에 어려웠다.) 그리고 TypeORM으로 query문 작성하는 것도 어려웠다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;동료들이 없었으면 백엔드 포기했을지도...&lt;/span&gt;&lt;/p&gt;</description>
      <category>우아한테크캠프5기</category>
      <category>우테캠 회고록</category>
      <category>우테캠5기 키오스크 프로젝트</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/263</guid>
      <comments>https://ghost4551.tistory.com/263#entry263comment</comments>
      <pubDate>Wed, 14 Sep 2022 02:14:42 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크캠프] 가계부 프로젝트</title>
      <link>https://ghost4551.tistory.com/262</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다 끝나고서야 정리하는 3~4주차 3번째 프로젝트...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3번째 프로젝트는 2주간 &quot;가계부 서비스&quot; 프로젝트였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;페이지는 크게 수입, 지출 리스트를 볼 수 있는 메인 페이지, 달력 차트로 내역을 볼 수 있는 페이지, 차트로 내역을 볼 수 있는 페이지를 개발하는 것이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;프로젝트&amp;nbsp;후기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FE 요구사항&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;객체 활용 프로그래밍&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-nodeid=&quot;36&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Model 과 View 역할을 나누고 옵저버 패턴을 활용해서 적용한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;history API 와 라우팅&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-nodeid=&quot;41&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대 분류에 해당하는 메뉴에 대해서 history API 를 적용해서 page routing 을 시도한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Fetch API와 Promise 패턴&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-nodeid=&quot;46&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;catch 를 활용한 에러처리를 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-nodeid=&quot;48&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;서버와 통신을 하는 동안 loading indicator 를 노출한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;차트 개발&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-nodeid=&quot;53&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Canvas, SVG 기반으로 구현한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-nodeid=&quot;43&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BE 요구사항&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-nodeid=&quot;66&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-nodeid=&quot;67&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Express를 이용해 프론트에 필요한 API를 개발한다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-nodeid=&quot;71&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;AWS EC2를 이용해 배포를 진행한다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-nodeid=&quot;73&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;VPC를 설정하고 서버와 DB를 분리한다.&lt;/span&gt;&lt;/li&gt;
&lt;li data-nodeid=&quot;75&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;스크립트를 이용해서 자동 배포가 이루어지도록 구성해 본다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;환경&amp;nbsp;세팅&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;VanillaJS(클래스형), &lt;span style=&quot;background-color: #ffffff;&quot;&gt;babel, webpack,&lt;/span&gt; &lt;span style=&quot;background-color: #ffffff;&quot;&gt;node.js express, &lt;span style=&quot;background-color: #ffffff;&quot;&gt;webpack-dev&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;-middleware, &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;aws 배포 환경으로 구성했다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;지난번 프로젝트 때 환경설정과 유사하게 진행했다.달라진 점은 프론트 웹팩 설정에서 개발 모드, 배포 모드 설정을 다르게 했다. 개발모드일 때만 소스트리를 추가하여 디버깅을 원활하게 진행했다. 그 외에는 2주차 프로젝트와 유사하게 구성했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;개발 (&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; title=&quot;레포주소&quot; href=&quot;https://github.com/minsu-zip/web-moneybook-04&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;레포주소&lt;/a&gt;&lt;/span&gt;)&lt;/b&gt;&lt;/span&gt;&lt;/h2&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/chlgMU/btrLR7rP1p6/4j9AxfObq96TmTkG7NW5Qk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chlgMU/btrLR7rP1p6/4j9AxfObq96TmTkG7NW5Qk/img.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;463&quot; data-is-animation=&quot;true&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chlgMU/btrLR7rP1p6/4j9AxfObq96TmTkG7NW5Qk/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchlgMU%2FbtrLR7rP1p6%2F4j9AxfObq96TmTkG7NW5Qk%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;800&quot; height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpaq9E/btrLMAaXEcC/mEuuFaebqbbo66kwSN4fK1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpaq9E/btrLMAaXEcC/mEuuFaebqbbo66kwSN4fK1/img.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;463&quot; data-is-animation=&quot;true&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpaq9E/btrLMAaXEcC/mEuuFaebqbbo66kwSN4fK1/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpaq9E%2FbtrLMAaXEcC%2FmEuuFaebqbbo66kwSN4fK1%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;800&quot; height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌) 메인페이지&amp;nbsp; 우) 가계부 생성&lt;/figcaption&gt;
&lt;/figure&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/b4xgVC/btrLR8EiCmD/kZqXuzsxrdwmqSaUTh8nE1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4xgVC/btrLR8EiCmD/kZqXuzsxrdwmqSaUTh8nE1/img.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;463&quot; data-is-animation=&quot;true&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4xgVC/btrLR8EiCmD/kZqXuzsxrdwmqSaUTh8nE1/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4xgVC%2FbtrLR8EiCmD%2FkZqXuzsxrdwmqSaUTh8nE1%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;800&quot; height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh2gyx/btrLLgEf8wf/idIZugf0vrg1GQnkiQ7W31/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh2gyx/btrLLgEf8wf/idIZugf0vrg1GQnkiQ7W31/img.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;463&quot; data-is-animation=&quot;true&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh2gyx/btrLLgEf8wf/idIZugf0vrg1GQnkiQ7W31/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh2gyx%2FbtrLLgEf8wf%2FidIZugf0vrg1GQnkiQ7W31%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;800&quot; height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌) 가계부 수정 및 결제수단 CRUD&amp;nbsp; 우)&amp;nbsp;달력 페이지 및 통계페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;개발 이슈&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;전역 상태 관리&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번 프로젝트에서 처음으로 클래스형으로 VanillaJS를 구성했고, 디자인 패턴(옵저버)도 처음으로 구현해 봤다. 그러다 보니, 문법이나 활용법에 처음에 많이 어려웠고 미숙했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;팀원과 회고를 하면서 문서화를 통해 템플릿 코드가 있으면 개발 시 수월할 것 같다는 의견을 나눴고 이를 문서화하여 참고하면서 개발을 진행했고, 해당 문제를 해결할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;옵저버 패턴&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662871593602&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const store = {}

/**
 * 상태를 구독한다(리렌더링 함수를 옵저버로 등록.
 * @param {string} key 구독할 key
 * @param {function} observer 변화 감지 후 실행시킬 함수(리렌더링 함수)
 * @returns
 */
const subscribe = (key, observer) =&amp;gt; store[key]._observers.add(observer)

/**
 * 해당 리렌더링 함수를 제거한다.
 * @param {string} key 구독할 key
 * @param {function} observer 리렌더링 함수
 */
const unsubscribe = (key, observer) =&amp;gt; {
  store[key]._observers = [...store[key]._observers].filter((subscriber) =&amp;gt; subscriber !== observer)
}

/**
 * 옵저버 함수를 실행한다.
 * @param {string} key 해당 key의 옵저버 함수를 실행시킨다.
 */
const notify = (key) =&amp;gt; store[key]._observers.forEach((observer) =&amp;gt; observer())

/**
 * store 객체에 전역 상태를 추가한다.
 * @param {{key, defaultValue}} key 전역 상태 key, defaultValue 전역 상태 밸류
 * @returns {string} 키를 반환함
 */
const initState = ({ key, defaultValue }) =&amp;gt; {
  store[key] = {
    _state: defaultValue,
    _observers: new Set(),
  }
  return key
}

/**
 * 해당 key의 상태 값을 불러온다.
 * @param {string} key 전역 상태 key
 * @returns {any} store[key]._state 상태 value
 */
const getState = (key) =&amp;gt; {
  return store[key]._state
}

/**
 * 해당 key의 상태를 수정하고, notify로 옵저버 함수(리렌더링 함수)를 실행시킨다.
 * @param {string} key 전역 상태 key
 */
const setState = (key) =&amp;gt; (newState) =&amp;gt; {
  store[key]._state = newState
  notify(key)
}

export { subscribe, unsubscribe, initState, getState, setState }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;옵저버 패턴에 등록한 데이터를 구독하는 코드 예시&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662871669250&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Component } from '../core/component.js'
import { getState, subscribe } from '../core/observer.js'
import { textState } from '../stores/textStore.js'

export default class Text extends Component {
  constructor(target) {
    super(target)

    subscribe(textState, this.render.bind(this))
  }

  template() {
    const text = getState(textState)
    return `

        &amp;lt;div&amp;gt;${text}&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;&lt;span style=&quot;color: #000000;&quot;&gt;옵저버 패턴에 등록한 데이터를 바꾸는 코드 예시&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662871695334&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Component } from '../core/component.js'
import { setState } from '../core/observer.js'
import { textState } from '../stores/textStore.js'

export default class Input extends Component {
  constructor(target) {
    super(target)

    this.setText = setState(textState)
  }

  handleInputText(e) {
    const { value } = e.target

    this.setText(value)
  }

  setEvent() {
    this.$target.querySelector('#test').addEventListener('input', this.handleInputText.bind(this))
  }

  template() {
    return `
        &amp;lt;input id=&quot;test&quot; type=&quot;text&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;웹 컴포넌트 활용&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;웹 컴포넌트로 컴포넌트를 만들 경우에는 컴포넌트 자체가 엘리먼트(요소)이기 때문에, 다루기가 편리해짐&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코드를 유지 보수하고 관리할 때 매우 이롭다. 각 컴포넌트 별로 독립적으로 존재하기 때문에 컴포넌트 별로 작업이 필요한 경우 반복문을 돌면서 직접 이벤트가 발생한 DOM을 찾고, 해당 로직을 찾을 필요 없이 상위에서 props함수를 통해 상위로 해당 객체의 정보를 반환하여 상위에서 컨트롤하면 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;props 컴포넌트 자체를 넘겨줘서 사용할 수 있기 때문에 재사용성에 편리해짐&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;props로 컴포넌트 자체를 넘겨서 활용한 사례&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662872919768&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    openModal(
      new UpdateTransactionModal({
        id,
        title,
        category,
        paymentId,
        payment,
        price,
        paymentDate,
      }),
    )&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1662872929708&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const openModal = (modalElement) =&amp;gt; {
  const $modalWrapper = document.querySelector('#modal')
  $modalWrapper.appendChild(modalElement)
  disableBodyScroll()
}

const closeModal = (modalElement) =&amp;gt; {
  modalElement.remove()
  enableBodyScroll()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;네트워크 비용 개선&lt;/span&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;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 컴포넌트 렌더링에 따른 지속적인 API 요청 문제&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; background-color: #ffffff;&quot;&gt;(상위) 결제내역바 컴포넌트 -&amp;gt; (하위) 결제 수단 리스트 컴포넌트 결제내역바에서 props 상태 변경에 따라 재렌더링 되면서 하위 컴포넌트도 렌더링 되는 과정에서 결제 수단 GET API를 지속적으로 요청하는 문제가 발생 (최소 5번 이상의 데이터 요청)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5A6ge/btrLMwsNBzX/DYzVy7Q387DfY1nGWkQzx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5A6ge/btrLMwsNBzX/DYzVy7Q387DfY1nGWkQzx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5A6ge/btrLMwsNBzX/DYzVy7Q387DfY1nGWkQzx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5A6ge%2FbtrLMwsNBzX%2FDYzVy7Q387DfY1nGWkQzx1%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;259&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;259&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: #000000;&quot;&gt;해결 방법 (상위에서 전역 데이터로 관리)&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결제 수단 리스트 컴포넌트를 두 곳에서 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사용하는 두 개의 컴포넌트라서 그 상위에서 데이터를 한번 요청 후, 옵저버에 등록&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결제 수단 리스트 컴포넌트는 옵저버에 등록된 데이터를 받아와서 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결제 수단 Create, Delete 시 옵저버에서 상태를 변경해서 사용&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CDm3m/btrLMxZxYiU/cO1EoBNk2c0WzKeKCQyoa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CDm3m/btrLMxZxYiU/cO1EoBNk2c0WzKeKCQyoa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CDm3m/btrLMxZxYiU/cO1EoBNk2c0WzKeKCQyoa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCDm3m%2FbtrLMxZxYiU%2FcO1EoBNk2c0WzKeKCQyoa1%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;259&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;259&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 결재내역 데이터 변경 시 API 요청에 따른 깜빡임 문제&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결제 내역 CRUD, 결제 수단 CRUD 시, API 요청을 보냅니다. API 요청을 보내고 변경 사항을 UI에 반영하기 위해서는 강제적으로 새로고침을 통해 네트워크 통신을 통해 새로 받아옵니다. 이 과정에서 새로고침에 따른 깜빡임 문제, 네트워크 통신 비용 문제가 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해결 방법 (낙관적 업데이트)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;API 요청이 정상적으로 되었다면, 사용하고 있는 객체들의 정보에 바뀌어야 하는 부분에 임의로 데이터를 수정합니다. 그 후 렌더링을 통해 사용자의 UI에서는 바로 적용된 것처럼 보이게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;3~4주차 전체&lt;span&gt;&amp;nbsp;&lt;/span&gt;회고&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2주간 진행된 만큼 많은 것을 배울 수 있었고 많은 것을 학습할 수 있었다. 또 실력 있는 동료를 만나 많은 자극이 되었다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;기술적으로 도움을 주지 못해 동료한테 많은 미안감이 들었다. 하지만 최종 회고에서 &quot;편한 분위기를 이끌어줘서 일하기 편했고, 제시하는 의견들을 다 반영해 줘서 좋았다&quot;라는 평가를 듣고 내가 이런 것에 강점이 있는 사람이구나 깨달을 수 있었다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>우아한테크캠프5기</category>
      <category>우테캠 회고록</category>
      <category>우테캠5기 가계부 프로젝트</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/262</guid>
      <comments>https://ghost4551.tistory.com/262#entry262comment</comments>
      <pubDate>Sun, 11 Sep 2022 14:27:32 +0900</pubDate>
    </item>
    <item>
      <title>React testing library + MSW 기본 사용법</title>
      <link>https://ghost4551.tistory.com/261</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;본 글은 cra react+typeScript 환경에서 테스팅을 사용하면서 기록하고 정리한 글입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h2 id=&quot;react-testing-library-소개&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;React Testing Library&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;testing-libray를 사용하면 사용자 중심 방식으로 UI 구성 요소를 테스트 할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://testing-library.com/docs/guiding-principles&quot;&gt;테스트가 소프트웨어 사용 방식과 비슷할수록 더 많은 자신감을 얻을 수 있습니다.&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;testing-libray는 행위 주도 테스트 방법론을 따르는 테스트를 작성하는데 매우 적합합니다.&amp;nbsp; jsdom이라는 라이브러리를 통해 실제 브라우저 DOM을 제공해주기 때문에 화면에 보여지는 실제 모습 그 상태로 테스트를 수행할 수 있게 해준다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;jest는 자바스크립트 또는 타입스크립트 환경에서 테스트를 진행할 수 있게 해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, 테스트 코드는 testing-libray로 작성하고 테스트를 실행하는 환경은 jest로 사용하기 때문에 둘다 필요하다.&lt;/span&gt;&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;&lt;span style=&quot;color: #000000;&quot;&gt;환경설정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;yarn&amp;nbsp;create&amp;nbsp;react-app&amp;nbsp;my-app&amp;nbsp;--template&amp;nbsp;typescript&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기본적으로 cra+ts로 설치하면 테스트에 필요한 react testing , jest 등의 라이브러리가 설치된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;cra react 환경이라면 검색을 통해 별도로 설치해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 파일은 각 컴포넌트와 동일한 폴더에 두어도 되고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 파일을 한 곳에서 관리하고 싶다면, test폴더를 두고 각 컴포넌트 테스팅을 진행합니다.&lt;/span&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 style=&quot;color: #000000;&quot;&gt;테스트 파일 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://testing-library.com/docs/react-testing-library/setup/#global-config&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;global 설정&lt;/a&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;App에서 전역 설정 (emotiom, components styled, contextAPI 등)을 사용할 경우 테스트 파일에도 적용을 시켜준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660549171080&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//test-utils.tsx

import { ThemeProvider } from '@emotion/react'
import { render, RenderOptions } from '@testing-library/react'
import React, { ReactElement } from 'react'
import { theme } from 'utils/styles'

interface Props {
  children: React.ReactNode
}

export const CustomProvider = ({ children }: Props) =&amp;gt; {
  return &amp;lt;ThemeProvider theme={theme}&amp;gt;{children}&amp;lt;/ThemeProvider&amp;gt;
}

const customRender = (
  ui: ReactElement,
  options?: Omit&amp;lt;RenderOptions, 'wrapper'&amp;gt;,
) =&amp;gt; render(ui, { wrapper: CustomProvider, ...options })

export * from '@testing-library/react'
export { customRender as render }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 전역에서 설정한 파일을 각각의 테스팅 파일에서 import로 사용하면 됩니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 코드&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;버튼 컴포넌트 렌더링 테스팅 예시&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1660549677538&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Button.test.tsx

import Button from 'components/atoms/Button'
import { render, screen, fireEvent } from './test-utils' // 전역에서 설정한 파일에서 import

describe('버튼 컴포넌트 테스트&amp;lt;Button /&amp;gt;', () =&amp;gt; {
  it('렌더링 테스트', () =&amp;gt; {
    // 버튼 컴포넌트 렌더링
    render(&amp;lt;Button&amp;gt;버튼&amp;lt;/Button&amp;gt;)
    // 버튼 text 내용을 가져옴
    const button = screen.getByText('버튼')
    // 화면에 '버튼' 내용이 렌더링 되었는가 테스팅
    expect(button).toBeInTheDocument()
  })
})&lt;/code&gt;&lt;/pre&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;span style=&quot;color: #000000;&quot;&gt;render 메소드를 이용하여 컴포넌트를 렌더링 시켜준다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;필수적으로 내려줘야하는 props가 있다면 더미데이터 props 주입&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;getByText or getBy등의 메소드를 이용하여 컴포넌트 내부에 존재하는 것을 아무거나 가져와서 잘 렌더링 되었는지 표시&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기에서는 render에서 chilrdren으로준 &quot;버튼&quot; 텍스트가 화면에 잘 보이는지 테스팅 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&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 style=&quot;color: #000000;&quot;&gt;사용자 액션 테스팅 예시&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1660549772824&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  it('클릭 이벤트 테스트', () =&amp;gt; {
    // 가상 함수 정의
    const onClick = jest.fn()
    render(&amp;lt;Button onClick={onClick}&amp;gt;버튼&amp;lt;/Button&amp;gt;)
    const button = screen.getByText('버튼')
    // 사용자 행동 이벤트
    fireEvent.click(button)
    // 클릭 후, 해당 정의한 함수가 1번 불렸는지 테스팅
    expect(onClick).toHaveBeenCalledTimes(1)
  })&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ex) 버튼 클릭 시 함수가 제대로 실행 되는지 테스트 한다고 가정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;버튼 컴포넌트 렌더링 props로 원하는 이벤트 함수 정의 jest.fn() 키워드 이용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;버튼 컴포넌트를 실행하기 위해 정보를 가져옴 screen.getByText('버튼')&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이벤트 실행 메서드&amp;nbsp;fireEvent를 이용하여 원하는 동작 이벤트 발생&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;함수가 실행되었는지 확인&amp;nbsp;toHaveBeenCalledTimes&amp;nbsp;함수 호출되었는지 판단하는 메서드&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;그 외 테스트&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660549860770&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  it('버튼 활성화 테스트', () =&amp;gt; {
    const { rerender } = render(&amp;lt;Button disabled={true}&amp;gt;버튼&amp;lt;/Button&amp;gt;)
    const button = screen.getByText('버튼')
    expect(button).toBeDisabled()

    // rerender를 통해 컴포넌트 재렌더링 후 테스팅
    rerender(&amp;lt;Button disabled={false}&amp;gt;버튼&amp;lt;/Button&amp;gt;)
    expect(button).toBeEnabled()
  })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;span style=&quot;color: #000000;&quot;&gt;msw라이브러리를 이용한 API 데이터 모킹&lt;/span&gt;&lt;/h1&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;msw&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;MSW(Mock Service Worker)는 서비스 워커(Service Worker)를 사용하여 네트워크 호출을 가로채는 API 모킹(mocking) 라이브러리입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;백엔드 API인 척하면서 프론트엔드의 요청에 가짜 데이터를 응답해주는 것으로 볼 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;애플리케이션 수준이 아닌 네트워크 수준에서 요청을 가로챕니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동일한 모의 정의를 단위, 통합, E2E 테스트 및 디버깅에 재사용할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대표적으로 두가지 사례에서 많이 사용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;백엔드 API 구현이 완료될 때까지 프론트엔드 팀에서 임시로 사용하기 위한 가짜 API로 활용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 코드 작성 시, 직접적인 네트워크 호출 대신 빠르고 안정적인 가짜 API 서버를 구축하기 위함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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 style=&quot;color: #000000;&quot;&gt;msw를 활용하여 api요청 후, 컴포넌트 렌더링 테스트 코드&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #eceeef;&quot;&gt;yarn add&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #eceeef;&quot;&gt;msw&lt;/span&gt;&lt;span style=&quot;background-color: #eceeef;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1660549986077&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 모의 api 요청 선언
const server = setupServer(
  // api 요청
  rest.get(`${API_END_POINT}categories`, (req, res, ctx) =&amp;gt; {
    return res(ctx.status(200), ctx.json(categoriesData))
  }),
 // api 요청
  rest.get(`${API_END_POINT}menus/1`, (req, res, ctx) =&amp;gt; {
    return res(ctx.status(200), ctx.json(menuData[0]))
  }),
)

// 테스트 전에 API 모킹을 사용하도록 설정합니다.
beforeAll(() =&amp;gt; server.listen())
// 테스트 중에 추가할 수 있는 런타임 요청 처리기를 재설정합니다.
afterEach(() =&amp;gt; server.resetHandlers())
//  테스트가 완료된 후 API 모킹을 비활성화합니다.
afterAll(() =&amp;gt; server.close())

// 첫 번째 선언한 카테고리 api를 정상적으로 불러왔을 때 main-test id값을 가진 컴포넌트가 렌더링 된다
test('전체 테이터 패칭 테스트', async () =&amp;gt; {
  render(&amp;lt;App&amp;gt;&amp;lt;/App&amp;gt;)
  await waitFor(() =&amp;gt; {
    expect(screen.getByTestId('main-test')).toBeInTheDocument()
  })
})

// 메뉴 버튼을 눌렀을 때, 두 번째 선언한 메뉴 정보 api를 불러왔을 때 '1menuComponent' id값을 가진 컴포넌트가 렌더링 된다.
test('메뉴 디테일 정보 패칭 테스트', async () =&amp;gt; {
  render(&amp;lt;Main menus={categoriesData[0].menus} /&amp;gt;)

  const selectedMenu = screen.getByTestId('1menuComponent')
  fireEvent.click(selectedMenu)

  await waitFor(() =&amp;gt; {
    expect(screen.getByText('수량')).toBeInTheDocument()
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;참고자료&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://testing-library.com/docs/react-testing-library/example-intro/#mock&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://testing-library.com/docs/react-testing-library/example-intro/#mock&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://github.com/mswjs/msw&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/mswjs/msw&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.daleseo.com/react-testing-library/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.daleseo.com/react-testing-library/&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;color: #000000;&quot;&gt;그 외에 테스트 코드 작성한 사례&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660550368166&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import MenuOption from 'components/molecules/MenuOption'
import { render, screen, fireEvent } from './test-utils'
import optionData from '../../../common/optionData.json'

const menuPrice = 10000

const component = (
  &amp;lt;MenuOption
    options={optionData}
    menuPrice={menuPrice}
    onClose={() =&amp;gt; null}&amp;gt;&amp;lt;/MenuOption&amp;gt;
)

describe('메뉴 옵션 컴포넌트&amp;lt;MenuOption /&amp;gt;', () =&amp;gt; {
  it('렌더링 테스트', () =&amp;gt; {
    render(component)
    const menuOption = screen.getByText('수량')
    expect(menuOption).toBeInTheDocument()
  })

  it('수량 카운트 MAX 테스트', () =&amp;gt; {
    render(component)
    const plusButton = screen.getByTestId('plusButton')

    for (let i = 0; i &amp;lt; 15; i++) {
      fireEvent.click(plusButton)
    }
    // 10개 이상 수량 선택을 못하도록 정의하여 위에서 10번 이상 클릭해도
    // 수량에 변함없고 &quot;최대 10 개&quot;를 출력하는 테스팅
    expect(screen.getByText('최대 10 개')).toBeInTheDocument()
  })

  it('수량 카운트 MIN 테스트', () =&amp;gt; {
    render(component)
    const minusButton = screen.getByTestId('minusButton')

    for (let i = 0; i &amp;lt; 20; i++) {
      fireEvent.click(minusButton)
    }
    // 최소 수량은 1개로 지정하여 20번 이상 감소 버튼을 눌러도
    // 최소 1개가 남아있는지 테스팅
    expect(screen.getByText('1 개')).toBeInTheDocument()
  })

  it('초기 상품 가격 테스트', () =&amp;gt; {
    render(component)

    const optionPrice = optionData.reduce(
      (acc, cur) =&amp;gt; acc + cur.details[0].price,
      0,
    )

	// 상품 선택 시, 초기 수량이 맞는지 테스트
    expect(
      screen.getByText(`${(optionPrice + menuPrice).toLocaleString()}원 담기`),
    ).toBeInTheDocument()
  })

  it('옵션 및 수량 선택에 따른 가격 테스트', () =&amp;gt; {
    render(component)

    optionData.forEach(({ details }) =&amp;gt; {
      const selectedOption = screen.getByTestId(
        `option-detail-${details[details.length - 1].id}`,
      )
      fireEvent.click(selectedOption)
    })

    const optionPrice = optionData.reduce(
      (acc, cur) =&amp;gt; acc + cur.details[cur.details.length - 1].price,
      0,
    )

    const plusButton = screen.getByTestId('plusButton')
    const count = 4
    for (let i = 0; i &amp;lt; count - 1; i++) {
      fireEvent.click(plusButton)
    }
    
    // 옵션이 달라지거나, 수량이 달라질 때, 총 수량이 달라지는지 테스팅
    expect(
      screen.getByText(
        `${((optionPrice + menuPrice) * count).toLocaleString()}원 담기`,
      ),
    ).toBeInTheDocument()
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;실제 코드 레포 -packages-client-src-test 폴더에 존재&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://github.com/minsu-zip/web-kiosk-parkminsu&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/minsu-zip/web-kiosk-parkminsu&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어려운 점&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사용자의 이벤트에 따라 상태가 바뀌고 재렌더링 되면서 함수 같은 것들이 다시 실행되고, 그래서 화면에 보여지는 부분도 다르게 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 코드는 이를 직접 해줘야 한다는 불편함과 어떻게 접근하고 확인할지 어려웠다. TS에 정의한 props도 맞춰주기위해 json객체를 불러와 넣어줬고, 액션 이벤트를 직접 하나하나 실행시키고, 컴포넌트 내부에 접근해 내가 예상한 결과값을 미리 계산하는 함수를 별도로 작성하여 (컴포넌트에서 사용 되는 함수라고 생각하면 되서 가져다 쓰면 된다.) 일치하는지 확인하는 과정이 어렵고 까다로웠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;+ msw를 이용한 네트워크 테스팅은 학습 난이도가 살짝 높았다. 네트워크 후킹작업과 테스팅을 함께 하다보니, 비동기 문제, 사용법 문제에 삽질을 다소 오래 진행했다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a id=&quot;user-content-사용후기&quot; style=&quot;color: #000000;&quot; href=&quot;https://github.com/woowa-techcamp-2022/web-kiosk-parkminsu/wiki/React-testing-library#%EC%82%AC%EC%9A%A9%ED%9B%84%EA%B8%B0&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;사용후기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;내가 원하던 테스팅은 개발된 화면을 이리저리 클릭 할 때마다 내가 정의한 테스팅이 잘 동작하는지 테스트하는거였는데 사용자 액션도 전부 테스팅 함수 호출을 통해 실행시키고 판단했어야 했다. 아마 예전에 어디서 얼핏 듣기론 가능했던거 같은데 처음 테스팅해보는 입장으로서 매우 어렵게 느껴졌다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 테스팅을 할려면 컴포넌트 내부에 일일히 접근하여 액션을 실행시키고 내가 원하는 값이 텍스트로 잘 보이는지 확인해야한다. 컴포넌트를 다 만들고 나서 테스팅을 진행해서 그런지 까다로웠다. TDD에 대해서는 잘 모르지만 테스트를 쉽고 견고하게 가져가기 위해 테스트 주도 개발이라는 얘기가 나온지 얼핏 알 것 같았다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발지식</category>
      <category>react msw testing 예시</category>
      <category>react testing msw</category>
      <category>react testing 사용법</category>
      <category>react testing 예시</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/261</guid>
      <comments>https://ghost4551.tistory.com/261#entry261comment</comments>
      <pubDate>Mon, 15 Aug 2022 17:09:20 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크캠프] TODO 프로젝트</title>
      <link>https://ghost4551.tistory.com/260</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2주 차도 빨리 지나갔다.. 이번 주는 많이 잔 게 6시간인 것 같다. 2주 차는 2인이 한 팀이 되어 &quot;TODO&quot; 프로젝트를 개발하는 것이었다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;월요일에 출근하자마자 팀원과 아이스브레이킹 타임을 가졌다. 짧은 시간이지만 이 분도 대단하다고 느껴졌다. 매일매일이 겸손해지는 것 같다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;간단한 잡담 타임이 끝나고 프로젝트 소개를 해주셨다. 이번 미션은 VanillaJS와 node를 이용하여 CSR로 개발하는 것이 미션이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;프런트에서 CSR을 하기 위해 babel, webpack 환경 세팅을 직접 하는 것도 미션에 포함되어 있었다. 간략한 수업을 마치고 팀원과 이번 프로젝트를 기획서를 보면서 일정과 협업 방식을 정했다. 하루마다 진행해야 할 일정을 정리하고 git commit, git issue, git branch 등을 정했다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;프로젝트&amp;nbsp;후기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FE 요구사항&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;Webpack과 Babel을 활용해 기본적인 환경 구성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;sourceMap 설정을 해서 디버깅이 가능하도록 구성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;fetch API 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;then 메서드 활용 (async / await는 나중에)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BE 요구사항&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;AWS를 이용한 배포&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MySQL 설치해서 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Node.js + Express를 활용해서 API 구현&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;요구사항에 따라 팀원과 일정과 시간 관리를 진행했다. 필요한 부분은 notion을 통해 기록하면서 회의를 진행했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;크게 월요일은 협업 방식과 환경 세팅, 화요일은 UI 작업, 수요일은 기능 개발, 목요일은 프론트와 서버와 sql 연동 및 배포. 금요일은 시연 영상 촬영으로 시간을 보냈다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;환경&amp;nbsp;세팅&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;node.js express, webpack-dev-middleware, mysql2, babel, webpack, aws 배포 환경으로 구성했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;환경 설정에 관한 것은 따로 포스팅할 예정이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;232&quot; data-origin-height=&quot;831&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rbsh6/btrHwk933GS/Oe3P6EjWXLO5cC0xh7BWM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rbsh6/btrHwk933GS/Oe3P6EjWXLO5cC0xh7BWM0/img.png&quot; data-alt=&quot;폴더구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rbsh6/btrHwk933GS/Oe3P6EjWXLO5cC0xh7BWM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frbsh6%2FbtrHwk933GS%2FOe3P6EjWXLO5cC0xh7BWM0%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;716&quot; data-origin-width=&quot;232&quot; data-origin-height=&quot;831&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;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;간략하게 설명하면 하나의 프로젝트에 프런트, 백엔드 폴더를 나누어 관리했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;웹 팩 설정으로 프런트 파일을 하나의 파일로 번들링 하여 public/dist로 내보내줍니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;서버는 서버에 있는 html에서 번들링 된 결과물은 dist 파일에 있는 js 파일을 import 하여 연결해 줍니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;그럼 사용자는 서버 접속 시 서버에서는 빈 html과 번들링 된 js 파일을 내려받아 csr로 화면을 렌더링 하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;개발 (&lt;a title=&quot;레포주소&quot; href=&quot;https://github.com/minsu-zip/web-todo-08&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;레포주소&lt;/a&gt;)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;드래그 앤 드롭에서 삽질과 너무 많이 시간을 할애해서 내가 맡은 사이드바 history 개발 쪽은 다 구현하지 못했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;crud, move 할 때 모든 기록을 저장해야 하는데 카드 생성 시에만 기록이 저장이 된다.&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;todo.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QY0vE/btrHzodsAb1/VwaL3JK0vl2E9PusSnEW1K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QY0vE/btrHzodsAb1/VwaL3JK0vl2E9PusSnEW1K/img.gif&quot; data-alt=&quot;todo&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QY0vE/btrHzodsAb1/VwaL3JK0vl2E9PusSnEW1K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/QY0vE/btrHzodsAb1/VwaL3JK0vl2E9PusSnEW1K/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;414&quot; data-filename=&quot;todo.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;todo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;개발 이슈&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;드래그 앤 드롭 UI 처리 이슈&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;드래그 앤 드롭 기능만 이틀이나 걸렸다. 드래그 앤 드롭을 UI에 적용시키고 코드를 이해하는 데 하루가 걸렸고, 드래그 앤 드롭에 따른 데이터 처리와 화면 렌더링에 또 하루가 걸렸다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;카드를 옮기는 로직이 제일 까다로웠는데 다음과 같이 처리했다. (코드 일부분 소개)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;todoCard는 마우스 좌표에서 가장 가까운 카드&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;todoCardContainer는 마우스 좌표에서 가장 가까운 컨테이너(칼럼 위치: 마우스가 어느 행에 있는지 알기 위해)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;targetLi는 클릭한 카드&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1658063990228&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	const elemBelow = document.elementFromPoint(pageX, pageY)
    // 마우스 좌표에서 가장 가까운 카드 받아오기 (상위 카드)
    $todoCard = elemBelow.closest('.todo-card-wrapper')
    // 마우스 좌표에서 가장 가까운 카드 컨테이너 받아오기
    const $todoCardContainer = elemBelow.closest('.todo-card-container')
    dragPoint.hidden = false

    // 마우스 따라 이동
    dragPoint.style.left = pageX - dragPoint.offsetWidth / 2 + 'px'
    dragPoint.style.top = pageY - dragPoint.offsetHeight / 2 + 'px'

    // 가장 가까운 카드는 없고 카드 컨테이너가 있는 경우
    // -&amp;gt; 카드가 제일 상단에 위치해야 하거나 제일 하단에 위치해야함
    // 그냥 하위에 요소를 넣으면 된다
    if (!$todoCard &amp;amp;&amp;amp; $todoCardContainer) {
      $todoCardContainer.appendChild(targetLi)
      return
    }
    // 가장 가까운 카드가 있는 경우 해당 카드 하단에 배치
    $todoCard?.parentNode?.insertBefore(targetLi, $todoCard)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;mouseMove가 발생될 때마다, 마우스 좌표에서 가장 가까운 카드 위치와 컨테이너(칼럼) 위치를 받아온다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;만약 카드가 없고 컨테이너는 있다면 두 가지 중 하나의 의미로 해석된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 해당 컨테이너(칼럼)에 카드가 하나도 없는 경우&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. 해당 컨테이너(칼럼)에 제일 하단에 배치되어야 하는 경우&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;두 가지 경우 그냥 컨테이너(칼럼) 아래에 자식 요소로 넣어주면 둘 다 해결된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&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;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;드래그 앤 드롭에 따른 데이터 변경&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;위의 과정으로 UI로의 처리가 끝났다. 하지만 각&amp;nbsp;&lt;/span&gt;컨테이너(칼럼)&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;상단에 카드가 몇 개인지&amp;nbsp;&lt;/span&gt;나타내야 하고&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;새로&amp;nbsp;&lt;/span&gt;고침 시&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;해당 카드들을 옮긴 위치에 제대로 다시 뿌려주기 위해서는 옮겨진 카드들의 정보를 계산해서 데이터를&amp;nbsp;&lt;/span&gt;교체해 주고&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;서버에게&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;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;카드를 클릭했을 때, 해당 카드의 DOM 정보는 가지고 올 수 있는데 어디로 옮겨졌는지는 따로 DOM을 저장하거나 가리키고 있지 않기 때문이었다. 그래서 팀원과 얘기를 통해 클릭이 끝났을 경우, 즉 카드가 옮겨진 후 어떻게 해보면 되지 않을까라는 의견을 토대로 해당 작업을 수행했다. 동작은 다음과 같다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 클릭이 끝난 경우 클릭한 노드 정보를 토대로 해당 카드가 어디 칼럼에 존재하는지(해야 할 일, 하고 있는 일, 완료한 일)에 몇 번째 데이터에 있는지 계산한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. 클릭된 카드의 상위 카드를 DOM에서 찾아서 상위 카드에 대한 1번 계산을 수행한다. (첫 번째 카드이고 상위가 컨테이너(칼럼)인 경우는 0번째로 인식한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. 1,2번에서 찾은 카드들의 정보로 전체 데이터에서 계산을 수행한다. (데이터를 삭제, 추가를 통해 옮겨진 것처럼 표현) 그다음 데이터 변경을 통해 카드 정보 DOM을 새로 그려주면 데이터도 UI에 맞게 변경이 된다. (서버에도 api 요청을 통해 카드의 상태 정보를 업데이트해준다.)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1658065749508&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const handleMouseUp = () =&amp;gt; {
    prevCard = targetLi?.previousSibling
    // 선택한 카드와 선택한 카드 상위 카드 or 컴포넌트가 있는 경우
    if (targetLi &amp;amp;&amp;amp; prevCard.getAttribute('data-name')) {
      // 선택한 카드 정보 계산
      getClickedCard()
      // 상위 카드 정보 계산
      getPrevCard()
      // 교체된 위치에 따라 전체 데이터 교체로직 함수
      setStateCard()
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;코드 리뷰&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;전반적인 코드 리뷰 타임으로 다음과 같은 말씀을 해주셨다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;mysql2 사용조건에 있었는데 mysql1과 다른 점이 무엇인지 알아볼 것&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;promise 지원으로 비동기 처리가 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;connection pool 지원으로 여러 번의 db 연결 시 과부하를 방지&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;저장소 보호 기능으로 dev 브랜치에서 작업하는 것을 방지&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;console.log 금지 디버깅 활용할 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이벤트 발생 순서와 제어 알기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2주차 전체 회고&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;좋았던 점&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;잡&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;담도&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;경쟁력이다&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이번 주도 많은 잡담 속에서 많은 것을 접하고 배울 수 있었다. 카드 작업에 따라 사이드바에 기록을 남겨야 하는 기능이 있었는데, 나는 카드 생성 시 카드 생성 api요청 따로, history 요청 따로 생각해서 두 번의 요청으로 해결하도록 생각했다. 하지만 생성 시 단 한 번의 요청으로 서버에서 두 개의 작업을 나누어 처리하면 된다는 얘기를 듣고 아! 하고 생각했다. 사실 생각해 보면 당연히 후자로 하는 것이 맞는데, 생각이 짧아 설계를 잘못했던 것 같다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이외에도 클래스로 컴포넌트를 작성한 팀과 VaillaJS에서도 커스텀 이벤트를 만들어서 사용한 팀, 웹 컴포넌트 개념을 사용하여 개발한 팀 등 새로운 것을 어깨너머 많이 들을 수 있었다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아쉬웠던&amp;nbsp;&amp;amp; 어려웠던 점&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;협업&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;처음에는 페어 프로그래밍을 통해 설계와 구조를 함께 작성했다. 이후 기능을 나누고 각자 개발하면서 pr 수행 시, 각자 기능 개발한 것을 설명하고 그것에 대해 토론하면서 코드 리뷰 타임을 가졌다. 하지만 시간이 점점 촉박해지면서 기능 설명이랑 코드 리뷰가 제대로 이루어지지 않았다. 그러다 보니 상대방의 기능을 이용해야 할 때 다소 힘들었다. 처음부터 끝까지 페어 프로그래밍 한 팀 보고 대단하다고 느꼈다. 다음 프로젝트 때는 규칙을 정해서 지키기 위해 노력해야겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;드래그 앤 드롭 기능에 시간을 많이 할애하여 다른 기능들을 많이 개발하지 못했다. 처음에는 엄청 쉽게 끝낼 줄 알았는데 역량이 부족해서 많이 걸렸다고 생각한다. 하지만 드래그 앤 드롭을 계속 파다 보니 해결할 수 있었다. 굉장히 뿌듯했고 하나라도 끝내서 좋았다. 서버 부분은 아예 작업을 못해봐서 많이 아쉬움이 남지만, 이후에 리팩토링하면서 학습할 예정이다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>우아한테크캠프5기</category>
      <category>Vanilla JS 드래그앤 드롭</category>
      <category>우테캠 회고록</category>
      <category>우테캠5기 todo프로젝트</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/260</guid>
      <comments>https://ghost4551.tistory.com/260#entry260comment</comments>
      <pubDate>Sun, 17 Jul 2022 23:47:01 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크캠프] 배민 회원가입 프로젝트</title>
      <link>https://ghost4551.tistory.com/259</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;시간이 이렇게 빨리 흘러가나라고 느껴질 정도로 한 주가 빨리 흘러갔다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;OT 내용에 언급했던 것처럼 이번 주 프로젝트는 로그인/회원가입 기능을 VanillaJs, node express를 이용해서&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;풀스택 개발을 진행하는 것이었다. 사실상 OT 날 제외하고 금요일 오전에 프로젝트 마감이라 이틀하고 반나절이라는 시간이 주어졌다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;둘째 날 강의장에 오니 1주 차 팀을 배정해 주셨다. 개인 프로젝트지만, 각자 의견을 나누면서 개발을 진행하라는 의미로 팀을 짜주셨다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;팀과의 첫 만남에서 아이스브레이킹 타임을 가졌다. 4명이지만 다양한 사람이 있었다. 경력자, 비전공자, 전공 학부생..&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;짧게 얘기를 나눴지만 전부 배울 점이 많고 실력이 뛰어나셨다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;그렇게 팀원들과 프로젝트 방향성이나 개발 환경에 대해서 의논을 얘기했다. 내가 구상한 것과 완전히 달랐다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;처음 구상한 것은 서버에서 빈 html을 내려주면 프런트에서 spa로 개발하고 회원가입/로그인 시 서버에 api 요청을 하고 응답 결과를 기반으로 작업하는 것으로 예상했다. 하지만, ssr 방식으로 서버에서 html까지 그려진 상태로 내려주는 방식으로 전부 기능 개발을 서버에서 하는 것이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;프로젝트 후기&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FE 요구사항&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;ES Class를 사용하지 않고 함수만으로 작성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;외부 라리브러리 사용 금지&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BE 요구사항&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;Node.js와 Express를 사용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;쿠기와 세션을 이용하여 로그인 구현&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;npm으로 설치 가능한 간단한 Embedded DB사용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;위의 요구사항에 따라 팀원들과 얘기를 나눠서 동작 과정을 생각했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;node에서 뷰 엔진 중 하나인 pug를 사용해서 html을 그리고 그 외에 필요한 css, js 파일은 개발하여 import 해서 전체 파일을 프런트에 내려주는 전형적인 ssr 과정으로 진행했다. api 요청에 따른 로직은 node 라우터에서 요청 값을 기반으로 db 로직, 응답 로직을 수행해서 반환해 주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 뷰 엔진이란?&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;뷰엔진은 서버에서 처리한 데이터 결과값을 정적인 페이지(HTML 파일)에 보다 편리하게 출력해주기 위해 사용한다. 뷰엔진에서 요구하는 형태로 템플릿 파일(문서)을 만들고, 해당 템플릿 문서에 서버에서 처리한 데이터를 전달하면 해당 데이터를 화면에 출력할 수 있다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;뷰 엔진 종류 중 하나인 pug를 사용하기로 했다. 간단한 사용법은 다음과 같다. (&lt;a style=&quot;color: #000000;&quot; title=&quot;공식문서&quot; href=&quot;https://pugjs.org/api/getting-started.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서&lt;/a&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657953795378&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
  body
    .container 
    header.header-title My 배민
    nav
      img.nav-banner(src='/images/banner.png')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;환경 세팅&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;웹 프레임워크인 express를 사용하여 서버 구축을 간결하게 하고 필요한 패키지와 구조를 잡아주는 generator을 이용하여 기본 환경 세팅을 이용했다. 그리고 view 엔진 Pug를 이용하기 위해 기본 환경 세팅을 다음과 같이 가져갔다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;(나중에 진행하긴 했지만 db는 json 기반으로 동작하는 쉽고 간편한 lowdb를 이용했다.)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657956311108&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install express-generator -g
express --view=pug -f&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;스크린샷 2022-07-16 오후 4.30.52.png&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crZ9im/btrHoOZce7q/8xKcEi8cAIaUoN4Va6MJh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crZ9im/btrHoOZce7q/8xKcEi8cAIaUoN4Va6MJh1/img.png&quot; data-alt=&quot;폴더구조와 기본 서버 환경세팅을 제공해준다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crZ9im/btrHoOZce7q/8xKcEi8cAIaUoN4Va6MJh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrZ9im%2FbtrHoOZce7q%2F8xKcEi8cAIaUoN4Va6MJh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;339&quot; data-filename=&quot;스크린샷 2022-07-16 오후 4.30.52.png&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;폴더구조와 기본 서버 환경세팅을 제공해준다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;개발 (&lt;a title=&quot;레포 주소&quot; href=&quot;https://github.com/minsu-zip/web-baemin-parkminsu&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;레포 주소&lt;/a&gt;) (&lt;a title=&quot;배포주소&quot; href=&quot;https://beamin-parkminsu.herokuapp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;배포주소&lt;/a&gt;)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;맥북을 처음 사용했기에 적응할 시간도 없이 바로 개발을 진행해서 엄청 불편했다. 그리고 폴더 구조에 한글이 껴있으면 git도 제대로 인식하지 못하는 문제로 이상한 삽질을 오래 했다. 서버도 처음이었고 이래저래 많이 부족하다고 느꼈다. 그래서 요구사항을 전부 개발하지는 못했다.&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;배민 로그인.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqpWyL/btrHoOyiMCM/Ocg9JcpYImwXYylxk5KljK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqpWyL/btrHoOyiMCM/Ocg9JcpYImwXYylxk5KljK/img.gif&quot; data-alt=&quot;로그인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqpWyL/btrHoOyiMCM/Ocg9JcpYImwXYylxk5KljK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cqpWyL/btrHoOyiMCM/Ocg9JcpYImwXYylxk5KljK/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;574&quot; data-filename=&quot;배민 로그인.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;574&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;로그인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;배민 회원가입.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Imua3/btrHum02JuH/JEcIQKKYVREJKIJKJ1ikK1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Imua3/btrHum02JuH/JEcIQKKYVREJKIJKJ1ikK1/img.gif&quot; data-alt=&quot;회원가입 일부분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Imua3/btrHum02JuH/JEcIQKKYVREJKIJKJ1ikK1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Imua3/btrHum02JuH/JEcIQKKYVREJKIJKJ1ikK1/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;574&quot; data-filename=&quot;배민 회원가입.gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;574&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회원가입 일부분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;개발 이슈&lt;/b&gt;&lt;/span&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;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;fetch api를 이용해 데이터를 서버로 보내고 서버에서 처리 후, 결괏값과 함께 해당하는 html(pug 파일)을 렌더링 하는 형식이었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;하지만, 프런트로부터의 요청, 서버로부터의 응닶은 넘어오지만 페이지 이동은 정상적으로 이루어지지 않았다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;서버에서 render, redirect 옵션을 이용했지만 똑같은 이슈가 발생했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;팀원과 이슈를 의논하는 중, fetch는 바로 redirect로 페이지 이동이 안되는 이슈가 있다고 알려주셨다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;그래서 서버에서는 redirect을 정상적으로 응답해 주고 프런트에서 fetch 요청에 대한 응답 값에서 redirect 값이 있다면 프런트에서 링크 이동하는 방식으로 해당 이슈를 해결할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1657958047799&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 서버 라우터 로직
router.post('/', function (req, res, next) {
	res.redirect('/')
})

// 프론트 api 요청 로직
const result = await fetch('/signIn', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email,
        password,
      }),
    })

if (result.redirected) window.location.href = result.url&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;코드 리뷰&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로젝트 마감이 끝난 후, 멘토님들이 랜덤으로 몇몇 학생들의 레포를 들어가 전반적으로 코드 리뷰를 해주셨다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;디버깅 적극적 활용하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자기만의 구현 방식으로 작성하기 (코드 리뷰와 공유는 좋지만 통일성은 기피)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파일 이름에 동사는 없애기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파일은 계층적으로 나누기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;콜백 함수 부분에 작성되는 로직은 함수로 빼기&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: -1px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;ex) addEventListener('cilck', 함수 이름)&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: -1px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: -1px;&quot;&gt;css bem 방법론을 활용하는것이 편의성에 좋다&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: -1px;&quot;&gt;숫자 길이 체크하는 로직도 숫자를 직접 적어서 사용하는 것보단 어떤 의미인지 알 수 있게 변수화를 통해 사용해라 (매직 넘버)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: -1px;&quot;&gt;http 관련 공부는 꾸준히 할 것&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: -1px;&quot;&gt;git branch 전략은 심플한 게 좋음 (우형 깃 브랜치 전략 추천)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: -1px;&quot;&gt;모든 기술 스택이나 모든 것을 남들이 써서 쓰는 게 아니라 필요에 따라 목적에 따라 근거 기반으로 사용하는 주관이 필요하다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1주차 전체 회고&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;좋았던 점&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;잡담도 경쟁력이다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;우아한 형제들에서 일을 더 잘하는 방법 11 가지 중에 하나인 &quot;잡담도 경쟁력이다.&quot;라는 부분이 있다. 이게 어떤 의미인지 한 주 동안 많은 사람들과 얘기를 나눠보니 알 수 있었다. 사람들과 중간중간 얘기하는 시간에서 자연스럽게 개발 얘기나 프로젝트 얘기가 나온다. 그 속에서 이 사람은 어떤 생각으로 개발을 진행하는지 알 수 있었고, 또 서로 프로젝트를 공유하면서 이슈 해결, 설계, 로직 등 다양한 얘기를 통해 프로젝트 개발 시 엄청 많이 도움이 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;협업&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;개인 프로젝트지만, 팀을 배치해 주셨고 함께 개발을 진행했다. 혼자 했다 라면 시간에 쫓겨 폴더 구조나, 깃 브랜치 전략, 이슈 관리를 제대로 활용하지 못했을 것 같은데 어떻게 이슈를 관리하고 브랜치 이름은 어떻게 작성할 것인지 얘기를 통해 협업하는 과정을 배웠다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;또한, 처음으로 서버를 구축했지만 팀원들과 하루에 2~3 스크럼을 통해 개발 상황과 요구 사항을 얘기하면서 빠르게 개발을 진행할 수 있었다 &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;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;풀스택 경험&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;작지만, 풀스택 개발과 db 개발 작업을 통해 하나의 사이트가 어떻게 돌아가는지 원리에 대해서 알 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프론트만 계속 학습했기에 이런 과정이 궁금해서 우테캠을 지원했었다. 작은 사이트지만, 풀스택 경험을 통해 이런 원리를 알 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;color: #000000;&quot;&gt;아쉬웠던 &amp;amp; 어려웠던 점&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;맥북..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;윈도우로만 사용했던 나에게는 맥북 개발이 너무 불편해서 이것도 개발 속도에 큰 영향을 준 것 같았다...빨리 맥북에 익숙해져야겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;부족한 개발 실력&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;짧은 기간에 다양한 기술 스택을 사용했어야 했다. 그렇기 때문에 제대로 학습할 시간이 없었다. 평소 개발 학습법은 어떤 것을 프로젝트에서 사용하기 전에 사전에 미리 충분한 학습을 거쳐 프로젝트에 도입했었다. 그래서 &quot;이건 이렇게 되고, 저건 저렇게 돼서 이렇게 되는 구나&quot;, &quot;이건 이렇게 해야지 훨씬 깔끔하고 좋지&quot; 등 판단과 근거가 생겨서 개발을 진행했었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 이번엔 학습할 시간 없이 바로 서버 구축과 pug, db 등을 도입했어야 했다.&amp;nbsp; 그래서 개발 과정에서 &quot;왜 이게 이렇게 되지?, 이게 맞나?&quot; 잘 이해가 가지 않은 상태로 개발을 했었다. 그래서 시간이 다소 지체되었고 결국 프로젝트를 전부 수행하지 못했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이런 개발 학습 방법에 익숙하지 않아서 생기는 문제라고 판단됐다. 빠르게 적응하기 위해 노력해야겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;시간 관리&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;처음 기획한 것은 환경 세팅을 끝내고, 프론트 작업을 전부 수행 후 서버 작업을 해야겠다고 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만, 프론트 작업만 하다가 개발 기간이 끝날 것 같아서 서버와 db도 경험해 보고 공부하기 위해 마지막 회원가입 기능은 구현하지 못한 채, 로그인 기능 부분 로직을 통해 서버와 db 연동을 수행할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;멘토님이 프로젝트 수행 &quot;ui나 프론트에만 너무 집중하지 말고, ui는 부족하더라도 서버나 이런 것을 다뤄보세요&quot;라는 말처럼 적절하게 업무를 나누고 시간 관리를 했어야 했는데 많이 부족했다고 생각한다. 4&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기 선배님이 오셔서 시간관리에 대해 특강 해주셨던 것 처럼 시간 관리와 업무 작업을 잘 고려해야겠다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>우아한테크캠프5기</category>
      <category>fetch redirect 문제</category>
      <category>node redirect 문제</category>
      <category>우테캠 회고록</category>
      <category>우테캠5기 1주차 후기</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/259</guid>
      <comments>https://ghost4551.tistory.com/259#entry259comment</comments>
      <pubDate>Sat, 16 Jul 2022 22:19:36 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크캠프] OT</title>
      <link>https://ghost4551.tistory.com/258</link>
      <description>&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/c6VcWo/btrGS5gd8Gs/qPd37XxlOZTJ2aBIuuRve1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6VcWo/btrGS5gd8Gs/qPd37XxlOZTJ2aBIuuRve1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;KakaoTalk_20220711_004257738_01.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6VcWo/btrGS5gd8Gs/qPd37XxlOZTJ2aBIuuRve1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6VcWo%2FbtrGS5gd8Gs%2FqPd37XxlOZTJ2aBIuuRve1%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;3024&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSnjq6/btrGUrwifHA/jft2pd0yTlIKrefQGw8Ufk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSnjq6/btrGUrwifHA/jft2pd0yTlIKrefQGw8Ufk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;KakaoTalk_20220711_004257738_02.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSnjq6/btrGUrwifHA/jft2pd0yTlIKrefQGw8Ufk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSnjq6%2FbtrGUrwifHA%2Fjft2pd0yTlIKrefQGw8Ufk%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;3024&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;롯데타워 &quot;더 큰집&quot;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;OT는 롯데타워에 &quot;더 큰집&quot;에서 진행됐다. (지옥철 1시간 30분 죽을 것 같았다..)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1층에서 모여서 운영 매니저님의 안내에 따라 출입증 발급하고 사무실로 갔다. 입구에서부터 여기가 회사인가 카페인지 헷갈릴 정도로 편안한 분위기의 인테리어로 되어 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;조금 앉아 있으니 운영진, 마스터, CTO 환영 인사와 설명회를 진행했다. 여러 설명과 조언을 토대로 설명을 해주셨다.&amp;nbsp; 기억에 남는 말은 &quot;지금 여러분은 탄소지만 우테캠 과정이 끝났을 때, 다이아몬드가 되어서 나갈 것을 당부했다.&quot; 그만큼 열정을 불태우라는 말씀이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이후에 오피스 투어를 진행했다. 구경하느라 사진을 못 찍었지만, 카페인지 세미나 왔는지 모를 정도로 엄청 휴게 공간과 자유로운 업무 환경으로 이루어져 있었다. 모든 공간과 인테리어에 대해 의미를 설명해주셨는데 사소한 것까지 의미를 부여한다는게 너무 신기했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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/VQ0mF/btrGTwkmbgP/cKBZHvArSKcH9sMXrcMbck/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VQ0mF/btrGTwkmbgP/cKBZHvArSKcH9sMXrcMbck/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;KakaoTalk_20220711_005624014_01.jpg&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VQ0mF/btrGTwkmbgP/cKBZHvArSKcH9sMXrcMbck/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVQ0mF%2FbtrGTwkmbgP%2FcKBZHvArSKcH9sMXrcMbck%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;3024&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMmCXH/btrGTxjdgzj/KUsJ2TfE7cyr6ujAU7wb90/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMmCXH/btrGTxjdgzj/KUsJ2TfE7cyr6ujAU7wb90/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;KakaoTalk_20220711_005624014_02.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMmCXH/btrGTxjdgzj/KUsJ2TfE7cyr6ujAU7wb90/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMmCXH%2FbtrGTxjdgzj%2FKUsJ2TfE7cyr6ujAU7wb90%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;3024&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이후 앞으로 두 달간 사용할 교육장 잠실 &quot;루터회관&quot;으로 장소를 이동하였다. 준비된 식사(원 할머니 보쌈 꿀맛) 이후에 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배민 쿠폰을 걸고 간단한 게임이 진행됐다. 음식 ASMR 게임에서 하나 맞춰서 쿠폰을 얻었다 ㅎㅎ&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이후, 맥북 장비 대여 및 선물(캠프 굿즈) 증정을 하고 웹, 안드로이드 과정으로 나누어서 강의장으로 이동했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;커리큘럼상 수업이 있었지만, 멘토님과 서로를 알아가는 잡답 타임이 이루어졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;잡답 타임이 끝나고, 첫 번째 미션에 대해서 설명해 주셨다. (역시 우테캠이구나.... 첫 날부터 과제라니....)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이전 기수분들과 똑같은 과제가 주어졌다. 배민 회원가입/로그인 기능이 포함된 사이트를 개발하는 것이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;근데 마감일이 금요일 오전까지였다. 오늘이 화요일인데..?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇게 OT가 끝나고 집으로 갔다. 퇴근길도 엄청 막혀서 오래 걸렸다. 진이 쏙 빠진 상태로 집에 와서 저녁만 후딱 먹고 과제 요구사항을 천천히 읽어보았다. 서버 부분은 처음이었고, 생소한 개념이 엄청 많았다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어떻게 설계해야할지 감이 안왔다. 대충 프론트 부분만 설계를 구상하고&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 나머지 부분은 내일 동료분들과 의논해야겠다 생각하고 잠에 들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;꿈에만 그리던 우테캠에 합류한다는 것이 믿기지가 않았다. 내 기준에서 우형은 서울대 급이기 때문에 너무나 소중한 기회를 열심히 해야겠다라는 생각밖에 들지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;앞으로 화이팅...!&lt;/span&gt;&lt;/p&gt;</description>
      <category>우아한테크캠프5기</category>
      <category>우아한형제들 5기 OT</category>
      <category>우테캠5기 OT</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/258</guid>
      <comments>https://ghost4551.tistory.com/258#entry258comment</comments>
      <pubDate>Mon, 11 Jul 2022 01:08:38 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크캠프] 5기 최종 합격 후기</title>
      <link>https://ghost4551.tistory.com/257</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;220406_우아한테크캠프_5기_포스터수정-1-1024x740.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IyySo/btrGS5eETwk/MKcKKdY3hPul3ckRZVYfOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IyySo/btrGS5eETwk/MKcKKdY3hPul3ckRZVYfOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IyySo/btrGS5eETwk/MKcKKdY3hPul3ckRZVYfOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIyySo%2FbtrGS5eETwk%2FMKcKKdY3hPul3ckRZVYfOK%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;497&quot; height=&quot;359&quot; data-filename=&quot;220406_우아한테크캠프_5기_포스터수정-1-1024x740.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;우아한테크캠프&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1년마다 돌아오는 우테캠 모집 글이 올라왔다. 우형에 대한 관심과 우테캠에 대한 관심이 많아서 무조건 붙어야겠다는 마음으로 준비했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;세부 전형 단계&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;사전접수 &amp;gt; 1차 코딩테스트 &amp;gt; 2차 과제테스트 &amp;gt; 서류접수 &amp;gt; 면접 &amp;gt; 우아한테크캠프&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;작년과 다르게 자소서 기입 항목이 2차 테스트까지 거친 이후에 진행되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;1차 코딩 테스트&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로그래머스 플랫폼으로 150분 4문제로 시험이 치러진다. 화상 감독이나 캠은 키지 않고 편안한 분위기에서 진행됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프론트엔드 웹 풀스택 과정은 코딩 테스트 언어를 JS로 제한되어 있었다. 덕분에 자료구조를 직접 구현해야 하는 까다로운 문제는 나오지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1번과 2번 문제는 최근에 코테에서 자주 나오는 유형으로 나왔다. 주어진 정보와 문제로 객체 or 배열로 만든 후, 여러 조건에 따라 정렬을해서 결과값을 반환하면 되는 문제다. (JS 객체와 정렬을 잘 다루면 수월하다)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3번 문제는 BFS or DFS 문제로 나왔다. 주어진 조건에 따라 탐색과 인접 노드들을 처리한 후, 결과값을 반환하는 문제였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4번 문제는 치환 문제였다. (치환 결과에 따라 또 치환이 되는 조건이 있기 때문에 어려웠고 풀지 못했다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테스트 케이스만 주어지고, 히든 케이스는 주어지지 않기 때문에 예외 조건을 고려하면서 작성해야 한다. 나는 4번을 제외하고 3문제를 풀었다. 오픈톡 방에서 통과한 사람들의 수요조사를 보니 4문제중 3문제를 맞춰야 안정권으로 합격하는 것 같았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;2차 과제 테스트&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;바닐라 JS로 SPA 개발을 진행하면 되는 문제이다. (코테는 못하지만 과제 테스트는 경험이 있어서 약간의 자신이 있었다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1차 합격 발표 후, 3~4일 뒤에 바로 2차 과제 테스트가 잡혀있어서 합격되자마자 바로 &lt;a style=&quot;color: #000000;&quot; title=&quot;프로그래머스 과제란&quot; href=&quot;https://school.programmers.co.kr/skill_check_assignments&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;프로그래머스에 있는 과제 테스트&lt;/span&gt;&lt;/a&gt;란(프론트엔드)에 있는 모든 문제를 풀면서 연습했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;처음엔 주어진 시간 안에 다 해결하지 못했지만, 해설지와 계속 학습하면서 각 문제당 2~3번씩은 풀면서 연습했었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(보너스로 존재하는 조건들도 연습해 보는 것을 추천한다. 비슷한 조건으로 과제 테스트에서도 나왔다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;과제 테스트는 4시간 동안 이루어진다. 여러 페이지가 아니라 한 페이지만 개발하면 되는 부분이어서 비교적 수월했다. API 호출을 통해 받은 형식이 다른 여러 데이터를 범용적으로 사용할 수 있게 테이블로 나타내는 것이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(그냥 객체와 if 조건을 쓸 수 있으면 되는 것 같다.) 그 외에 보너스 구현 사항도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;범용적으로 쓰기 위한 설계 고민에서 시간이 좀 걸렸지만, 주어진 요구사항을 전부 구현했다. 시간이 조금 남아 보너스 구현 사항도 전부 구현할 수 있었고, 리팩토링도 조금 진행했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;서류 접수 (자소서)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2차 진행 후, 1~2주 뒤에 합격 메일과 함께 서류 접수 메일을 받았다. 우아한 형제들 채용 홈페이지에서 접수가 진행되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;질문은 다른 곳들과 유사한 질문들이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;1.&amp;nbsp; 개발자가 갖추어야 할 덕목과, 여기에 비추어 봤을 때 본인의 어떤 점이 개발자로 일하기에 적합한지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;2. 지원 이유&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;3. 나만의 프로그래밍 학습 방법&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;4. 협업 과정에서 어려움을 겪었던 경험과 해결하기 위해 어떤 노력을 했는지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;그 외에 본인의 블로그나 이력서, 깃헙 링크를 제출하는 것도 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;최대한 나의 경험을 위주로 작성하기 위해 과거의 일을 많이 생각하면서 적었다.&lt;b&gt; 자신의 경험 위주로 풀어나가는 것이 근거를 뒷받침하기에 중요한 것 같다. &lt;/b&gt;주변 동료, 멘토님들을 괴롭히면서 끊임없는 피드백으로 매일 수정하면서 신경 썼다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;면접 심사&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIC3MB/btrGS4z5MUZ/4Vwompvci4NsqQ6ecaUKv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIC3MB/btrGS4z5MUZ/4Vwompvci4NsqQ6ecaUKv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIC3MB/btrGS4z5MUZ/4Vwompvci4NsqQ6ecaUKv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIC3MB%2FbtrGS4z5MUZ%2F4Vwompvci4NsqQ6ecaUKv0%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;350&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;458&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: #000000;&quot;&gt;서류 접수 후, 며칠 이내에 바로 면접 심사 메일을 받았다. 서류 접수가 프로세스 마지막이라 접수한 모든 인원들에 대해 면접이 진행되는지 아니면 서류 전형도 따로 심사를 거친 후 면접이 진행되는지는 모르겠지만 엄청 기뻤고, 욕심이 나기 시작했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이전 기수분들의 후기를 찾아보면서 엄청 준비를 많이 했다. 기술 면접보다는 개인이 어떤 생각을 가지고 있고, 어떤 가치관인지 물어보는 위주인 것 같다. 또한, 예상 질문이 있는 것이 아니라, 예상치 못한 질문으로 이루어진다는 얘기를 들어서 지금껏 겪은 경험과 자소서, 이력서 위주로 예상 질문을 위주로 많이 정리하면서 준비했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번에도 주변 동료, 멘토님들에게 면접 연습을 해달라고 괴롭히면서 매일매일 연습과 답변을 생각하면서 준비했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;연습이지만, 처음에는 너무 긴장해서 말하면서도 질문을 까먹고, 표정, 말투, 제스처 모든 것이 어색했지만, 하면 할수록 좋아졌다는 피드백을 받았다. (면접은 연습+자신감만이 살길이다...)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;내가 받은 질문을 대충 고려하자면 자소서에서 적은 문항을 검증하는 질문과,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어떨 때 개발하면서 즐거움을 느끼는지, 트렌트를 어떻게 쫓아가는지, 기술 스택을 선택하는 기준, 개발적으로 잘하는 점 부족한 점 등에 대한 질문과 꼬리 질문으로 진행되었다. &lt;b&gt;30분간 진행되는 만큼&amp;nbsp;&lt;/b&gt;그렇게 질문이 많지는 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예상치 못한 질문들로 중간에 조금 답변이 이상한 것들도 조금 있었던 것 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여유 있게 면접 보는 것이 가장 중요한 것 같다....&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;최종 결과&lt;/b&gt;&lt;/span&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-origin-width=&quot;602&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OIqbq/btrGSyuECOc/HDjHaq1cY42lMf6uBec7I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OIqbq/btrGSyuECOc/HDjHaq1cY42lMf6uBec7I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OIqbq/btrGSyuECOc/HDjHaq1cY42lMf6uBec7I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOIqbq%2FbtrGSyuECOc%2FHDjHaq1cY42lMf6uBec7I0%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;551&quot; height=&quot;150&quot; data-origin-width=&quot;602&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;&lt;span style=&quot;color: #000000;&quot;&gt;사실 최종 결과 발표날에 불합격 메일을 받았다. 김칫국을 사발로 드링킹하고 있어서 너무 기대한 나머지 기분이 씁쓸했다. 그리고 다음날 마음이 꿀꿀한 상태에서 모르는 번호로 전화가 왔다. 누군가 싶어 받았는데 추가 합격이라는 전화를 받고 감사하다는 인사를 수차례 드리고 바로 주위에 자랑을 했다ㅎㅎ..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;+ 5기부터 안드로이드 과정이 생기면서 인원이 절반으로 나누어진 것 같다. 최종 인원 웹 20명, 안드로이드 20명이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;앞으로 우테캠 하면서 목표는 모든 동료들과 친해져서 교육이 끝나더라고 꾸준히 교류하면서 개발자 친구들을 많이 두는 것이다. 그리고&amp;nbsp; 꿈에만 그리던 채용까지 되도록 최선을 다하는 것이다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>우아한테크캠프5기</category>
      <category>우아한테크캠프 5기 지원 후기</category>
      <category>우아한테트캠프 5기 최종합격</category>
      <category>우테캠 5기 지원 후기</category>
      <category>우테캠 5기 합격 후기</category>
      <category>우테캠5기 최종합격</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/257</guid>
      <comments>https://ghost4551.tistory.com/257#entry257comment</comments>
      <pubDate>Sat, 9 Jul 2022 16:00:43 +0900</pubDate>
    </item>
    <item>
      <title>[백준 node.js] 7576번_토마토</title>
      <link>https://ghost4551.tistory.com/256</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.acmicpc.net/problem/7576&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt; 7576번_토마토&lt;/span&gt;&lt;/a&gt; 골드5&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제&amp;nbsp;설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; : N x M 상자 크기에 토마토가 있을 경우, 하루가 지날 때마다 인접 토마토들을 익게 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;모든 토마토가 익는 데까지 걸리는 최소 일수 구하기&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;  시간 초과를 벗어나기 위해서는 queue 알고리즘을 구현해서 사용 or &lt;span style=&quot;background-color: #ffffff;&quot;&gt;index가 증가하는 cursor를 두고, queue.length &amp;lt; queueCursor를 사용&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 data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;queue 알고리즘을 구현 예시&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650562233278&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fs = require('fs')
const [mn, ...input] = fs
  .readFileSync('/dev/stdin')
  .toString()
  .trim()
  .split('\n')
const [m, n] = mn.split(' ').map(Number)
const graph = input.map((x) =&amp;gt; x.split(' ').map(Number))

// JS에서 BFS 구현시 shift, push로 queue 구현
// shift 연산은 O(n)을 가지므로 시간 초과날 가능성이 높다.
// 따라서 직접 구현해서 시용

//  각각의 노드, 노드의 data와 다음 노드를 가리키고 있다.
class Node {
  constructor(data) {
    this.data = data
    this.next = null
  }
}

// 큐 클래스
class Queue {
  constructor() {
    this.head = null // 제일 앞 노드
    this.rear = null // 제일 뒤 노드
    this.length = 0 // 노드의 길이
  }

  enqueue(data) {
    // 노드 추가.
    const node = new Node(data) // data를 가진 node를 만들어준다.
    if (!this.head) {
      // 헤드가 없을 경우 head를 해당 노드로
      this.head = node
    } else {
      this.rear.next = node // 아닐 경우 마지막의 다음 노드로
    }
    this.rear = node // 마지막을 해당 노드로 한다.
    this.length++
  }

  dequeue() {
    // 노드 삭제.
    if (!this.head) {
      // 헤드가 없으면 한 개도 없는 것이므로 false를 반환.
      return false
    }
    const data = this.head.data // head를 head의 다음 것으로 바꿔주고 뺀 data를 return
    this.head = this.head.next
    this.length--

    return data
  }
}

// 인덱스 검증
function isRange(dx, dy) {
  if (dx &amp;gt;= 0 &amp;amp;&amp;amp; dy &amp;gt;= 0 &amp;amp;&amp;amp; dx &amp;lt; n &amp;amp;&amp;amp; dy &amp;lt; m) {
    return true
  }
  return false
}

function BFS(graph, queue) {
  // 상하좌우
  const dx = [-1, 1, 0, 0]
  const dy = [0, 0, -1, 1]
  let day = 0

  while (queue.length) {
    let [i, j, cnt] = queue.dequeue()
    for (let k = 0; k &amp;lt; 4; k++) {
      let mx = i + dx[k]
      let my = j + dy[k]
      if (isRange(mx, my) &amp;amp;&amp;amp; graph[mx][my] === 0) {
        graph[mx][my] = 1
        queue.enqueue([mx, my, cnt + 1])
      }
    }
    day = cnt
  }
  let answer = day

  // BFS 수행 후 0이 있을 경우 토마토가 익지 못하는 상황 발생
  graph.forEach((_, i) =&amp;gt; {
    if (graph[i].includes(0)) {
      answer = -1
    }
  })

  return answer
}

function solution() {
  let answer = 0
  const queue = new Queue()
  graph.forEach((_, i) =&amp;gt; {
    graph[i].forEach((_, j) =&amp;gt; {
      if (graph[i][j] === 1) {
        queue.enqueue([i, j, 0])
      }
    })
  })

  // BFS 함수 수행하고 나면 인접 토마토가 모두 익게 된다.
  answer = BFS(graph, queue)

  return answer
}
console.log(solution())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;index를 이용하여 해결한 예시 (이 외의 코드는 동일)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1650562297182&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;....
....
function BFS(graph, queue) {
  // 상하좌우
  const dx = [-1, 1, 0, 0]
  const dy = [0, 0, -1, 1]
  let day = 0
  let index = 0

  while (queue.length &amp;gt; index) { // index 이용
    let [i, j, cnt] = queue[index++]
    for (let k = 0; k &amp;lt; 4; k++) {
      let mx = i + dx[k]
      let my = j + dy[k]
      if (isRange(mx, my) &amp;amp;&amp;amp; graph[mx][my] === 0) {
        graph[mx][my] = 1
        queue.push([mx, my, cnt + 1])
      }
    }
....
....&lt;/code&gt;&lt;/pre&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;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;상자 (graph)를 돌면서 토마토의 위치 정보를 전부 queue에 저장해 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;queue를 가지고 흔한 BFS 알고리즘을 사용해 주면 된다. 그러면 최소한의 일수를 구할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사실 이 문제는 이전에 해결하지 못했었다. 예전에는 다른 블로그들을 참고해도 동일하게 queue를 사용해서 했는데 그대로 로직을 구성했지만, 시간 초과가 계속 났었다. 그래서 단순 언어적의 한 계로 풀지 않고 미뤄뒀었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번에 다시 풀게 되면서 시간 초과를 해결하기 위해 queue를 구현하였고(오픈 소스), 또 다른 풀이를 통해 index로 해결하는 법을 알 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/백준_Graph(DFS,BFS)</category>
      <category>백준 7576 js</category>
      <category>백준 7576 node.js</category>
      <category>백준 js</category>
      <category>백준 js 7576 토마토</category>
      <category>백준 node.js</category>
      <category>백준 node.js 7576 토마토</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/256</guid>
      <comments>https://ghost4551.tistory.com/256#entry256comment</comments>
      <pubDate>Fri, 22 Apr 2022 02:38:45 +0900</pubDate>
    </item>
    <item>
      <title>아토믹 디자인(Atomic Design) 소개 및 실제 사용 경험</title>
      <link>https://ghost4551.tistory.com/255</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아토믹 디자인(Atomic Design)을 착안하여 실제로 프로젝트를 수행한 경험을 늦게나마 공유하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;완벽한 아토믹 디자인이 아니지만 프로젝트 기획에 맞게 팀원들과 논의를 통해 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;적절히 입맛에 바꿔 사용했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그전에 아토믹 디자인에 대해서 간략히 알아보도록 합시다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;atomic-design이란&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;❓ Atomic Design이란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;웹 프론트 개발 프레임워크, 라이브러리인 Angular, Vue, React는 컴포넌트 단위로 개발을 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 컴포넌트를 가장 작은 단위로 설정하고 이를 바탕으로 상위 컴포넌트를 만들어 코드 재사용을 최대화하는 방법론이다. 따라서 아토믹 디자인이 컴포넌트 중심 설계 패턴에서 더욱 주목받게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아토믹 디자인은 원자(Atoms), 분자(Molecules), 유기체(Organisms), 템플릿(Templates), 페이지(Pages)로&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;효과적인 인터페이스 시스템을 만든다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;861&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/voVIZ/btrzDPi16ML/3HcNnUSQKFutl8DnMY0Zu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/voVIZ/btrzDPi16ML/3HcNnUSQKFutl8DnMY0Zu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/voVIZ/btrzDPi16ML/3HcNnUSQKFutl8DnMY0Zu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvoVIZ%2FbtrzDPi16ML%2F3HcNnUSQKFutl8DnMY0Zu1%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;861&quot; height=&quot;327&quot; data-origin-width=&quot;861&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Atoms(원자)&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;가장 작은 단위의 컴포넌트이다. (디자인과 기능의 최소 단위)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다양한 state 즉 상태, 색상, 폰트, 애니메이션과 같은 추상적인 요소가 포함될 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대표적인 컴포넌트는 버튼(Button), 텍스트(Text), 아이콘(Icon) 등이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-app=&quot;{&amp;quot;type&amp;quot;:&amp;quot;text&amp;quot;,&amp;quot;data&amp;quot;:[{&amp;quot;type&amp;quot;:&amp;quot;text&amp;quot;,&amp;quot;text&amp;quot;:&amp;quot;분자(Molecules)&amp;quot;}],&amp;quot;size&amp;quot;:&amp;quot;h2&amp;quot;}&quot; data-block-index=&quot;15&quot; data-shown=&quot;true&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;분자(Molecules)&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;2개 이상의 원자로 구성되어 있다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하나의 단위로 함께 동작하는 UI 컴포넌트들의 단순한 그룹이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대표적인 컴포넌트는 입력 폼(Input Form), 내비게이션(Navigation), 카드(Card) 등이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;-organism유기체&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Organism(유기체)&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;분자들을 결합하여 유기체를 형성 (분자가 되지 않은 원자도 포함)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인터페이스가 어떻게 보이는지 시작하는 단계&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대표적인 컴포넌트는 입력 폼과 함께 헤더를 감싸거나, 카드를 관리하는 그리드 등이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-app=&quot;{&amp;quot;type&amp;quot;:&amp;quot;text&amp;quot;,&amp;quot;data&amp;quot;:[{&amp;quot;type&amp;quot;:&amp;quot;text&amp;quot;,&amp;quot;text&amp;quot;:&amp;quot;템플릿(Templetes)&amp;quot;}],&amp;quot;size&amp;quot;:&amp;quot;h2&amp;quot;}&quot; data-block-index=&quot;26&quot; data-shown=&quot;true&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;템플릿(Templetes)&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;여러 유기체의 집합&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;디자인을 확인하고 레이아웃이 실제로 구동하는지 확인하는 단계&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대표적인 컴포넌트는 여러 카드와 그리드를 묶는 템플릿(헤더, 메인, 푸터) 등이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;-page페이지&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Page(페이지)&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;템플릿을 이용해서 배치를 통해 컴포넌트를 그려서 디스플레이한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;완성된 하나의 페이지이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  장점&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;디자인 시스템 구성에 있어서 가이드라인으로 활용할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;애플리케이션과 분리하여 컴포넌트를 개발하고 테스트할 수 있고, 스타일 가이드와 같은 도구에서 볼 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;컴포넌트 재사용성이 극대화된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  단점&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;의존성과 복잡도가 까다롭다&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ex) A에서 사용하는 헤더에서 변경 사항이 필요할 때, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 헤더 컴포넌트에 props를 추가해 변경 사항을 적용하게 한다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 새로운 헤더 컴포넌트를 만든다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1번으로 해결할 경우 어디까지 props로 지정해야 할지 모호하다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2번으로 해결할 경우 컴포넌트가 무한 증식될 수 있고, 재사용성이 떨어지고, 어떤 기능을 하는지 명확히 구분하기 어려워진다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&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;h2 data-ke-size=&quot;size26&quot;&gt;  &quot;&lt;a href=&quot;https://github.com/prgrms-web-devcourse/Team_DOKEV_GOLDDDUCK_FE&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;금나와라 뚝딱&lt;/a&gt;&quot;&lt;span style=&quot;color: #000000;&quot;&gt; 프로젝트 적용 후기&lt;/span&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;❓ 도입 이유&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이전 프로젝트에서 구체적으로 컴포넌트를 나누지 않고 개발을 진행했었다. 그러다 보니 중복된 코드가 많았고, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;재사용 가능한 컴포넌트가 극히 드물었다. 그래서 이번 프로젝트에서는 재사용성 가능한 컴포넌트로 개발하고 싶었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;팀원들과 프로젝트 회의에서 재사용성을 극대화하는 방향에 대해서 얘기를 했고, 개발 방법론에 대해 찾아보던 도중, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아토믹 디자인을 접하게 되었다. 그래서 아토믹 디자인을 기획 단계에서부터 적용하면서 개발을 진행했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;우선 피그마를 통해 우리가 개발해야 할 페이지를 그렸다. 그 후, 여러 번 중복으로 사용되는 기능, 디자인을 찾기 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;시작했다. Checkbox, Input, Icon, Image, Input, Logo, Modal, Avatar, Button 등 수많은 최소한의 기능을 여러 번 사용되는 것을 확인했고 우리는 이를 Atoms(원자)로 분류해서 개발을 했다.&lt;/span&gt;&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/c2O2Kx/btrzz6mluhC/Fn2XKNCd3XrkC4pWYk1wHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2O2Kx/btrzz6mluhC/Fn2XKNCd3XrkC4pWYk1wHK/img.png&quot; width=&quot;189&quot; height=&quot;328&quot; data-origin-width=&quot;178&quot; data-origin-height=&quot;309&quot; data-widthpercent=&quot;34.59&quot; style=&quot;width: 34.1885%; margin-right: 10px;&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2O2Kx/btrzz6mluhC/Fn2XKNCd3XrkC4pWYk1wHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2O2Kx%2Fbtrzz6mluhC%2FFn2XKNCd3XrkC4pWYk1wHK%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;178&quot; height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1WA3P/btrzMJiCZwR/PkUpZnKzSfqo0XHr0YfM20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1WA3P/btrzMJiCZwR/PkUpZnKzSfqo0XHr0YfM20/img.png&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;504&quot; data-is-animation=&quot;false&quot; style=&quot;width: 64.6487%;&quot; data-widthpercent=&quot;65.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1WA3P/btrzMJiCZwR/PkUpZnKzSfqo0XHr0YfM20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1WA3P%2FbtrzMJiCZwR%2FPkUpZnKzSfqo0XHr0YfM20%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;549&quot; height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;원자: 좌(폴더구조) 우(storybook Avatar 예시)&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;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 이 원자들을 조합해 하나의 기능 및 동작을 수행하는&amp;nbsp;분자(Molecules) &amp;amp;&amp;nbsp;Organism(유기체)를 개발했다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;분자와 유기체를 나누지 않은&amp;nbsp;이유가 재사용성이 필요 없는 원자나 분자 같은 것들은 애매모호해서 우리는 그냥 해당 기능에 바로 작성했기로 했기 때문이다. (타이머 기능, TextArea 등)&lt;/span&gt;&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/zo9lN/btrzAUMJ4t6/nYwvqgocwQM3VXMbKNONkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zo9lN/btrzAUMJ4t6/nYwvqgocwQM3VXMbKNONkk/img.png&quot; width=&quot;195&quot; height=&quot;394&quot; data-origin-width=&quot;165&quot; data-origin-height=&quot;333&quot; data-widthpercent=&quot;36.1&quot; style=&quot;width: 35.6795%; margin-right: 10px;&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zo9lN/btrzAUMJ4t6/nYwvqgocwQM3VXMbKNONkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzo9lN%2FbtrzAUMJ4t6%2FnYwvqgocwQM3VXMbKNONkk%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;165&quot; height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NXNie/btrzKufEuMp/ppFdQICY4ocw1zQNphx8D0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NXNie/btrzKufEuMp/ppFdQICY4ocw1zQNphx8D0/img.png&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;716&quot; data-is-animation=&quot;false&quot; style=&quot;width: 63.1577%;&quot; data-widthpercent=&quot;63.9&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NXNie/btrzKufEuMp/ppFdQICY4ocw1zQNphx8D0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNXNie%2FbtrzKufEuMp%2FppFdQICY4ocw1zQNphx8D0%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;628&quot; height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;분자&amp;amp;유기체 : 좌(폴더구조) 우(storybook TextArea 예시)&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;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마지막으로는 원자, 분자&amp;amp;유기체의 조합으로 화면을 구성하는 템플릿(Templetes) &amp;amp; Page(페이지)을 개발했다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 또한 템플릿과 페이지를 나누지 않는 이유는 템플릿에서 재사용성이 우리 프로젝트에서는 필요가 없었기 때문에 바로 페이지 단계로 넘어가서 작업을 했다. (그래서 페이지 단 코드가 다소 길다)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;207&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5O3yj/btrzGMNeoNG/7adYWBARD08pb7zjsOUawk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5O3yj/btrzGMNeoNG/7adYWBARD08pb7zjsOUawk/img.png&quot; data-alt=&quot;페이지(템플릿)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5O3yj/btrzGMNeoNG/7adYWBARD08pb7zjsOUawk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5O3yj%2FbtrzGMNeoNG%2F7adYWBARD08pb7zjsOUawk%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;207&quot; height=&quot;312&quot; data-origin-width=&quot;207&quot; data-origin-height=&quot;312&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  사용하면서 느낀 Atomic Design 장점&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 확실히 분자 형태로 자주 사용하는 Button, Avatar 등 기본 컴포넌트를 만들어 놓으니 재사용이 극대화될 수 있었다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;또한 다시 작업해야 할 필요성도 없었고, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;디자인도 통일성을 가질 수 있어서 매우 좋았다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 협업 시에도 필요한 컴포넌트만 가져다 사용하면 되고, StoryBook을 활용해 UI, UX 확인 및 테스트를 할 수 있어서 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;매우 편리했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  사용하면서 느낀 Atomic Design 단점&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 분자, 원자, 유기체, 템플릿, 페이지의 5단계를 명확하게 나누기가 힘들었다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;처음으로 디자인 시스템을 도입하여 미숙해서 그런지 이런 기능도 굳이 분자로 나눠야 하나?라는 고민도 있었고, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;재사용이 필요 없고 한 번만 사용할 건데 그냥 포함 시키면 안 되나?라는 고민도 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 컴포넌트 수정 사항이 빈번했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이건 개발 기획에서 약간의 미흡한 점으로 인해 발생했던 것 같다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를 들어 button에 대한 hover 이벤트, click 이벤트를 적용했다. 이를 재사용하고 활용하는 더 높은 단계에서 버튼 비활성화 기능 (disabled 속성) 값이 필요하면 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기존 button 컴포넌트를 수정하여 해당 옵션을 추가해야 하는 경우와 같은 수정 상황이 빈번히 발생했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  결론&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아마 프로젝트 Repository를 타고 들어가서 확인하면&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot;이게 무슨 아토믹 디자인이야&quot;라고 할 만큼 &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;100% 활용하지 못했다. 하지만 우리는 우리 프로젝트 상황에 맞게 변형 시켜서 사용했다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;나름 잘 활용했고, &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&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 data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  참고 자료&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a href=&quot;https://ui.toast.com/weekly-pick/ko_20200213&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ui.toast.com/weekly-pick/ko_20200213&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;color: #000000;&quot;&gt;&lt;a href=&quot;https://zoomkoding.github.io/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BA%A0%ED%94%84/2020/07/09/atomic-design-pattern.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://zoomkoding.github.io/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BA%A0%ED%94%84/2020/07/09/atomic-design-pattern.html&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;color: #000000;&quot;&gt;&lt;a href=&quot;https://velog.io/@moaikang/Atomic-Design%EC%9D%84-%EC%9D%91%EC%9A%A9%ED%95%B4-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%A1%B0%EB%A5%BC-%EC%84%A4%EA%B3%84%ED%95%98%EA%B3%A0-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%83%9D%EC%9D%84-%EA%B2%B0%EC%A0%95%ED%95%9C-%EA%B2%BD%ED%97%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@moaikang/Atomic-Design%EC%9D%84-%EC%9D%91%EC%9A%A9%ED%95%B4-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%A1%B0%EB%A5%BC-%EC%84%A4%EA%B3%84%ED%95%98%EA%B3%A0-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%83%9D%EC%9D%84-%EA%B2%B0%EC%A0%95%ED%95%9C-%EA%B2%BD%ED%97%98&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발지식</category>
      <category>atomic design</category>
      <category>Atomic Design react 프로젝트 적용 후기</category>
      <category>Atomic Design 프로젝트</category>
      <category>아토믹 디자인</category>
      <category>아토믹 디자인 react 프로젝트 적용 후기</category>
      <category>아토믹 디자인 프로젝트</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/255</guid>
      <comments>https://ghost4551.tistory.com/255#entry255comment</comments>
      <pubDate>Tue, 19 Apr 2022 02:48:01 +0900</pubDate>
    </item>
    <item>
      <title>교내 개발 동아리 특강</title>
      <link>https://ghost4551.tistory.com/254</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특강 소개에 앞서 간략하게 동아리에 대해 소개를 하고 본론으로 넘어가겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  개발 동아리 &quot;InQ&quot; 창립&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2021년도에 복학한 후배들과 함께 개발 얘기를 나누던 도중, 교내에 개발 동아리가 없어 동아리를 창립하여 학부생들끼리 학습하고 프로젝트를 같이 하는 환경이 생기면 좋겠다는 얘기를 나누었다. 그리고 며칠뒤 직접 만들어서 활동하자고 의견을 모아 바로 창립을 하게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;졸업을 앞두고 있어서 직접적인 활동은 하지 못했지만, 대외 개발 동아리 활동, 부트 캠프 활동 경험을 통해&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 활동 방식을 모태로 동아리 방향성을 잡았다. 하나의 팀을 꾸려 하나의 프로젝트를 목표로 한 학기를 수행하도록 계획을 했고, 거기에서 나의 역할은 개발 경험이 다소 적은 팀 or 학생들을 도와 방향성을 잡아주는 길라잡이 역할을 수행했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &amp;zwj;♂️ 동아리 특강 진행&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;졸업을 하고 취준 생활을 하고 있던 도중에 학교는 대면 강의로 바뀌었고, 그에 따라 신입생들이 늘어나게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;저학년과 코로나 학번으로 개발 경험이 적어 동아리 운영에 힘이 든다는 소식을 후배를 통해 듣게 되었고, 학부생 입장에서 어떻게 공부하면 좋은지 나에게 의견을 물어보았다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;나 역시도 개발을 희망하면서 맨땅에 헤딩하듯 삽질과 걱정이 많았기에 이러한 나의 경험을 통해 조금이나마 후배들에게 방향성이 되고, 도움이 되고자 특강을 진행하기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;처음으로 누군가에게 특강을 하는 만큼 많이 조심스러웠고 주변에 많은 자문과 자료들을 찾아보면서 준비했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot;만약 내가 학부생으로 돌아간다면 어떻게 공부할지?&quot;라는 생각으로 준비를 하게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이를 통해 &quot;개발자의 필수 덕목&quot;이라는 주제로 특강을 진행했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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/8VSl3/btrzw9BuWQm/8TL5UCkehZi7zJf4B0bukk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8VSl3/btrzw9BuWQm/8TL5UCkehZi7zJf4B0bukk/img.png&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;719&quot; width=&quot;372&quot; height=&quot;330&quot; data-widthpercent=&quot;29.7&quot; style=&quot;width: 29.0087%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8VSl3/btrzw9BuWQm/8TL5UCkehZi7zJf4B0bukk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8VSl3%2Fbtrzw9BuWQm%2F8TL5UCkehZi7zJf4B0bukk%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;810&quot; height=&quot;719&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAqGpy/btrzwQoEQ6O/VkkwHUu4juVpMbW0q1HDO1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAqGpy/btrzwQoEQ6O/VkkwHUu4juVpMbW0q1HDO1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;KakaoTalk_20220413_203402728_09.jpg&quot; style=&quot;width: 34.3329%; margin-right: 10px;&quot; data-widthpercent=&quot;35.15&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAqGpy/btrzwQoEQ6O/VkkwHUu4juVpMbW0q1HDO1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAqGpy%2FbtrzwQoEQ6O%2FVkkwHUu4juVpMbW0q1HDO1%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;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccUlLm/btrzv3B24hz/HtvkQMoPKtWrNu7ieuFmKk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccUlLm/btrzv3B24hz/HtvkQMoPKtWrNu7ieuFmKk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;KakaoTalk_20220413_203402728_07.jpg&quot; data-widthpercent=&quot;35.15&quot; style=&quot;width: 34.3329%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccUlLm/btrzv3B24hz/HtvkQMoPKtWrNu7ieuFmKk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccUlLm%2Fbtrzv3B24hz%2FHtvkQMoPKtWrNu7ieuFmKk%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;4032&quot; height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;특강 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;취준생 입장과 학교 생활을 이루어 봤을 때 가장 중요하다고 생각하는 것들을 어떤 식으로 스킬과 역량을 쌓아나갈지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;나의 경험에 빗대어 준비를 했고, 성공적으로 특강을 마무리할 수 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특강과 더불어 Q&amp;amp;A를 진행했다. 내가 예전에 고민했던 내용들이랑 많이 비슷한 내용이 많았다. 그렇기에 말이 많아지게 되었고 짧게 끝내려고 했지만 1시간을 넘겨버렸다...&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; &amp;zwj;♂️&lt;/span&gt;후기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;준비 과정에서 부담감도 있었고, 아직 취업도 못했는데 내가 도와줄 수 있을까라고 생각을 했지만,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특강 이후에 많은 도움이 되었다고 연락이 왔다. 선배로서 굉장히 뿌듯한 시간이었고, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;도움을 줄 수 있어서 매우 좋았던 경험이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;나중에는 실력 있는 개발자가 되어 우아한 형제 세미나, 네이버 세미나에서 특강을 진행하시는 분들처럼&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;꼭 그렇게 될 수 있도록 열심히 해야겠다는 생각이 들었다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>일기장</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/254</guid>
      <comments>https://ghost4551.tistory.com/254#entry254comment</comments>
      <pubDate>Fri, 15 Apr 2022 19:36:59 +0900</pubDate>
    </item>
    <item>
      <title>[백준 node.js] 1149번_RGB거리</title>
      <link>https://ghost4551.tistory.com/253</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1463&quot;&gt;1463번_1로 만들기&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;실버3&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제&amp;nbsp;설명&lt;/b&gt;&amp;nbsp;: N 개의 줄에 각각 빨강, 초록, 파랑 집이 주어진다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;N 번째 선택한 집의 색은 N-1 번째 선택한 집의 색과 같지 않아야 한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉 N 번째 집이 빨간색일 때,&amp;nbsp; N-1, N+1은 빨간색으로 칠할 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또한, 각각의 집의 비용이 다르기 때문에 최소한의 비용을 선택하면서 규칙에 어긋나지 않게&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;선택해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  모든 경우를 선택해서 마지막에 최솟값을 출력하면 된다. 즉, N 번째 파란색을 선택하는 경우는 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;N 번째 파란색 집 + N-1 번째 빨간색 집 + N-1 번째 초록색 집의 가격이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 개념을 생각해서 각각의 집을 선택할 때 적용하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649667003067&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fs = require('fs')
const input = fs.readFileSync('/dev/stdin').toString().trim().split('\n')
const [n, ...n_arr] = input.slice()
const m = n_arr.map((el) =&amp;gt; el.trim().split(/\s/).map(Number))

function solution() {
  let answer = 0
  const dp = Array.from(Array(Number(n)), () =&amp;gt; Array(3).fill(0))
  // 0번째 초기화
  dp[0][0] = m[0][0]
  dp[0][1] = m[0][1]
  dp[0][2] = m[0][2]
  for (let i = 1; i &amp;lt; Number(n); i++) {
    // i, i-1이 서로 다른 색상을 가져야하기 때문에
    // i에서 가져야 하는 색상을 제외한 색상 중 i-1에서 최소값을 구한다.
    dp[i][0] = Math.min(dp[i - 1][1], dp[i - 1][2]) + m[i][0]
    dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][2]) + m[i][1]
    dp[i][2] = Math.min(dp[i - 1][0], dp[i - 1][1]) + m[i][2]
  }
  answer = Math.min(...dp[Number(n - 1)]) // 누적된 값중에서 최소값을 찾는다.
  return answer
}

console.log(solution())&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;N, N-1이 서로 다른 색상을 가져야 하기 때문에 N에서 가져야 하는 색상을 제외한 색상 중 N-1에서 최솟값을 구한다. dp는 누적된 집의 비용, m은 기존 집의 비용일 때, 점화식으로 표현하면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649667287403&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; dp[i][0] = Math.min(dp[i - 1][1], dp[i - 1][2]) + m[i][0]
 dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][2]) + m[i][1]
 dp[i][2] = Math.min(dp[i - 1][0], dp[i - 1][1]) + m[i][2]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이후 누적된 마지막 집의 빨강, 초록, 파랑 집에서 최소 비용을 return 해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/백준_DP_동적계획법</category>
      <category>1149 nodejs</category>
      <category>baekjoon 1149번 RGB거리</category>
      <category>baekjoon 1149번 RGB거리 node.js</category>
      <category>백준 1149번 RGB거리 node.js</category>
      <category>백준1149번 RGB거리</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/253</guid>
      <comments>https://ghost4551.tistory.com/253#entry253comment</comments>
      <pubDate>Mon, 11 Apr 2022 18:01:35 +0900</pubDate>
    </item>
    <item>
      <title>[백준 node.js] 1463번_1로 만들기</title>
      <link>https://ghost4551.tistory.com/252</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.acmicpc.net/problem/1463&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;1463번_1로 만들기&lt;/a&gt;&lt;/span&gt; 실버3&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제&amp;nbsp;설명&lt;/b&gt; : 정수 N이 주어졌을 때 주어진 조건에 따라 1로 만든다. 이때 연산을 사용하는 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;횟수의 최솟값을 구해라.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  DP 유형이므로 i번째를 구할 때, i 이전의 값들을 이용해서 해결한다.&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_1649601960325&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fs = require('fs')
const input = fs.readFileSync('/dev/stdin').toString().trim()

let N = Number(input)
let answer = Array.from(Array(N + 1), () =&amp;gt; 0)
answer[2] = 1
answer[3] = 1

for (let i = 4; i &amp;lt;= N; i++) {
  // -1 하는 경우 -&amp;gt; 이전 값의 경우의 수 +1
  answer[i] = answer[i - 1] + 1
  // 3으로 나누어지는 경우 -&amp;gt; 3으로 나눈 값의 경우의 수 +1
  if (i % 3 === 0) {
    answer[i] = Math.min(answer[i], answer[i / 3] + 1)
  }
  // 2로 나누어지는 경우 -&amp;gt; 2로 나눈 값의 경우의 수 +1
  if (i % 2 === 0) {
    answer[i] = Math.min(answer[i], answer[i / 2] + 1)
  }
}

console.log(answer[N])&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;숫자 2와 3은 1로 가는 경우의 수가 한 가지이다. 4의 경우에는 3으로 가는 경우의 수 + 3이 1로 가는 경우의 수의 합이다. 또 숫자 5는 4로 가는 경우의 수 + 4가 1로 가는 경우의 수가 되고,&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;숫자 6은 5로 가는 경우의 수 + 5가 1로 가는 경우 수이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이를 점화식으로 나타내면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649602362516&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;answer[i] = answer[i - 1] + 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;하지만 숫자 6은 2로 나누어지므로 2로 나눈 값에 -1을 통해 1로 만들 수 있기 때문에 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2로 나눈 몫 + 1이 된다. 이들 값 중 최솟값으로 지정해 주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649602384163&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (i % 2 === 0) {
    answer[i] = Math.min(answer[i], answer[i / 2] + 1)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3의로 나누어지는 경우도 위와 동일하게 작성하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/백준_DP_동적계획법</category>
      <category>1463 node.js</category>
      <category>baekjoon 1463 node.js</category>
      <category>백준 1463 node.js</category>
      <category>백준 1463번 1로 만들기 node.js</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/252</guid>
      <comments>https://ghost4551.tistory.com/252#entry252comment</comments>
      <pubDate>Sun, 10 Apr 2022 23:55:07 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스 JavaScript] 최소직사각형</title>
      <link>https://ghost4551.tistory.com/251</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  최소직사각형 JS&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제&amp;nbsp;설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;: 가로, 세로 길이가 적힌 명함들의 정보가 주어질 때, 최소한의 크기로 명함 지갑을 만들려고 할 때, &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;필요한 가로, 세로 크기를 구해라. 단 명함은 가로로 눕혀 가로 세로를 바꿀 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  명함을 가로 세로 전환이 가능하므로(눕힐 수 있다) 명함 당 가로를 큰 쪽, 세로를 작을 쪽으로 돌린다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649423657478&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(sizes) {
  let answer = 1

  // 가로의 길이를 제일 긴 변으로 설정 후 회전
  for (let i = 0; i &amp;lt; sizes.length; i++) {
    if (sizes[i][0] &amp;lt; sizes[i][1]) {
      ;[sizes[i][0], sizes[i][1]] = [sizes[i][1], sizes[i][0]]
    }
  }

  // 가로의 최대값 * 세로의 최대값 = 최소 지갑
  let x = sizes.sort((a, b) =&amp;gt; b[0] - a[0])[0][0]
  let y = sizes.sort((a, b) =&amp;gt; b[1] - a[1])[0][1]
  answer = x * y
  return answer
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 명함들을 순회하면서 가로를 큰 쪽으로 돌린다. 즉, 명함이 세로가 더 길고 가로가 더 짧으면 눕혀서 가로 세로를 반대로 바꾸어 가로를 큰 쪽으로, 세로를 작은 쪽으로 돌린다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. 1번을 수행하고 나면, 모든 명함이 큰 쪽이 가로, 작은 쪽이 세로가 되어있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;그러면 최소한의 지갑을 만들기 위해서는 가로에서 제일 큰 값, 세로에서 제일 큰 값의 지갑을 만들어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. 정렬을 통해 원하는 부분을 추출하고 return 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/프로그래머스_Level1</category>
      <category>프로그래머스 level1 최소직사각형 js</category>
      <category>프로그래머스 최소직사각형 js</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/251</guid>
      <comments>https://ghost4551.tistory.com/251#entry251comment</comments>
      <pubDate>Fri, 8 Apr 2022 22:19:09 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스 JavaScript] 신고 결과 받기</title>
      <link>https://ghost4551.tistory.com/250</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  신고 결과 받기 JS&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; : 일반 유저가 악성 유저를 신고할 수 있다. 사용자가 지정한 신고 횟수 이상으로 신고를 당한 악성 유저는 차단을 당하고, 차단 당한 사실을 신고한 유저에게 메일로 알려줘야 한다.&amp;nbsp; &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;http://일반%20유저가 악성 유저를 신고할 수 있다. 사용자가 지정한 신고 횟수 이상으로 신고를 당한 악성 유저는 차단을 당하고, 차단 당한 사실을 신고한 유저에게 메일로 알려줘야한다&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로그래머스 문제&lt;/a&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;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;  Map, Set 객체를 이용한다.&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;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;처음 해결한 코드&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649394594476&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(id_list, report, k) {
  let answer = []

  let idMap = new Map()
  // key : id_list,
  // value : {reportList : 유저가 신고한 ID 목록, cnt : 자신이 신고 당한 횟수, mail : 메일을 받을 횟수}
  id_list.forEach((el) =&amp;gt; idMap.set(el, { reportList: [], cnt: 0, mail: 0 }))

  report.forEach((el) =&amp;gt; {
    let [userId, reportId] = el.split(' ')

    let reportValue = idMap.get(userId)
    let cntValue = idMap.get(reportId)

    // 한 유저가 같은 유저 여러 번 신고하는 경우 처리
    // 유저 ID에 유저가 신고한 ID가 포함되지 않을 경우(아직 신고한 적이 없을 경우)만
    // 신고한 ID를 찾아 자신이 신고 당한 횟수 카운트
    if (![...reportValue.reportList].includes(reportId)) {
      idMap.set(reportId, {
        reportList: cntValue.reportList,
        cnt: cntValue.cnt + 1,
        mail: 0,
      })
    }

    // 유저 ID를 찾아 유저가 신고한 ID 추가
    // Set 객체를 통해 중복 제거
    idMap.set(userId, {
      reportList: new Set([...reportValue.reportList, reportId]),
      cnt: reportValue.cnt,
      mail: 0,
    })
  })

  // 정지 userId 판별
  let blackList = []
  for (let [key, value] of idMap) {
    if (value.cnt &amp;gt;= k) {
      blackList.push(key)
    }
  }

  // 메일 발송 갯수 카운트
  // 유저가 신고한 ID에 정지 ID가 포함되는 경우 유저 mail 값 카운트
  blackList.forEach((id) =&amp;gt; {
    for (let [key, value] of idMap) {
      if ([...value.reportList].includes(id)) {
        idMap.set(key, {
          reportList: value.reportList,
          cnt: value.cnt,
          mail: value.mail + 1,
        })
      }
    }
  })

  // 메일 발송
  for (let [key, value] of idMap) {
    answer.push(value.mail)
  }

  return answer
}

const id_list = ['muzi', 'frodo', 'apeach', 'neo']
const report = [
  'muzi frodo',
  'apeach frodo',
  'frodo neo',
  'muzi neo',
  'apeach muzi',
]
// console.log(solution(id_list, report, 2))

const id_list2 = ['con', 'ryan']
const report2 = ['ryan con', 'ryan con', 'ryan con', 'ryan con']
console.log(solution(id_list2, report2, 3))&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. Map 객체를 이용해서(idMap) i 번째 유저가 신고한 ID, i 번째 유저 자기 자신이 신고 당한 횟수, i 번째 유저가 메일을 받을 횟수를 기억할 resportList, cnt, mail 변수를 선언한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. 매개변수 report를 순회하면서, 자신이 신고 당한 횟수, 자신이 신고한 유저 정보를 저장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. 1,2번을 수행 후 idMap을 순회하면서 자신이 신고 당한 횟수가 K 횟수 이상일 때, 유저 이름을 블랙리스트 배열에 저장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;4. idMap을 순회하면서 신고한 유저 정보에 블랙리스트 id가 포함되면 해당 유저에게 메일을 보내야 하므로 메일 횟수를 카운트한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;5. 메일 횟수만 따로 배열에 저장 후 return 해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  문제는 풀었지만 로직이 너무 복잡하고 이해하기 어렵다.. 리팩토링을 위해 다른 사람의 풀이 참고&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;리팩토링 코드&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649395383604&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(id_list, report, k) {
  let answer = []

  // 중복 제거 후, 유저 ID, 유저가 신고한 ID 분리
  let reports = [...new Set(report)].map((el) =&amp;gt; el.split(' '))

  // 신고 당한 ID Map
  let reportId = new Map()

  // 신고 당한 ID 카운트
  for ([, report] of reports) {
    reportId.set(report, reportId.get(report) + 1 || 1)
  }

  // 메일 발송 ID Map
  let mailId = new Map()

  // 메일 발송 갯수 카운트
  for ([user, report] of reports) {
    if (reportId.get(report) &amp;gt;= k) {
      mailId.set(user, mailId.get(user) + 1 || 1)
    }
  }

  // 메일 발송 추출 (없는 경우 0)
  answer = id_list.map((id) =&amp;gt; mailId.get(id) || 0)

  return answer
}

const id_list = ['muzi', 'frodo', 'apeach', 'neo']
const report = [
  'muzi frodo',
  'apeach frodo',
  'frodo neo',
  'muzi neo',
  'apeach muzi',
]
console.log(solution(id_list, report, 2))

const id_list2 = ['con', 'ryan']
const report2 = ['ryan con', 'ryan con', 'ryan con', 'ryan con']
console.log(solution(id_list2, report2, 3))&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. Set 객체를 이용하여 문자열 자체를 중복 제거한다. 즉 동일한 유저가 동일한 다른 유저를 신고하는 경우를 처리한다. 그 후, 유저 ID, 유저가 신고한 ID를 분리하여 reports 배열에 저장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. reportId Map을 선언하여 신고 당한 ID를 카운트해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. mailId Map을 선언하여 신고 당한 ID가 K 횟수 이상일 때, mailId에 신고를 한 유저 ID 정보를 저장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;4. 메일 발송을 위해 id_list 순서대로 순회하며 메일 발송 횟수를 저장한 후 return 해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  Set 객체가 문자열 자체도 중복 제거할 수 있는지 몰랐다. 그래서 중복 제거를 위한 로직이 훨씬 깔끔해졌다. 또한 유저 ID, 신고 당한 유저 ID, 메일 발송을 각각 분리하여 조합하고 비교를 통해 훨씬 깔끔하고 직관적이고 짧은 코드로 해결할 수 있었다. 다른 사람들의 코드를 보면서도 알고리즘 푸는데 많이 도움 되는 것 같다.&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 style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/프로그래머스_Level1</category>
      <category>신고 결과 받기 js</category>
      <category>프로그래머스 Level1</category>
      <category>프로그래머스 level1 신고 결과 받기</category>
      <category>프로그래머스 신고 결과 받기 js</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/250</guid>
      <comments>https://ghost4551.tistory.com/250#entry250comment</comments>
      <pubDate>Fri, 8 Apr 2022 14:45:06 +0900</pubDate>
    </item>
    <item>
      <title>[DP] 최대 부분 증가 수열</title>
      <link>https://ghost4551.tistory.com/249</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  최대 부분 증가 수열&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;: N개의 자연수의 수열이 주어질 때, 가장 길게 증가하는 원소의 길이를 찾아라.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;ex) &lt;b&gt;2&lt;/b&gt;, 7, &lt;b&gt;5&lt;/b&gt;, 8, &lt;b&gt;6&lt;/b&gt;, 4, &lt;b&gt;7&lt;/b&gt;, &lt;b&gt;12&lt;/b&gt;, 3 이면 가장 길게 증가하는 원소들은&amp;nbsp; 2, 5, 6, 7, 12이다. 따라서 길이는 5이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;  i 번째 원소에서 0 ~ i-1 번째 원소를 탐색하면서 자기보다 작은 원소들의 개수를 파악한다.&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_1649336210986&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 최대 부분 증가수열 (DP)

function solution(arr, n) {
  let answer = 0
  let dp = Array.from(Array(n), () =&amp;gt; 0)
  dp[0] = 1

  for (let i = 1; i &amp;lt; n; i++) {
    let max = 0
    for (let j = i - 1; j &amp;gt;= 0; j--) {
      if (arr[i] &amp;gt; arr[j]) {
        max = Math.max(max, dp[j])
      }
    }
    dp[i] = max + 1
  }

  //   console.log(dp) 부분 증가 수열 원소
  answer = Math.max(...dp)

  return answer
}

let arr = [2, 7, 5, 8, 6, 4, 7, 12, 3]
console.log(solution(arr, 9)) // 5

let arr2 = [5, 3, 7, 8, 6, 2, 9, 4]
console.log(solution(arr2, 8)) // 4&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 삽입 정렬 로직과 유사하다. 위에 힌트에 작성한 것처럼 i 번째 원소에서 0 ~ i-1 번째 원소를 탐색하면서 자기보다 작은 원소들의 개수를 파악한다. 파악한 개수에 +1을 i 번째 원소에 넣어준다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. 1번을 수행하고 나면 i 번째는 i-1 번째까지의 수 중에서 자기보다 작은 수들의 개수를 파악할 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. N 번째까지 수행한 후, 배열 원소에서 가장 큰 수를 리턴해준다. 가장 큰 수가 결국 가장 크게 증가하는 수열의 수이기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <category>인프런 최대 부분 증가 수열 문제</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/249</guid>
      <comments>https://ghost4551.tistory.com/249#entry249comment</comments>
      <pubDate>Thu, 7 Apr 2022 22:16:59 +0900</pubDate>
    </item>
    <item>
      <title>[DP] 계단 오르기</title>
      <link>https://ghost4551.tistory.com/248</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  계단 오르기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; : 계단을 오를 때 한 번에 한 계단 또는 두 계단씩 올라간다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;4계단을 오른다면 1+1+1+1, 1+1+2, 1+2+1, 2+1+1, 2+2로 총 5가지이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;N 계단일 때 올라갈 수 있는 방법의 수는??&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&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_1649236144129&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 계단오르기 DP

function solution(n) {
  let answer = 0
  let dp = Array.from(Array(n + 1)).fill(0)
  dp[1] = 1 // 첫 번째 계단 경우의 수
  dp[2] = 2 // 두 번째 계단 경우의 수

  for (let i = 3; i &amp;lt;= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2]
  }
  answer = dp[n]
  return answer
}

console.log(solution(7))&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;세 번째 계단으로 가는 경우는 첫 번째 계단의 경우의 수 + 2 계단 오르는 경우,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 번째 계단의 경우의 수 + 1 계단 오르는 경우로 i-2, i-1 계단의 경우의 수와 동일하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉 3가지 경우이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;네 번째 계단의 경우는 두 번째 계단에서 +2 계단 오르는 경우, 세 번째 계단에서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;+1 계단 오르는 경우이다. 즉 2+3 = 5가지이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 계단 오르기 문제</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/248</guid>
      <comments>https://ghost4551.tistory.com/248#entry248comment</comments>
      <pubDate>Wed, 6 Apr 2022 18:13:45 +0900</pubDate>
    </item>
    <item>
      <title>[BFS, DFS] 섬나라 아일랜드</title>
      <link>https://ghost4551.tistory.com/247</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  섬나라 아일랜드 BFS, DFS 풀이&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;: 0과 1로 이루어진 격자판 정보가 주어진다. 이웃한 1의 집합이 곧 하나의 섬이 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;몇 개의 섬으로 이루어져 있는지 구해라. (상하좌우 대각선 이동 가능)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;  모든 격자판을 순회하면서 1일 경우 탐색을 하고, 상하좌우 대각선으로 이동하면서 0으로 값을 변경&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;하나의 탐색이 끝나면 하나의 섬을 탐색한 것이다.&lt;/span&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 style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;BFS&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1649138775312&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 섬나라 아일랜드(BFS)

// 상, 하, 좌, 우, 왼쪽 위, 오른쪽 위, 왼쪽 아래, 오른쪽 아래
const dx = [-1, 1, 0, 0, -1, -1, 1, 1]
const dy = [0, 0, -1, 1, -1, 1, -1, 1]

function BFS(i, j, arr) {
  let queue = []
  queue.push([i, j])
  arr[i][j] = 0
  while (queue.length) {
    let [x, y] = queue.shift()
    for (let k = 0; k &amp;lt; dx.length; k++) {
      let checkX = x + dx[k]
      let checkY = y + dy[k]
      if (checkLength(checkX, checkY, arr.length) &amp;amp;&amp;amp; arr[checkX][checkY]) {
        queue.push([checkX, checkY])
        arr[checkX][checkY] = 0
      }
    }
  }
}

// 배열의 인덱스 범위 체크 함수
function checkLength(checkX, checkY, n) {
  if (checkX &amp;gt;= 0 &amp;amp;&amp;amp; checkY &amp;gt;= 0 &amp;amp;&amp;amp; checkX &amp;lt; n &amp;amp;&amp;amp; checkY &amp;lt; n) return true
  else return false
}

function solution(n, arr) {
  let answer = 0

  for (let i = 0; i &amp;lt; n; i++) {
    for (let j = 0; j &amp;lt; n; j++) {
      if (arr[i][j]) {
        BFS(i, j, arr)
        answer += 1
      }
    }
  }

  return answer
}

let arr = [
  [1, 1, 0, 0, 0, 1, 0],
  [0, 1, 1, 0, 1, 1, 0],
  [0, 1, 0, 0, 0, 0, 0],
  [0, 0, 0, 1, 0, 1, 1],
  [1, 1, 0, 1, 1, 0, 0],
  [1, 0, 0, 0, 1, 0, 0],
  [1, 0, 1, 0, 1, 0, 0],
]
console.log(solution(7, arr))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;DFS&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1649138788716&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 섬나라 아일랜드(DFS)

// 상, 하, 좌, 우, 왼쪽 위, 오른쪽 위, 왼쪽 아래, 오른쪽 아래
const dx = [-1, 1, 0, 0, -1, -1, 1, 1]
const dy = [0, 0, -1, 1, -1, 1, -1, 1]

function DFS(arr, x, y) {
  arr[x][y] = 0 // 방문 처리

  for (let i = 0; i &amp;lt; dx.length; i++) {
    let checkX = x + dx[i]
    let checkY = y + dy[i]

    // 유효한 배열 범위 &amp;amp; 방문 하지 않았을 경우
    if (checkLength(checkX, checkY, arr.length) &amp;amp;&amp;amp; arr[checkX][checkY]) {
      DFS(arr, checkX, checkY)
    }
  }
}

// 배열의 인덱스 범위 체크 함수
function checkLength(checkX, checkY, n) {
  if (checkX &amp;gt;= 0 &amp;amp;&amp;amp; checkY &amp;gt;= 0 &amp;amp;&amp;amp; checkX &amp;lt; n &amp;amp;&amp;amp; checkY &amp;lt; n) return true
  else return false
}

function solution(n, arr) {
  let answer = 0

  for (let i = 0; i &amp;lt; n; i++) {
    for (let j = 0; j &amp;lt; n; j++) {
      // 방문하지 않은 경우 탐색 완료 후, 섬 개수 카운트
      if (arr[i][j]) {
        DFS(arr, i, j)
        answer += 1
      }
    }
  }

  return answer
}

let arr = [
  [1, 1, 0, 0, 0, 1, 0],
  [0, 1, 1, 0, 1, 1, 0],
  [0, 1, 0, 0, 0, 0, 0],
  [0, 0, 0, 1, 0, 1, 1],
  [1, 1, 0, 1, 1, 0, 0],
  [1, 0, 0, 0, 1, 0, 0],
  [1, 0, 1, 0, 1, 0, 0],
]
console.log(solution(7, arr))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 모든 격자판을 순회하면서 1인 경우 BFS/DFS를 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 상하좌우 대각선이 유효하고 1인 경우 재귀적으로 탐색을 진행하면서 0으로 값을 바꾼다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 하나의 BFS/DFS 탐색이 종료되었으면 하나의 섬을 탐색 완료한 것이므로 섬의 개수를 카운트해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 섬나라 아일랜드 문제</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/247</guid>
      <comments>https://ghost4551.tistory.com/247#entry247comment</comments>
      <pubDate>Tue, 5 Apr 2022 15:11:02 +0900</pubDate>
    </item>
    <item>
      <title>[BFS] 송아지 찾기</title>
      <link>https://ghost4551.tistory.com/246</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  송아지 찾기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt; : 현수가 -1, +1, +5칸을 움직일 수 있다. 최소 몇 번의 이동으로 송아지가 위치한 곳 까지 갈 수 있는지 구해라.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;  +1을 간 경우에서 -1을 수행할 경우 똑같은 위치를 반복해서 탐색하는 경우가 있다. 따라서 방문 표시를 위한 체크 배열을 이용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649002260369&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 송아지 찾기 (BFS: 상태트리탐색)

function solution(S, E) {
  let answer = 1 // 연산 편의를 위한 1부터 시작
  let queue = [S] // 처음 나의 위치
  let visited = [] // 중복 숫자 처리를 위한 방문 처리 배열

  while (true) {
    let tmp = []

    let n = queue.length // queue.shift 할 경우 길이가 반복문 수행시 달라짐
    for (let i = 0; i &amp;lt; n; i++) {
      let firstQueue = queue.shift()
      // 방문한적 없는 숫자일 경우만 계산을 위한 tmp 배열 삽입 및 방문 처리
      if (!visited.includes(firstQueue)) {
        tmp.push(firstQueue)
        visited.push(firstQueue)
      }
    }

    // 점프 연산
    for (let j = 0; j &amp;lt; tmp.length; j++) {
      queue.push(tmp[j] - 1)
      queue.push(tmp[j] + 1)
      queue.push(tmp[j] + 5)
    }

    // E 값이 있을 경우 송아지 찾기 완료
    if (queue.includes(E)) {
      console.log(queue)
      break
    }
    answer += 1
  }

  return answer
}

console.log(solution(5, 14)) // 3
console.log(solution(8, 3)) // 5&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 현재 현수의 위치를 queue에 삽입해 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 송아지를 찾을 때까지 반복문을 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. queue에 있는 값을 꺼내 방문하지 않았던 위치의 경우 방문 표시를 해주고, 해당 위치에서 -1, +1, +5 연산을 수행한 위치를 넣어준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. queue에서 송아지의 위치가 있을 경우 찾았기 때문에 반복문을 종료하고 그렇지 않으면 이동 횟수를 카운트해주고 3번의 과정을 다시 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 송아지 문제</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/246</guid>
      <comments>https://ghost4551.tistory.com/246#entry246comment</comments>
      <pubDate>Mon, 4 Apr 2022 01:20:39 +0900</pubDate>
    </item>
    <item>
      <title>[DFS] 경로 탐색</title>
      <link>https://ghost4551.tistory.com/245</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  경로 탐색 (인접행렬, 인접리스트)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;: 방향그래프가&amp;nbsp;주어지면&amp;nbsp;1번&amp;nbsp;정점에서&amp;nbsp;N번&amp;nbsp;정점으로&amp;nbsp;가는&amp;nbsp;모든&amp;nbsp;경로의&amp;nbsp;가지&amp;nbsp;수를&amp;nbsp;출력&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;(인접 행렬, 인접 리스트)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;인접 행렬 방식&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1649000582743&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 경로 탐색(인접행렬)

let path = [] // 탐색한 노드 경로

function DFS(n, graph, visited, v, arr) {
  if (v === n) {
    // 누적 경로 + 현재 경로
    path.push([...arr, v])
    return
  }

  visited[v] = 1 // 방문 처리

  // 인접 행렬을 순회하면서 방문하지 않은 노드들의 경로를 재귀적으로 탐색
  for (let j = 1; j &amp;lt;= n; j++) {
    if (graph[v][j] === 1 &amp;amp;&amp;amp; visited[j] === 0) {
      DFS(n, graph, visited, j, [...arr, v])
      visited[v] = 0 // 방문 해체
    }
  }
}

function solution(n, node) {
  let answer = 0
  let graph = Array.from(Array(n + 1), () =&amp;gt; Array(n + 1).fill(0))
  let visited = Array.from(Array(n + 1), () =&amp;gt; 0) // 방문 처리

  for (let [x, y] of node) {
    graph[x][y] = 1
  }

  DFS(n, graph, visited, 1, [])

  console.log(path)
  answer = path.length
  return answer
}

let node = [
  [1, 2],
  [1, 3],
  [1, 4],
  [2, 1],
  [2, 3],
  [2, 5],
  [3, 4],
  [4, 2],
  [4, 5],
]
console.log(solution(5, node))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인접 리스트 방식&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1649000628856&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 경로 탐색(인접리스트)

let path = [] // 탐색한 노드 경로

function DFS(n, graph, visited, v, arr) {
  if (v === n) {
    // 누적 경로 + 현재 경로
    path.push([...arr, v])
  }
  visited[v] = 1 // 현재 노드 방문 표시

  // 현재 노드의 배열 길이까지 탐색
  for (let j = 0; j &amp;lt; graph[v].length; j++) {
    // 현재 노드 값에 해당하는 방문 배열이 방문 한 적 없을 때
    if (visited[graph[v][j]] === 0) {
      DFS(n, graph, visited, graph[v][j], [...arr, v])
      visited[graph[v][j]] = 0 // 방문 해제
    }
  }
}

function solution(n, node) {
  let answer = 0
  let graph = Array.from(Array(n + 1), () =&amp;gt; Array(0))
  let visited = Array.from(Array(n + 1), () =&amp;gt; 0) // 방문 처리

  for (let [x, y] of node) {
    graph[x].push(y)
  }

  DFS(n, graph, visited, 1, [])

  console.log(path)
  answer = path.length
  return answer
}

let node = [
  [1, 2],
  [1, 3],
  [1, 4],
  [2, 1],
  [2, 3],
  [2, 5],
  [3, 4],
  [4, 2],
  [4, 5],
]
console.log(solution(5, node))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #424242;&quot;&gt;인접 행렬은 구현이 쉽지만, 노드의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;개수만큼&lt;span style=&quot;background-color: #ffffff; color: #424242;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;배열을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;할당해야 하므로&lt;span style=&quot;background-color: #ffffff; color: #424242;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메모리에 큰 낭비가 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #424242;&quot;&gt;인접 리스트는 구현이 행렬에 비해 다소 어렵지만, 주어진 연결 노드만을 가지고 있기 때문에&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #424242;&quot;&gt;메모리에서 큰 장점을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 경로탐색 문제</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/245</guid>
      <comments>https://ghost4551.tistory.com/245#entry245comment</comments>
      <pubDate>Mon, 4 Apr 2022 00:46:09 +0900</pubDate>
    </item>
    <item>
      <title>[재귀함수, 완전탐색] 조합 구하기</title>
      <link>https://ghost4551.tistory.com/244</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  조합 구하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;: 1부터 N 까지의 자연수 중 M 개를 뽑는 방법의 수 출력(조합)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;순열과 비슷하지만 순서가 상관 없기 때문에 [1,4] 뽑혔다면 [4,1]은 뽑는것이 불가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;  뽑은 조합 중에 가장 마지막 카드의 +1부터 N까지 재귀 함수 이용&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_1648916622207&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let combination = []

function recursion(n, r, arr) {
  // 배열 길이가 r 보다 클 때
  if (arr.length &amp;gt; r) {
    return
  }

  // arr 마지막 배열 +1 부터 반복문을 실행해야지 중복값이 안들어감
  // 처음 arr 배열은 undefined이기 때문에 || 1을 통해 1로 초기화
  for (let j = arr[arr.length - 1] + 1 || 1; j &amp;lt;= n; j++) {
    recursion(n, r, [...arr, j])
  }
  if (arr.length === r) {
    console.log(...arr)
    combination.push(arr)
  }
}

function solution(n, r) {
  let answer = 0
  let memoization = Array.from(Array(n + 1), () =&amp;gt; Array(r + 1).fill(0))

  recursion(n, r, [])
  answer = combination.length
  return answer
}

console.log(solution(4, 2))
// console.log(solution(33, 19))&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;1. 초기에는 1부터 N까지 반복문을 통해 재귀 함수를 호출한다. 호출시 해당 인덱스를 뽑은 조합에 넣어준다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. 재귀를 통해 받은 배열의 마지막 값 +1 부터 N까지 반복하면서 재귀 함수를 호출한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;ex) (N=4, R=2) 받은 배열이 [2]일 경우 [2,3], [2,4] 가 되어야 하므로 3~4까지 반복 호출해서 각각 해당 인덱스를 누적 시켜준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. 주어진 R의 길이에 부합하면 조합을 출력한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <category>인프런 조합 구하기 문제</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/244</guid>
      <comments>https://ghost4551.tistory.com/244#entry244comment</comments>
      <pubDate>Sun, 3 Apr 2022 01:36:49 +0900</pubDate>
    </item>
    <item>
      <title>[재귀함수, 완전탐색] 순열 구하기</title>
      <link>https://ghost4551.tistory.com/243</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  순열 구하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; : &lt;span style=&quot;background-color: #ffffff;&quot;&gt;N 개의 자연수 중 M 개를 뽑는 경우의 수 출력&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;중복은 제외되므로 한 번 뽑은 수는 뽑을 수 없다. 1,2 수 중 2개를 뽑을 때, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;[1,2], [2,1]의 경우만 가능하다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&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_1648903228628&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 순열 구하기 (DFS)

const permutation = []

function DFS(n, m, i, visited, arr) {
  if (arr.length &amp;gt;= m) {
    console.log(...arr)
    permutation.push(arr)
    return
  }

  for (let j = 0; j &amp;lt; n.length; j++) {
    if (visited[j] === 0) {
      visited[j] = 1
      DFS(n, m, i + 1, visited, [...arr, n[j]])
      visited[j] = 0
    }
  }
}

function solution(n, m) {
  let answer = 0

  // 중복 제거를 위해 방문 체크
  let visited = Array.from({ length: n.length }, () =&amp;gt; 0)

  DFS(n, m, 0, visited, [])
  answer = permutation.length
  return answer
}

let n = [3, 6, 9]
console.log(solution(n, 2))&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 한 번 뽑은 카드는 두 번째에 뽑을 수 없기 때문에 뽑은 여부를 기록하는 배열을 사용한다. (visited)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. 재귀 함수인에서 주어진 카드의 수만큼 반복하면서 카드를 순차적으로 뽑는다. (DFS 호출을 한다는 의미)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;여기서 뽑기 전에 해당 카드가 이전에 뽑았는지를 파악하고 뽑지 않았을 경우만 뽑는다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;뽑기가 끝난 후, 뽑았다는 표시를 초기화해준다. (3에 대해 [3,6], [3,9]를 뽑았지만, [6,3], [6,9]도 뽑아야 하기 때문에)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. baseCase에서 M 만큼 일 때 조합을 출력해 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 순열 구하기 문제</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/243</guid>
      <comments>https://ghost4551.tistory.com/243#entry243comment</comments>
      <pubDate>Sat, 2 Apr 2022 21:49:36 +0900</pubDate>
    </item>
    <item>
      <title>[재귀함수, 완전탐색] 중복순열 구하기</title>
      <link>https://ghost4551.tistory.com/242</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt; 중복 순열 구하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&amp;nbsp;: 1부터 N까지 번호중 M번을 뽑는 경우의 수를 모두 출력&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;중복도 가능하므로 [1,1] , [2,2] 등의 중복 숫자도 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;  재귀를 이용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1648902165677&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 중복순열 구하기 (다중 for문과 재귀의 차이점)

function DFS(n, m, i, arr) {
  // 모든 n 탐색 완료
  if (i &amp;gt;= n || arr.length &amp;gt; m) {
    return
  }

  // n 만큼 조합
  for (let j = 1; j &amp;lt;= n; j++) {
    DFS(n, m, i + 1, [...arr, j])
  }

  // m 조합 개수일 때만 출력
  if (arr.length &amp;gt;= m) console.log(...arr)
}

// 다중 for문의 한계 : m값에 따라 이중for, 삼중for...조건이 바뀜
function forFn(n) {
  for (let i = 1; i &amp;lt;= n; i++) {
    for (let j = 1; j &amp;lt;= n; j++) {
      console.log(i, j)
    }
  }
}

function solution(n, m) {
  let answer = n ** m

  forFn(n)
  console.log('----------------')
  DFS(n, m, 0, [])

  return answer
}

console.log(solution(3, 2))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;위의 코드를 보면 다중 for 문을 활용한 예시도 있다. 하지만 다중 for 문을 이용할 경우 M의 수에 따라 for 문의 수가 달라진다. 예를 들어 2번 뽑는 경우 이중 for, 3번 뽑는 경우 삼중 for 문이 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 재귀 함수 안에서 반복문을 통해 M 번을 뽑는 수만큼 반복문을 돌려준다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;그러면 M이 어떤 수의 경우에도 M 번 만큼 뽑을 수 있기 때문이다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. N과 M의 길이에 따라 적절한 baseCase를 설정하여 종료 조건 및 출력 조건을 제시한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <category>인프런 중복순열 구하기 문제</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/242</guid>
      <comments>https://ghost4551.tistory.com/242#entry242comment</comments>
      <pubDate>Sat, 2 Apr 2022 21:29:11 +0900</pubDate>
    </item>
    <item>
      <title>[재귀함수, 완전탐색] 합이 같은 부분집합</title>
      <link>https://ghost4551.tistory.com/241</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt; 합이&amp;nbsp;같은&amp;nbsp;부분집합(DFS&amp;nbsp;:&amp;nbsp;아마존&amp;nbsp;인터뷰)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; : N개의 원소로 구성된 자연수 집합이 주어지면, 이 집합을 두 개의 부분집합으로 나누었을 때 두 부분집합의 원소의 합이 서로 같은 경우가 존재하는지 판단&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  배열의 전체 합을 이용한다. 두 개의 부분집합의 원소가 같다는 것은 결국 집합의 원소 총합이 하나의 부분 집합의 뺀 결과의 값이 부분 집합의 값과 같다는 것이다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;재귀 함수를 이용해서 각각의 요소를 포함하는 경우, 포함하지 않는 경우로 나누어 판단한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1648834982567&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let flag = false // 합이 같은지 판단 변수
function recursion(arr, i, sum, total) {
  if (flag) return

  if (i &amp;gt;= arr.length) {
    if (total - sum === sum) {
      flag = true
    }
    return
  }

  recursion(arr, i + 1, sum + arr[i], total)
  recursion(arr, i + 1, sum, total)
}

function solution(a) {
  let answer = false
  let arr = [...a]
  let total = [...a].reduce((a, b) =&amp;gt; a + b, 0)
  // 배열, 시작 인덱스, 총합
  recursion(arr, 0, 0, total)
  answer = flag
  return answer
}

let a = [1, 3, 5, 6, 7, 10]
console.log(solution(a))&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 재귀 함수를 이용하여 배열을 순차적으로 순회하며 해당 요소를 포함하는 경우, 포함하지 않는 경우로 나눈다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. baseCase i가 배열의 모든 요소를 순회했을 때(하나의 집합이 완성되었을 경우) 총합 - 부분 집합 값 = 부분 집합 값인지 판단한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. 부분 집합 값을 찾은 경우, 전역 flag 변수로 해당 결과를 반환하고 나머지 재귀 함수 로직은 종료한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <category>인프런 합이 같은 부분집합 문제 js</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/241</guid>
      <comments>https://ghost4551.tistory.com/241#entry241comment</comments>
      <pubDate>Sat, 2 Apr 2022 02:51:05 +0900</pubDate>
    </item>
    <item>
      <title>[이분 탐색] 마구간 정하기</title>
      <link>https://ghost4551.tistory.com/240</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&amp;nbsp; 마구간 정하기 문제 (이분탐색 활용)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;: N 개의 위치를 나타내는 마구간이 있고, C 마리의 말이 있을 때,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;마구간에 말을 배치했을 때 가장 가까운 두 말의 거리의 최대를 구해라.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;즉, 가장 넓은 간격으로 넣을 수 있는 최대 거리를 구하는 문제이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;(말끼리 넓게 떨어져 있을수록 좋다)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;  마구간의 좌표는 고정이 아니므로 오름차순으로 정렬한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;제일 첫 마구간에 첫 말을 집어넣고 이분 검색을 통해 나머지 말들을 차례로 넣으면서,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&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_1648832749718&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 마구간 정하기 (결정 알고리즘)
// O(nlogn)

function solution(a, N) {
  let answer = 0
  let maxDistance = []
  let arrSort = [...a].sort((a, b) =&amp;gt; a - b)

  let start = 1
  let end = arrSort[arrSort.length - 1]

  while (start &amp;lt;= end) {
    let mid = parseInt((start + end) / 2)
    let ep = arrSort[0] // 현재 마구간의 위치
    let cnt = 1
    arrSort.forEach((x) =&amp;gt; {
      if (x - ep &amp;gt;= mid) {
        ep = x
        cnt += 1
      }
    })

    // 배치한 말의 개수가 적을 때 범위를 더 작게 탐색
    if (cnt &amp;lt; N) {
      end = mid - 1
    }
    // 배치한 말의 개수가 충족했을 때 범위를 더 크게 해서 최대 거리 파악
    else {
      maxDistance.push(mid)
      start = mid + 1
    }
  }

  answer = Math.max(...maxDistance)
  return answer
}

let a = [1, 2, 8, 4, 9]
console.log(solution(a, 3))&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 이분 탐색 범위를 지정하여 반복문을 진행한다. 마구간의 최소 범위는 1칸이고, 최대 범위는 제일 큰 마구간의 위치이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. 처음 마구간에 말을 넣고 cnt를 해준다. (한 마리를 배치한 상황)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. 반복문을 돌면서 이전에 넣은 마구간과의 차이가 이분 탐색 mid 값 보다 큰 지 비교한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3-1. 클 경우 범위를 넘었기 때문에 해당 위치의 마구간에 말을 넣고 3번의 과정을 반복한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. 3번의 과정이 끝나고 마구간에 넣은 말의 수와 주어진 말의 수를 비교하며 이분 탐색 범위를 조절한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4-1. 같은 경우에도 최적의 해를 위해 범위를 조절해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 마구간정하기 문제 js</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/240</guid>
      <comments>https://ghost4551.tistory.com/240#entry240comment</comments>
      <pubDate>Sat, 2 Apr 2022 02:32:09 +0900</pubDate>
    </item>
    <item>
      <title>[이분 탐색] 뮤직비디오</title>
      <link>https://ghost4551.tistory.com/239</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&amp;nbsp; 뮤직비디오 문제 (이분탐색 활용)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;: &lt;span style=&quot;background-color: #ffffff;&quot;&gt;레코드의 수와 각 노래들의 재생 시간이 주어질 때,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;레코드에 적절히 배분하였을 때, 레코드가 필요한 최소 용량을 구하는 문제이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;즉 레코드가 3개, 노래가 순서대로 5, 6, 7, 8분짜리가 있다고 했을 때, 최소 용량은 11이 필요하다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;5분, 6분의 노래가 하나의 레코드, 7,8분짜리가 각 하나의 레코드에 들어가는 경우가 최소 용량이기 때문.&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;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;  노래 리스트 중에 제일 긴 노래를 시작 지점,&amp;nbsp; 노래 리스트의 총합을 끝 지점으로 두어 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이분 탐색을 진행한다. 왜냐하면 노래 리스트들의 수 만큼 레코드가 있다고 했을 때, 한 개씩 사용해도 최소 용량은 제일 큰 노래이기 때문이다. 그리고 레코드가 하나 일때는 모든 노래가 담겨있기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648749908402&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이분 탐색 응용
// O(nlogn)

function solution(a, M) {
  let answer = 0
  let arr = [...a]

  let start = Math.max(...arr)
  let end = arr.reduce((a, b) =&amp;gt; a + b, 0) // 총 노래 시간
  let DVD = [] // DVD 용량

  // 제일 긴 노래 이상 ~ 총 노래 시간 사이의 조건을 이분 탐색으로 순회
  while (start &amp;lt;= end) {
    let mid = parseInt((start + end) / 2)
    let sum = 0
    let cnt = 1 // DVD 레코드 수
    arr.forEach((el) =&amp;gt; {
      if (sum + el &amp;gt; mid) {
        DVD.push(sum)
        cnt += 1
        sum = el
      } else {
        sum += el
      }
    })
    // 마지막 노래 카운팅
    DVD.push(sum)

    // M과 DVD 개수가 같을 때
    if (M === cnt) {
      answer = Math.max(...DVD)
      break
      // M이 더 크면 용량을 더 작게 쪼개고 DVD 개수를 늘릴 수 있다.
    } else if (M &amp;gt; cnt) {
      end = mid - 1
    } else {
      start = mid + 1
    }
    DVD = []
  }

  return answer
}

let a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(solution(a, 3))&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 이분 탐색 범위를 지정하여 반복문을 진행한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. 노래의 리스트들을 더해가며 mid 조건보다 클 경우 DVD에 해당 범위까지 더 해준다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;3. 그렇게 구한 DVD 개수와 문제에서 주어진 M 개가 같은 경우는 M 개의 레코드에 정확히 다 들어갔으므로,&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;DVD 리스트에 있는 최대 노래 수를 리턴해주면 그것이 필요로 하는 용량이다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3-1. 조건에 부합하지 않다면 이분 탐색 범위를 재 설정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 뮤직비디오 문제 js</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/239</guid>
      <comments>https://ghost4551.tistory.com/239#entry239comment</comments>
      <pubDate>Fri, 1 Apr 2022 03:14:46 +0900</pubDate>
    </item>
    <item>
      <title>[투포인트] 결혼식</title>
      <link>https://ghost4551.tistory.com/238</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&amp;nbsp; 결혼식 문제 (투포인트)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; : 결혼식에 참석하는 친구들의 도착 시간과 떠나는 시간이 제각각이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이때 최대로 겹치는 친구들의 시간을 구해라. &lt;span style=&quot;background-color: #ffffff;&quot;&gt;(자세한 내용은 강의 및 문제지 참고)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;* A의 떠나는 시간과 B의 도착시간이 같을 경우 겹치는 시간에서 제외한다. 즉, 떠나는 시간 미 포함&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&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_1648221063740&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(arr) {
  let answer = 0

  let arrival = [] // 도착 시간
  let exit = [] // 퇴장 시간
  arr.forEach((element) =&amp;gt; {
    arrival.push(element[0])
    exit.push(element[1])
  })

  arrival.sort((a, b) =&amp;gt; a - b)
  exit.sort((a, b) =&amp;gt; a - b)

  let cnt = 0 // 겹치는 인원 카운트
  let i = 0 // arrival Index
  let j = 0 // exit Index
  while (i &amp;lt; arrival.length + 1 &amp;amp;&amp;amp; j &amp;lt; exit.length) {
    if (arrival[i] &amp;lt; exit[j]) {
      cnt += 1
      i += 1
    } else {
      cnt -= 1
      j += 1
    }
    answer = answer &amp;gt; cnt ? answer : cnt
  }
  return answer
}

let arr = [
  [14, 18],
  [12, 15],
  [15, 20],
  [20, 30],
  [5, 14],
]
console.log(solution(arr)) // 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 도착 시간과 떠나는 시간을 각각 나누어 오름차순 정렬&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. 투 포인트로 도착 시간 배열과 떠나는 시간 배열을 순차적으로 비교해서 떠나는 시간이 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;더 크다면 겹치기 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;때문에 카운트 +1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. 도착 시간이 더 크다면 한 명이 빠졌기 때문에 카운트 -1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;4. 2번, 3번 과정을 투 포인트로 사용한 도착 시간, 떠나는 시간 배열의 인덱스가 종료될 때까지 순회&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;(마지막 종료까지 카운트하기 위해 도착 시간의 인덱스는 +1까지 포함)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 결혼식 문제 js</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/238</guid>
      <comments>https://ghost4551.tistory.com/238#entry238comment</comments>
      <pubDate>Sat, 26 Mar 2022 00:19:48 +0900</pubDate>
    </item>
    <item>
      <title>Least Recently Used(카카오 캐시 문제 변형)</title>
      <link>https://ghost4551.tistory.com/237</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&amp;nbsp; LRU 삽입 정렬 응용&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;: 캐시의 개수와 작업의 개수가 주어질 때, LRU 알고리즘과 유사하게 작업을 할 수 있게 구성하면 된다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;(자세한 내용은 강의 및 문제지 참고)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Tip. includes, indexOf 메소드 사용&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_1648219893109&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// LRU 삽입 정렬 응용 (카카오 캐시 문제 변형)
// O(n^2)
function solution(S, arr) {
  let answer = [...arr]
  let cache = new Array(S).fill(0)

  answer.forEach((el) =&amp;gt; {
    // Cache Miss
    if (!cache.includes(el)) {
      // 한 칸씩 뒤로 미룬 후 처음에 작업 삽입
      for (let i = cache.length - 2; i &amp;gt;= 0; i--) {
        cache[i + 1] = cache[i]
      }
      cache[0] = el
    }
    // Chache Hit
    else {
      let idx = cache.indexOf(el)
      for (let j = idx - 1; j &amp;gt;= 0; j--) {
        cache[j + 1] = cache[j]
      }
      cache[0] = el
    }
  })

  return cache
}

let S = 5
let arr = [1, 2, 3, 2, 6, 2, 3, 5, 7]
console.log(solution(S, arr))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 배열에 해당 요소가 포함되어 있을 경우 Cache Hit, 없을 경우 Cache Miss로 나눈다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. Cache Miss일 경우 배열의 요소를 한 칸씩 뒤로 미루고 제일 앞에 해당 요소 삽입&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. Cahche Hit일 경우 indexOf로 포함된 문자의 위치를 찾고, 해당 위치에서 제일 앞 요소까지 순회하면서 뒤로 한 칸씩 미룬 뒤, 제일 앞에 해당 요소 삽입&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;* 배열 전체를 순회 화면서 앞으로 미는 작업 대신에 배열 자르기와 shift 연산을 통해 조금 더 깔끔하게도 가능&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>인프런 LRU js</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/237</guid>
      <comments>https://ghost4551.tistory.com/237#entry237comment</comments>
      <pubDate>Fri, 25 Mar 2022 23:56:07 +0900</pubDate>
    </item>
    <item>
      <title>괄호 문자 제거(스택)</title>
      <link>https://ghost4551.tistory.com/236</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&amp;nbsp; 괄호 문자 제거 (스택)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&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;br /&gt;프로그램을&amp;nbsp;작성하세요.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Tip. 제목 그대로 스택 이용&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_1648142067558&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(s) {
  let answer = ''
  let stack = []
  let str = []

  for (let x of s) {
    stack.push(x)
  }

  for (let i = 0; i &amp;lt; s.length; i++) {
    let tmp = stack.pop()

    // 여는 괄호 나올 시
    if (tmp === '(') {
      // 저장된 str 배열에서 닫는 괄호를 만날 때 까지 요소 제거
      // 닫는 괄호 개수 만큼 반복문을 수행하기 때문에 시간 복잡도에 영향은 크게 미치지 않는다.
      while (str.pop() !== ')') {}
    } else {
      str.push(tmp)
    }
  }

  // 문제 풀이에서 끝에서 부터 반대로 제거 했기 때문에 문자열이 반대로 들어가 있다.
  // 따라서 배열 reverse 후 문자열로 합치기
  answer = str.reverse().join('')

  return answer
}

let s = '(A(BC)D)EF(G(H)(IJ)K)LM(N)' // EFLM
console.log(solution(s))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. 스택을 이용해 문자열 s를 stack[] 배열에 넣어준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2. stack 배열에서 하나씩 pop 하면서 str 배열에 넣어준다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;2-1. 만약 여는 괄호 &quot;(&quot;가 나왔다면 닫는 괄호까지 모두 삭제해야 하므로 str 배열에서 닫는 괄호를 만날 때까지 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;제거해 준다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;3. stack을 이용했기 때문에 반대로 들어가 있으므로 마지막에 배열의 요소를 뒤집어 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>괄호문자제거 js</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/236</guid>
      <comments>https://ghost4551.tistory.com/236#entry236comment</comments>
      <pubDate>Fri, 25 Mar 2022 02:18:43 +0900</pubDate>
    </item>
    <item>
      <title>모든 아나그램 찾기</title>
      <link>https://ghost4551.tistory.com/235</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&amp;nbsp; 모든 아나그램 찾기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt; : S문자열에서 T문자열과 아나그램이 되는 S의 부분문자열의 개수를 구하는 프로그램을 작성하세요. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&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;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&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_1648139513953&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(S, T) {
  let answer = 0

  let map1 = new Map()
  let map2 = new Map()

  // 초기 슬라이딩 윈도우 크기 세팅
  // 비교 대상 고정된 T의 문자열 길이 만큼 Map set
  for (let i = 0; i &amp;lt; T.length; i++) {
    if (map1.has(S[i])) {
      map1.set(S[i], map1.get(S[i]) + 1)
    } else {
      map1.set(S[i], 1)
    }
    if (map2.has(T[i])) {
      map2.set(T[i], map2.get(T[i]) + 1)
    } else {
      map2.set(T[i], 1)
    }
  }

  // 초기 윈도우 이후 오른쪽으로 이동하면서 아나그램 비교
  // 초기 비교에 비교를 수행하고
  // 문자를 삭제, 추가하므로 마지막 비교를 위해 S길이 + 1
  let lt = 0
  for (let rt = T.length; rt &amp;lt; S.length + 1; rt++) {
    let compare = true

    // 아나그램 비교
    for (let [key, val] of map2) {
      if (!map1.has(key) || val !== map1.get(key)) {
        compare = false
        break
      }
    }

    if (compare) answer += 1

    // 제일 왼쪽 문자 한 개 삭제
    map1.set(S[lt], map1.get(S[lt]) - 1)
    lt += 1

    // 제일 오른쪽 문자 추가
    if (map1.has(S[rt])) {
      map1.set(S[rt], map1.get(S[rt]) + 1)
    } else {
      map1.set(S[rt], 1)
    }
  }
  return answer
}

let S = 'bacaAacba'
let T = 'abc'
console.log(solution(S, T)) // 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. T의 길이만큼 T와 S 소요를 map 객체를 이용해 생성해 준다. (각각 S : map1,&amp;nbsp; T : map2라고 지칭)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 반복문을 통해 map1 객체의 key, value를 map2 객체와 비교를 통해 같은지 비교한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(전부 같으면 카운트)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 비교가 끝난 map1 객체에서&amp;nbsp; 제일 앞 요소의 값을 제거해 주고 다음 인덱스의 S 소요의 값을 넣어 준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. 2번, 3번을 반복&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>모든 아나그램 찾기 js</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/235</guid>
      <comments>https://ghost4551.tistory.com/235#entry235comment</comments>
      <pubDate>Fri, 25 Mar 2022 01:54:04 +0900</pubDate>
    </item>
    <item>
      <title>연속 부분수열 2</title>
      <link>https://ghost4551.tistory.com/234</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&lt;b&gt;연속 부분 수열 2 -&amp;gt; 투포인터 알고리즘&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;: 여러 개의 수로 이루어진 수열이 주어집니다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이&amp;nbsp;수열에서&amp;nbsp;연속부분수열의&amp;nbsp;합이&amp;nbsp;특정숫자&amp;nbsp;M이하가&amp;nbsp;되는&amp;nbsp;경우가&amp;nbsp;몇&amp;nbsp;번&amp;nbsp;있는지&amp;nbsp;구하는&amp;nbsp;프로그 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;램을 작성하세요.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 M=5이고 수열이 다음과 같다면 [1, 3, 1, 2, 3]&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;합이&amp;nbsp;5이하가&amp;nbsp;되는&amp;nbsp;연속부분수열은&amp;nbsp;{1},&amp;nbsp;{3},&amp;nbsp;{1},&amp;nbsp;{2},&amp;nbsp;{3},&amp;nbsp;{1,&amp;nbsp;3},&amp;nbsp;{3,&amp;nbsp;1},&amp;nbsp;{1,&amp;nbsp;2},&amp;nbsp;{2,&amp;nbsp;3}, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;{1,&amp;nbsp;3,&amp;nbsp;1}로&amp;nbsp;총&amp;nbsp;10가지입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;input : M = 5 , arr =&amp;nbsp;[1, 3, 1, 2, 3] =&amp;gt; output : 10&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;input : M = 6 , arr =&amp;nbsp;[1,&amp;nbsp;1,&amp;nbsp;1, 1, 1] =&amp;gt; output : 15&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt; Tip. 투포인터 알고리즘으로 이중 for 문으로 전체 탐색을 하면 O(n^2)이 되기 때문에&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;투포인터 알고리즘을 이용하여 O(n)으로 해결해야 한다.&lt;/span&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 style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;소스 코드&amp;nbsp; ⏰시간복잡도 O(n)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1647365275175&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(arr, m) {
  let result = 0,
    sum = 0,
    rt = 0

  for (let lt = 0; lt &amp;lt; arr.length; lt++) {
    rt = lt
    sum = arr[rt++]

    while (sum &amp;lt;= m &amp;amp;&amp;amp; rt &amp;lt;= arr.length) {
      result += 1
      sum += arr[rt++]
    }
  }
  return result
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;for 문안에 있는 while 문의 총 반복 횟수가 n을 넘지 않기 때문에 시간 복잡도는 O(n)이다.&lt;/span&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 style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a href=&quot;https://ghost4551.tistory.com/233&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;연속 부분 수열1 문제&lt;/a&gt;와 유사하다. 다만 여기서는 연속된 배열에서 특정 숫자 M 이하의 경우를 구해야 하므로 로직은 다음과 같이 구성할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 반복문을 통해 배열 처음부터 끝까지 순회한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 시작 시점을 sum으로 초기화 후 while을 통해 rt 변수를 오른쪽으로 옮겨가면서 M보다 작을 때까지&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;계속 더해나가고 개수(result)도 카운트해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 만약 sum이 M보다 클 경우 연속된 배열에서 M 이하인 경우의 수가 끝났으므로 다음 반복문을 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>연속 부분수열2 js</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <category>투포인터알고리즘 js</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/234</guid>
      <comments>https://ghost4551.tistory.com/234#entry234comment</comments>
      <pubDate>Wed, 16 Mar 2022 02:37:44 +0900</pubDate>
    </item>
    <item>
      <title>연속 부분수열 1</title>
      <link>https://ghost4551.tistory.com/233</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;b&gt;연속 부분 수열 1 -&amp;gt; 투포인터 알고리즘&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;문제 설명&lt;/b&gt;&amp;nbsp;: 여러 개의 수로 이루어진 수열이 주어집니다. &lt;br /&gt;이 수열에서 연속 부분 수열의 합이 특정 숫자 M이 되는 경우가 몇 번 있는지 구하는 프로그램을 &lt;br /&gt;작성하세요. &lt;br /&gt;만약 M=6이고 수열이 다음과 같다면 [1, 2, 1, 3, 1, 1, 1, 2]&lt;br /&gt;합이 6이 되는 연속 부분 수열은 {2, 1, 3}, {1, 3, 1, 1}, {3, 1, 1, 1}로 총 3가지입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;input : M = 6 , arr = &lt;span style=&quot;background-color: #ffffff;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;1, 2, 1, 3, 1, 1, 1, 2] =&amp;gt; output : 3&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;input : M = 6 , arr =&amp;nbsp;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px; background-color: #ffffff; font-size: 1.12em;&quot;&gt;] =&amp;gt; output : 1&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt; Tip. 투포인터 알고리즘으로 이중 for 문으로 전체 탐색을 하면 O(n^2)이 되기 때문에&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;투포인터 알고리즘을 이용하여 O(n)으로 해결해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;nbsp;소스 코드&amp;nbsp; ⏰시간복잡도 O(n)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1647335118117&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(arr, m) {
  let result = 0,
    lt = 0,
    sum = 0

  for (let rt = 0; rt &amp;lt; arr.length; rt++) {
    sum += arr[rt]
    if (sum === m) result += 1

    while (sum &amp;gt;= m) {
      sum -= arr[lt++]
      if (sum === m) result += 1
    }
  }
  return result
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;for 문안에 있는 while 문의 총 반복 횟수가 n을 넘지 않기 때문에 시간 복잡도는 O(n)이다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; &amp;zwj; &amp;nbsp;코드 설&lt;/b&gt;&lt;b&gt;명&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot;연속된 ~~를 구해라&quot;&amp;nbsp; 문제에서 &quot;&lt;b&gt;연속된&lt;/b&gt;&quot; 키워드가 있고 흔히 생각했을 때,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이중 반복문으로 해결할 수 있는 경우는 투포인트 알고리즘이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 배열의 위치를 기억하는 두 개의 변수를 이용한다. (left, right)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;처음부터 배열 끝까지 반복을 수행하는 right 변수, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;누적 합이 주어진 M보다 클 때, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배열의 제일 앞 부분에 있는 요소를 제거하면서 M보다 작게 만들어야 하므로 이를 기억하는 left 변수를 이용한다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 반복문을 통해 배열의 요소의 누적 합을 구한다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 만약 누적 합이 M과 같다면 개수를 카운트해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. 만약 누적 합이 M 이상이라면 3번에서 카운트를 해줬기 때문에 연속된 배열의 위치를 수정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;4-1. left 변수를 옮기면서 누적 합이 M보다 작을 때까지 배열의 left가 가리키고 있는 요소를 빼준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;4-2. &lt;span style=&quot;color: #000000;&quot;&gt;만약 누적 합이 M과 같다면 개수를 카운트해준다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, 배열에서 왼쪽, 오른쪽 두 개의 변수를 이용하여 연속된 길이에서의 누적합을 통해 M과 같을 때를 구해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;잘못된 설명, 코드, 예외 케이스가 있다면 댓글 남겨주시면 수정하겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>알고리즘_JS/인프런 JS알고리즘</category>
      <category>연속 부분수열 js</category>
      <category>인프런 자바스크립트 알고리즘 문제풀이</category>
      <category>투포인터알고리즘 js</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/233</guid>
      <comments>https://ghost4551.tistory.com/233#entry233comment</comments>
      <pubDate>Tue, 15 Mar 2022 18:20:59 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript DeepDive] 49장_Babel과 Webpack</title>
      <link>https://ghost4551.tistory.com/232</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;✍ Babel과 Webpack을 이용한 ES6+/ES.NEXT 개발 환경 구축&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;크롬, 사파리, 파이어폭스, 엣지 같은 에버그린 브라우저의 ES6 지원율은 약 98%로 거의 대부분&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ES6 사양을 지원한다. 하지만 IE 11의 지원율은 11%다. 그리고 매년 새롭게 도입되는 버전은&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;브라우저에 따라 지원율이 제각각이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 최신 ECMAScript 사양을 사용하여 프로젝트를 진행하려면 IE를 포함한 구형 브라우저에서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제 없이 동작시키기 위한 개발 환경 구축이 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;✍ Babel&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Babel은 자바스크립트 컴파일러이다. &lt;span style=&quot;background-color: #ffffff;&quot;&gt;Babel은 현재 및 이전 브라우저 또는 환경에서 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;ECMAScript 2015+&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;(이상)의 코드를 이전 버전의 JavaScript로 변환하는 데 주로 사용되는 도구이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다음 예제에서는 ES6 화살표 함수와 ES7의 지수 연산자를 사용하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;[1, 2, 3].map(n =&amp;gt; n ** n);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;IE 같은 구형 브라우저에서는 ES6의 화살표 함수와 ES7의 지수 연산자를 지원하지 않을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Babel을 사용하면 위 코드를 ES5 사양으로 변환할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;&quot;use strict&quot;;

[1, 2, 3].map(function (n) {
  return Math.pow(n, n);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이처럼 Babel은 최신 사양의 소스코드를 IE 같은 구형 브라우저에서도 동작하는 ES5 사양의 소스코드로 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;변환(트랜스파일링)할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;✍ Webpack&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;webpack은&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;모던 JavaScript 애플리케이션을 위한&amp;nbsp;&lt;/span&gt;정적 모듈 번들러&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;입니다. webpack이 애플리케이션을 처리할 때, 내부적으로는 프로젝트에 필요한 모든 모듈을 매핑하고 하나 이상의&amp;nbsp;&lt;/span&gt;번들을&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;생성하는&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://webpack.kr/concepts/dependency-graph/&quot;&gt;디펜던시 그래프&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;를 만듭니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Webpack은 의존 관계에 있는 자바스크립트, CSS, 이미지 등의 리소스들을 하나(또는 여러 개)의 파일로&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;번들링하는 모듈 번들러다. Webpack을 사용하면 의존 모듈이 하나의 파일로 번들링되므로 별도의 모듈&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;로더가 필요 없다. 그리고 여러 개의 자바스크립트 파일을 하나로 번들링하므로 HTML 파일에서 script 태그로 여러 개의 자바스크립트 파일을 로드해야 하는 번거로움도 사라진다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt; &amp;zwj;  이번 장에서는 Babel, Webpack에 대한 간략한 소개와 환경 설정법을 다뤘다. 환경 설정하는 법은 정리하지 않았지만, 읽는 과정에서 많은 npm 설치와 파일 설정이 까다롭다는 것을 알 수 있었다. 지금까지는 간단한 설치 명령어를 통해 Babel, Webpack을 직접 커스터마이징 한 적은 없지만, 실무에서는 직접 서비스에 맞게 적용할 줄 알아야 될 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;+ 49장 책 페이로 따지면 대략 950장 정도 되는 내용을 언제 읽나 싶었지만, 두 달가량의 기간 동안 한 번 정독과 정리를 끝낼 수 있었다. 생각보다 긴 기간 동안 한 번 정독을 했지만, 많이 유익했고 좋았다. 앞으로 정리한 개념을 바탕으로 다시 한번 복습해야겠다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>모던 자바스크립트 Deep Dive</category>
      <category>모던 javascript Deep Dive</category>
      <category>모던 자바스크립트 Deep Dive TIL</category>
      <category>모던 자바스크립트 딥다이브</category>
      <category>모던 자바스크립트 딥다이브 49장 babel webpack</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/232</guid>
      <comments>https://ghost4551.tistory.com/232#entry232comment</comments>
      <pubDate>Mon, 14 Mar 2022 00:01:11 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript DeepDive] 48장_모듈</title>
      <link>https://ghost4551.tistory.com/231</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모듈이란 애플리케이션을 구성하는 개별적 요소로서 재사용 가능한 코드 조각을 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일반적으로 모듈은 기능을 기준으로 파일 단위로 분리한다. 모듈이 성립하려면 모듈은 자신만의&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;파일 스코프(모듈 스코프)를 가질 수 있어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모듈은(변수, 함수, 객체 등)은 기본적으로 비공개 상태다. 즉, 자신만의 파일 스코프를 갖는 모듈의 자산은&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;캡슐화되어 다른 모듈에서 접근할 수 없다. 즉, 모듈은 개별적 존재로서 애플리케이션과 분리되어 존재한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 완전히 분리되어 개별적으로 존재하면 재사용이 불가능하므로 &lt;b&gt;모듈은 공개가 필요한 곳에 한정하여 명시적으로 공개가 가능하다. 이를 export라 한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;공개된 모듈의 자산은 다른 모듈에서 재사용할 수 있다. 공개된 모듈을 이용하는 모듈을 모듈 사용자라 한다. &lt;b&gt;모듈 사용자는 모듈이 공개한 일부 또는 전체를 성택해 자신의 스코프 내로 불러들여 재사용할 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이를 import라 한다.&lt;/b&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;42_1.png&quot; data-origin-width=&quot;2562&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CbHrn/btrvKqazfuL/rvITLM6YgbD9NhfGpclgi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CbHrn/btrvKqazfuL/rvITLM6YgbD9NhfGpclgi0/img.png&quot; data-alt=&quot;모듈의 import와 export&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CbHrn/btrvKqazfuL/rvITLM6YgbD9NhfGpclgi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCbHrn%2FbtrvKqazfuL%2FrvITLM6YgbD9NhfGpclgi0%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;616&quot; height=&quot;167&quot; data-filename=&quot;42_1.png&quot; data-origin-width=&quot;2562&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모듈의 import와 export&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이처럼 모듈은 애플리케이션과 분리되어 개별적으로 존재하다가 필요에 따라 다른 모듈에 의해 재사용된다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;✍ 자바스크립트와 모듈&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바스크립트는 웹페이지의 단순한 보조 기능을 처리하기 위한 제한적인 용도를 목적으로 태어났다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 모듈 시스템을 지원하지 않는다. 즉, 자바스크립트는 모듈이 성립하기 위해 필요한 파일 스코프와&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;import, export를 지원하지 않았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;script 택를 사용하여 외부의 자바스크립트 파일을 로드할 수 있지만 파일마다 독립적인 파일 스코프를 갖지 않는다. 다시 말해, 여러 개의 파일로 분리하여 script 태그로 로드해도 결국 하나의 자바스크립트 파일 내에 있는 것처럼 동작한다. 즉, 모든 자바스크립트 파일은 하나의 전역을 공유한다. 따라서 전역 변수가 중복되는 등의 문제가 발생하여 모듈을 구현할 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 문제를 해결하기 위해 자바스크립트 런타임 환경인 Node.js는 모듈 시스템을 구현한 CommonJS을 표준으로 채택하여 독자전인 변화를 거쳐 사용하고 있다. 즉, Node.js는 ECMAScript 표준 사양은 아니지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모듈 시스템을 지원한다.따라서 Node.js 환경에서는 파일별로 독립적인 파일 스코프를 갖는다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;✍ ES6 모듈(ESM)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 상황에서 ES6에서는 클라이언트 사이드 자바스크립트에서도 동작하는 모듈 기능을 추가했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;scrpit 태그에 type=&quot;module&quot; 어트리뷰트를 추가하면 로드된 자바스크립트 파일은 모듈로서 동작한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ES6 모듈(앞으로 ESM으로 지칭) 모듈임을 나타내기 위해 ESM의 파일 확장자는 mjs를 권장한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;script type=&quot;module&quot; src=&quot;app.mjs&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ESM에는 기본적으로 strict mode가 적용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  모듈 스코프&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ESM은 독자적인 모듈 스코프를 갖는다. 일반적인 자바스크립트 파일은 script 태그로 분리해도 독자적인&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모듈 스코프를 갖지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;script src=&quot;foo.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script src=&quot;bar.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;// foo.js
// x 변수는 전역 변수다.
var x = 'foo';
console.log(window.x); // foo&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;div&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;// bar.js
// x 변수는 전역 변수다. foo.js에서 선언한 전역 변수 x와 중복된 선언이다.
var x = 'bar';

// foo.js에서 선언한 전역 변수 x의 값이 재할당되었다.
console.log(window.x); // bar&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;HTML에서 script 태그로 분리된 2개의 자바스립트 파일은 하나의 파일 내에 있는 것처럼 동작한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, 하나의 전역을 공유한다. 따라서 x의 변수값이 의도치 않게 덮어써진다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ESM은 파일 자체의 독자적인 모듈 스코프를 제공한다. 따라서 모듈 내에서 선언한 변수는&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;전역 변수가 아니며 window 객체의 프로퍼티도 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;script type=&quot;module&quot; src=&quot;foo.mjs&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script type=&quot;module&quot; src=&quot;bar.mjs&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;// foo.mjs
// x 변수는 전역 변수가 아니며 window 객체의 프로퍼티도 아니다.
var x = 'foo';
console.log(x); // foo
console.log(window.x); // undefined&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;// bar.mjs
// x 변수는 전역 변수가 아니며 window 객체의 프로퍼티도 아니다.
// foo.mjs에서 선언한 x 변수와 스코프가 다른 변수다.
var x = 'bar';
console.log(x); // bar
console.log(window.x); // undefined&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  export 키워드&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모듈은 독자적인 모듈 스코프를 갖는다. 따라서 모듈 내부에서 선언한 모든 식별자는 기본적으로 해당 모듈 내부에서만 참조할 수 있다. 이를 외부에 공개하여 다른 모듈들이 재사용할 수 있게 하려면 export 키워드를 사용한다. export 키워드는 선언문 앞에 사용 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// lib.mjs
// 변수의 공개
export const pi = Math.PI;

// 함수의 공개
export function square(x) {
  return x * x;
}

// 클래스의 공개
export class Person {
  constructor(name) {
    this.name = name;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;export할 대상을 하나의 객체로 구성하여 한 번에 export 가능&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// lib.mjs
const pi = Math.PI;

function square(x) {
  return x * x;
}

class Person {
  constructor(name) {
    this.name = name;
  }
}

// 변수, 함수 클래스를 하나의 객체로 구성하여 공개
export { pi, square, Person };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  import 키워드&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다른 모듈에서 공개한 식별자를 자신의 모듈 스코프 내부로 로드하려면 import 키워드를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// app.mjs
// 같은 폴더 내의 lib.mjs 모듈이 export한 식별자 이름으로 import한다.
// ESM의 경우 파일 확장자를 생략할 수 없다.
import { pi, square, Person } from './lib.mjs';

console.log(pi);         // 3.141592653589793
console.log(square(10)); // 100
console.log(new Person('Lee')); // Person { name: 'Lee' }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;div&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;script type=&quot;module&quot; src=&quot;app.mjs&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;export한 모듈을 import로 가져와서 사용할 때, 다른 식별자 이름을 이용할려면 as 키워드를 통해 원하는 명칭으로 바꿔 사용할 수도 있고, 파일 자체를 export 하여 import하여 하위 컴포넌트 처럼 구성할 수도 있다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt; &amp;zwj;  모듈의 개념과 export, import에 대한 개념을 다루어보았다. 비교적 가벼운 주제였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;15장에서 다룬 var, let, const 내용과 23장 실행 컨텍스트에서 다룬 내용에서 var는 전역 프로퍼티로 사용 가능하지만, let과 const는 개념적인 블록(선언적 환경 레코드)로 인해 전역으로 사용 불가능하기 때문에 독자적인 스코프를 가진 다라는 내용만 이해하면 될 것 같다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>모던 자바스크립트 Deep Dive</category>
      <category>모던 javascript Deep Dive</category>
      <category>모던 자바스크립트 Deep Dive TIL</category>
      <category>모던 자바스크립트 딥다이브</category>
      <category>모던 자바스크립트 딥다이브 48장 모듈</category>
      <author>[리우]</author>
      <guid isPermaLink="true">https://ghost4551.tistory.com/231</guid>
      <comments>https://ghost4551.tistory.com/231#entry231comment</comments>
      <pubDate>Sun, 13 Mar 2022 17:31:01 +0900</pubDate>
    </item>
  </channel>
</rss>