<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>haranglog</title>
    <link>https://haranglog.tistory.com/</link>
    <description>  나의 개발 이야기
 e-mail : dbd02169@naver.com
 GitHub: https://github.com/saseungmin</description>
    <language>ko</language>
    <pubDate>Fri, 21 Aug 2026 19:36:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>seungmin2</managingEditor>
    <image>
      <title>haranglog</title>
      <url>https://tistory1.daumcdn.net/tistory/3995045/attach/071cb3bebe974571ba568a150042efcb</url>
      <link>https://haranglog.tistory.com</link>
    </image>
    <item>
      <title>[완강] 미니학습지 일본어 학습지 8단계</title>
      <link>https://haranglog.tistory.com/45</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;ldquo;미니학습지 환급원정대 미션 완수를 통해 소정의 수강료 환급을 받았습니다.&amp;rdquo;&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;a href=&quot;https://bit.ly/studymini&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미니학습지&lt;/a&gt; 환급원정대의 마지막 단계 8단계까지 마무리되었다.&lt;br /&gt;처음 시작했던게 올해 1월달인데... 벌써 1년이라는 시간이 금방 지나가버렸다.&lt;br /&gt;그 사이에 개인적인 여러 일들이 있었지만, 미니학습지 공부는 8단계가 끝나기 오늘까지도 나름 꾸준하게 열심히해왔다.&lt;br /&gt;그래도 1년간의 기간동안 완강을 해서 개인적으로도 너무 뿌듯하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7889 2.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kZjKP/btsLAzQegDk/ok5u2nWjxjmAHChxPK4Pk1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kZjKP/btsLAzQegDk/ok5u2nWjxjmAHChxPK4Pk1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kZjKP/btsLAzQegDk/ok5u2nWjxjmAHChxPK4Pk1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkZjKP%2FbtsLAzQegDk%2Fok5u2nWjxjmAHChxPK4Pk1%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;424&quot; height=&quot;565&quot; data-filename=&quot;IMG_7889 2.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;마지막 단계인 8단계에서도 배운 주요 내용들을 정리해보고 가자&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;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;〜うちに - ~하는 동안 / &lt;/span&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;/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;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;〜わけだ/〜というわけだ - ~인 것이다(납득, 결과)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;〜に決まっている - ~임에 틀림없다, 당연히 ~하다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;〜がきっかけで - ~가 계기가 되어&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;〜わけがない/〜はずがない - ~일리가 없다&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 많지만,, 어려웠던 위주로 정리해보았다.&lt;br /&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/bNXPZo/btsLBLCn9vQ/8l7leoO04S8cyY6Af6I9Hk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNXPZo/btsLBLCn9vQ/8l7leoO04S8cyY6Af6I9Hk/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;61F66D6B-B320-41FC-868A-13B7EB9C5D50.jpeg&quot; style=&quot;width: 62.5007%; margin-right: 10px;&quot; data-widthpercent=&quot;63.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNXPZo/btsLBLCn9vQ/8l7leoO04S8cyY6Af6I9Hk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNXPZo%2FbtsLBLCn9vQ%2F8l7leoO04S8cyY6Af6I9Hk%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/OXE2K/btsLCrDokro/KK4SOpCq79JRkKXo1Edr31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OXE2K/btsLCrDokro/KK4SOpCq79JRkKXo1Edr31/img.png&quot; data-origin-width=&quot;1586&quot; data-origin-height=&quot;2046&quot; data-is-animation=&quot;false&quot; style=&quot;width: 36.3365%;&quot; data-widthpercent=&quot;36.76&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OXE2K/btsLCrDokro/KK4SOpCq79JRkKXo1Edr31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOXE2K%2FbtsLCrDokro%2FKK4SOpCq79JRkKXo1Edr31%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;1586&quot; height=&quot;2046&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8단가를 마무리로 1단계 때와 내가 많이 발전했을까를 생각해본다면, 꽤나 많이 달라진 것 같다.&lt;br /&gt;이제는 그래도 말하는건 아직은 어렵지만, 어느정도 들린다. 그냥 가볍게 일본에 자주 가니 한번 배워볼까?로 시작했던 환급원정대 과정을 1년간 무사히 마친것 같다.&lt;br /&gt;공부를 뭔가 틈내서 하기 어려운 과정에서 공부를 틈틈히 할 수 있게 도와준 장치였던 것 같다. 역시 뭘해도 본인 하기 나름인 것 같다는 걸 다시금 알게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 미니학습지 환급원정대를 할꺼야?한다면 다시 할 것 같다.&lt;br /&gt;솔직히 귀찮고.. 하기 싫을때도 많았지만, 이 장치가 없었다면 했을까?라는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8단계까지 마무리했고 앞으로 여기서 배웠던 것들을 가지고, 개인적인 공부를 더 해봐야겠다.(부족해 부족해..)&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;figure id=&quot;og_1735481862758&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;미니학습지 &amp;ndash; 즐거운 외국어 생활의 시작&quot; data-og-description=&quot;&quot; data-og-host=&quot;studymini.com&quot; data-og-source-url=&quot;https://bit.ly/studymini&quot; data-og-url=&quot;https://studymini.com/?utm_source=zero&amp;amp;utm_medium=sns&amp;amp;utm_campaign=202212&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c7McTQ/hyXWspnRwi/ck4p120adiGpWTJ2dkwga0/img.jpg?width=400&amp;amp;height=229&amp;amp;face=0_0_400_229,https://scrap.kakaocdn.net/dn/dHXGe7/hyXSpOCE0N/NdbrinpOi0eMiTvXgiwd4k/img.png?width=1536&amp;amp;height=690&amp;amp;face=0_0_1536_690,https://scrap.kakaocdn.net/dn/bcJPPc/hyXSpnxPeY/E8ePxGjWtQ9Dy7aX1kgSi1/img.png?width=900&amp;amp;height=945&amp;amp;face=0_0_900_945&quot;&gt;&lt;a href=&quot;https://bit.ly/studymini&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bit.ly/studymini&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c7McTQ/hyXWspnRwi/ck4p120adiGpWTJ2dkwga0/img.jpg?width=400&amp;amp;height=229&amp;amp;face=0_0_400_229,https://scrap.kakaocdn.net/dn/dHXGe7/hyXSpOCE0N/NdbrinpOi0eMiTvXgiwd4k/img.png?width=1536&amp;amp;height=690&amp;amp;face=0_0_1536_690,https://scrap.kakaocdn.net/dn/bcJPPc/hyXSpnxPeY/E8ePxGjWtQ9Dy7aX1kgSi1/img.png?width=900&amp;amp;height=945&amp;amp;face=0_0_900_945');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;미니학습지 &amp;ndash; 즐거운 외국어 생활의 시작&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;studymini.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;div style=&quot;color: #333333; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0593d3; text-align: left;&quot;&gt;#미니학습지 #스터디미니 #일본어인강&lt;span&gt;&amp;nbsp;&lt;/span&gt;#성인학습지&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>일본어</category>
      <category>미니학습지</category>
      <category>성인학습지</category>
      <category>스터디미니</category>
      <category>일본어인강</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/45</guid>
      <comments>https://haranglog.tistory.com/45#entry45comment</comments>
      <pubDate>Sun, 29 Dec 2024 23:37:23 +0900</pubDate>
    </item>
    <item>
      <title>[단계 완료] 미니학습지 일본어 학습지 7단계</title>
      <link>https://haranglog.tistory.com/44</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;드디어!! &lt;/span&gt;&lt;a href=&quot;https://bit.ly/studymini&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미니학습지&lt;/a&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;7&lt;/span&gt;단계가 완료되었다..!&lt;br /&gt;이번 단계 느낀점은 매.우.어.려.움... 이제 점점 난이도가 감당하기 힘든 난이도를 만난 느낌이다...&lt;br /&gt;저번 단계 완료 후기글에서는 뭐 이제는 크게 &quot;어려움이 거기서 거기지~&quot;라고 했던 내 자신한테 바보같은 생각이었단 걸 느끼게 해줬다..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7744.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvzk17/btsKM16YZZZ/bdUj4gZOxAKbmDKtiUy4Fk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvzk17/btsKM16YZZZ/bdUj4gZOxAKbmDKtiUy4Fk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvzk17/btsKM16YZZZ/bdUj4gZOxAKbmDKtiUy4Fk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvzk17%2FbtsKM16YZZZ%2FbdUj4gZOxAKbmDKtiUy4Fk%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;520&quot; height=&quot;693&quot; data-filename=&quot;IMG_7744.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 7단계에서 주요 배운 내용들을 정리해보고가자.&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;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;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;li&gt;お + ます형 + ください - ~하세요&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&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/bPEQdb/btsKL8lI691/20qJmyQTvxxYVRFRdRC7cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPEQdb/btsKL8lI691/20qJmyQTvxxYVRFRdRC7cK/img.png&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot; data-is-animation=&quot;false&quot; data-filename=&quot;edited_IMG_7746.jpg&quot; style=&quot;width: 63.1622%; margin-right: 10px;&quot; data-widthpercent=&quot;63.91&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPEQdb/btsKL8lI691/20qJmyQTvxxYVRFRdRC7cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPEQdb%2FbtsKL8lI691%2F20qJmyQTvxxYVRFRdRC7cK%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;3000&quot; height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPMhwd/btsKNxqOOEZ/WFTru1iwhKWIkRllGMG330/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPMhwd/btsKNxqOOEZ/WFTru1iwhKWIkRllGMG330/img.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;1620&quot; data-is-animation=&quot;false&quot; style=&quot;width: 35.675%;&quot; data-widthpercent=&quot;36.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPMhwd/btsKNxqOOEZ/WFTru1iwhKWIkRllGMG330/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPMhwd%2FbtsKNxqOOEZ%2FWFTru1iwhKWIkRllGMG330%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;1220&quot; height=&quot;1620&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&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;일본어를 공부하면서 어려웠던 점이 이런 부분이였던 것 같다. 한국에는 별 표현이 없는데 일본에서는 나눠지고 강조되는 부분이랄까?&lt;br /&gt;특히 겸양어 표현은 특별한 겸양어 표현도 있어서 많이 어려웠던 것 같다. 그냥 외워야하다니... 어렵다 어려워.&lt;br /&gt;다다음주에 후쿠오카를 가게되었는데 써먹어볼수 있을까? 어쨌든 공부좀 더 해봐야겠다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;7단계를 마무리하면서 이제는 정말 미니학습지 마지막 단계인 8단계가 남았다!&lt;br /&gt;마지막까지 환급원정대 화이팅..!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://bit.ly/studymini&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://bit.ly/studymini&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0593d3; text-align: left;&quot;&gt;#미니학습지 #스터디미니 #일본어인강 #성인학습지&lt;/span&gt;&lt;/p&gt;</description>
      <category>일본어</category>
      <category>미니학습지</category>
      <category>성인학습지</category>
      <category>스터디미니</category>
      <category>오블완</category>
      <category>일본어인강</category>
      <category>티스토리챌린지</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/44</guid>
      <comments>https://haranglog.tistory.com/44#entry44comment</comments>
      <pubDate>Sun, 17 Nov 2024 23:21:15 +0900</pubDate>
    </item>
    <item>
      <title>[단계 완료] 미니학습지 일본어 학습지 6단계</title>
      <link>https://haranglog.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;벌써 &lt;a href=&quot;https://bit.ly/studymini&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미니학습지&lt;/a&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; 6&lt;/span&gt;단계가 완료되었다..! 6단계에 접어드면서 점점 난이도가 높아지고 있다는것을 느끼고있다.&lt;br /&gt;그치만 이제는 단계와 상관없이 상황과 위치에 따라서 문법과 문장이 다른 느낌이라 어느순간부터는 별 차이가 없어진 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7571.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XAusF/btsJV27vjlx/FPeL6eOHMywdgh7mkQOzJK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XAusF/btsJV27vjlx/FPeL6eOHMywdgh7mkQOzJK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XAusF/btsJV27vjlx/FPeL6eOHMywdgh7mkQOzJK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXAusF%2FbtsJV27vjlx%2FFPeL6eOHMywdgh7mkQOzJK%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;548&quot; height=&quot;731&quot; data-filename=&quot;IMG_7571.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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: #333333; text-align: start;&quot;&gt;이번 6단계에서 주요 배운 내용들을 정리해보고가자.&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;/li&gt;
&lt;li&gt;〜らしい - a ~인 것 같다, b ~답다 (명사 뒤에 붙는 내용)&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;/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;/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;이정도 였던 것 같다.&lt;br /&gt;이전 5단계에서도 느낀거지만 이렇게 많은 것들을 뭔가 한번에 주입해서 사용하는 것보단... 문장을 이해하고 사용하는게 좋은 방향이지 않을까 싶다.&lt;span style=&quot;color: #333333; text-align: start;&quot;&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/cm0SqY/btsJV8NnWrc/quwV3NkA5mtSGBCVNqyyB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cm0SqY/btsJV8NnWrc/quwV3NkA5mtSGBCVNqyyB0/img.png&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot; data-is-animation=&quot;false&quot; data-filename=&quot;edited_IMG_7572.JPG&quot; style=&quot;width: 67.9684%; margin-right: 10px;&quot; data-widthpercent=&quot;68.77&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cm0SqY/btsJV8NnWrc/quwV3NkA5mtSGBCVNqyyB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcm0SqY%2FbtsJV8NnWrc%2FquwV3NkA5mtSGBCVNqyyB0%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;3000&quot; height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJYxQd/btsJWOU1jLn/InYz7sbA1KRDI5j93enp00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJYxQd/btsJWOU1jLn/InYz7sbA1KRDI5j93enp00/img.png&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;2378&quot; data-is-animation=&quot;false&quot; width=&quot;249&quot; height=&quot;411&quot; style=&quot;width: 30.8688%;&quot; data-widthpercent=&quot;31.23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJYxQd/btsJWOU1jLn/InYz7sbA1KRDI5j93enp00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJYxQd%2FbtsJWOU1jLn%2FInYz7sbA1KRDI5j93enp00%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;1440&quot; height=&quot;2378&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 어려웠던 것으로는 ことにする, ことになる 이 두차이였는데 전자는 내가 결정한 사항을 얘기할 때 사용하고, 후자는 다른사람이 결정한 걸 하기로 되었다의 차이로 이 두가지가 헷갈렸던 포인트였다. 즉, &quot;하기로 하다&quot;, &quot;하기로 되다&quot;이런 의미로 달랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6단계는 이정도로 정리하면될 것 같고, 앞으로 남은 7, 8단계 화이팅하면서...&lt;br /&gt;겨울에 또 오랜만에 삿포로를 가려고 예약을 해두었는데 가서 조금 더 나은 내가 된걸 보고 싶다. 그전까지 좀 더 화이팅...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1728223881539&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;미니학습지 &amp;ndash; 즐거운 외국어 생활의 시작&quot; data-og-description=&quot;&quot; data-og-host=&quot;studymini.com&quot; data-og-source-url=&quot;https://bit.ly/studymini&quot; data-og-url=&quot;https://studymini.com/?utm_source=zero&amp;amp;utm_medium=sns&amp;amp;utm_campaign=202212&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Oe2uh/hyXazkILVr/Be1dPkDa6gW69pa4HFegDK/img.jpg?width=400&amp;amp;height=229&amp;amp;face=0_0_400_229,https://scrap.kakaocdn.net/dn/dS6643/hyXaIILDZh/cbCBmnpbThUZ60Kp0JuwE1/img.png?width=1536&amp;amp;height=690&amp;amp;face=0_0_1536_690,https://scrap.kakaocdn.net/dn/0S4nM/hyXd4wB1GK/jMdkMVt66uZQSnZzq0wUQk/img.png?width=600&amp;amp;height=588&amp;amp;face=0_0_600_588&quot;&gt;&lt;a href=&quot;https://bit.ly/studymini&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bit.ly/studymini&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Oe2uh/hyXazkILVr/Be1dPkDa6gW69pa4HFegDK/img.jpg?width=400&amp;amp;height=229&amp;amp;face=0_0_400_229,https://scrap.kakaocdn.net/dn/dS6643/hyXaIILDZh/cbCBmnpbThUZ60Kp0JuwE1/img.png?width=1536&amp;amp;height=690&amp;amp;face=0_0_1536_690,https://scrap.kakaocdn.net/dn/0S4nM/hyXd4wB1GK/jMdkMVt66uZQSnZzq0wUQk/img.png?width=600&amp;amp;height=588&amp;amp;face=0_0_600_588');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;미니학습지 &amp;ndash; 즐거운 외국어 생활의 시작&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;studymini.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;&lt;span style=&quot;color: #0593d3; text-align: left;&quot;&gt;#미니학습지 #스터디미니 #일본어인강 #성인학습지&lt;/span&gt;&lt;/p&gt;</description>
      <category>일본어</category>
      <category>미니학습지</category>
      <category>성인학습지</category>
      <category>스터디미니</category>
      <category>일본어인강</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/43</guid>
      <comments>https://haranglog.tistory.com/43#entry43comment</comments>
      <pubDate>Sun, 6 Oct 2024 23:12:05 +0900</pubDate>
    </item>
    <item>
      <title>[단계 완료] 미니학습지 일본어 학습지 5단계</title>
      <link>https://haranglog.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://bit.ly/studymini&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미니학습지&lt;/a&gt; 5단계가 완료되었다. 환급원정대를 하고 있는데, 벌써 3단계만 남았다. 시작한지 1년이 다되어간다는 소리...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7440.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mkiDP/btsJgdtX7Yu/TMVAwdG7nFXeOf3eYydfy0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mkiDP/btsJgdtX7Yu/TMVAwdG7nFXeOf3eYydfy0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mkiDP/btsJgdtX7Yu/TMVAwdG7nFXeOf3eYydfy0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmkiDP%2FbtsJgdtX7Yu%2FTMVAwdG7nFXeOf3eYydfy0%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;444&quot; height=&quot;592&quot; data-filename=&quot;IMG_7440.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점점 어려워지고 문장의 단어와 문법이 어려워지고 있는 것 같다. 5단계에서는 전 단계들과 다르게 한국말로도 표현하기 힘든 즉, 표현이 함축되어있는 문법을 가진 문장도 있어서 확실히 어려워졌다고 느꼈다.&lt;br /&gt;한가지의 예로 사역수동형이 있었는데, 그게 뭐임? 부터 나와버렸...다. 어쨌든 하기는 싫지만 어떤 것에 의해 하는 상황을 나타내는 문장을 사역수동형이라고 한단다. 痩せすぎたから、運動をやめさせられました。( 너무 말랐기 때문에, 운동을 (어쩔 수 없이) 그만두었어요.) 요런 문장이 있을때, 어쩔 수 없이가 られました。로 표현하고 있는 것이다. 이처럼 이런 문법적인 요인들이 많이 포함되어있던 5단계였다.&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/RliOa/btsJfTvJHpf/rAzO98VYEE3tDS1MUJMAD1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RliOa/btsJfTvJHpf/rAzO98VYEE3tDS1MUJMAD1/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_7441.jpg&quot; width=&quot;487&quot; height=&quot;365&quot; style=&quot;width: 59.9316%; margin-right: 10px;&quot; data-widthpercent=&quot;60.64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RliOa/btsJfTvJHpf/rAzO98VYEE3tDS1MUJMAD1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRliOa%2FbtsJfTvJHpf%2FrAzO98VYEE3tDS1MUJMAD1%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/LjWZi/btsJequJ3sY/uO1fkdet5mYndbiCGM75q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LjWZi/btsJequJ3sY/uO1fkdet5mYndbiCGM75q0/img.png&quot; data-origin-width=&quot;1558&quot; data-origin-height=&quot;1800&quot; data-is-animation=&quot;false&quot; width=&quot;549&quot; height=&quot;634&quot; style=&quot;width: 38.9056%;&quot; data-widthpercent=&quot;39.36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LjWZi/btsJequJ3sY/uO1fkdet5mYndbiCGM75q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLjWZi%2FbtsJequJ3sY%2FuO1fkdet5mYndbiCGM75q0%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;1558&quot; height=&quot;1800&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 어려워졌다는건 점점 늘고 있다는 거겠지..? 문법적인 요소들을 외우기보단, 직접써보면서 어떤 상황에서 어떤 문장을 사용해야하는지를 이해해야할 것 같다. 그럴려면 단순히 공부하는 것 보다 말을 많이해야할 것 같다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 5단계에서 주요 배운 것들을 잠깐 정리해보자.&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;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;li&gt;~かもしれません 일어날 확률이 50%정도일 경우의 추측표현&lt;/li&gt;
&lt;li&gt;동사의 사역수동형&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이정도인 것 같다.&amp;nbsp;&lt;br /&gt;중요한건 이런 표현들을 그럴상황에 잘 사용해야할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언어를 배운다는건 너무 어렵지만... 미니학습지의 강력한 꾸준함을 만들어주는 환급원정대라는 도구로 많이 배우고 있는 건 확실한 것 같다! (내 전문이 꾸준함이지.. 딱 맞아.. 아주)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 6,7,8 세 단계 남았는데 마지막까지 화이팅해보자!&lt;/p&gt;
&lt;figure id=&quot;og_1724596363291&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;미니학습지 &amp;ndash; 즐거운 외국어 생활의 시작&quot; data-og-description=&quot;Previous Next&quot; data-og-host=&quot;studymini.com&quot; data-og-source-url=&quot;https://bit.ly/studymini&quot; data-og-url=&quot;https://studymini.com/?utm_source=zero&amp;amp;utm_medium=sns&amp;amp;utm_campaign=202212&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bICYwb/hyWVYJQYuM/q9nl5kZy8LeS6I1L0VfD8k/img.jpg?width=400&amp;amp;height=229&amp;amp;face=0_0_400_229,https://scrap.kakaocdn.net/dn/npRqG/hyWVTaHw0o/9mf6yqtRXjPOPKswjMwhS1/img.jpg?width=2560&amp;amp;height=941&amp;amp;face=0_0_2560_941,https://scrap.kakaocdn.net/dn/mvRAJ/hyWScXgOCF/NqFF3QVxr0kh7UIADDWC1K/img.png?width=1280&amp;amp;height=300&amp;amp;face=0_0_1280_300&quot;&gt;&lt;a href=&quot;https://bit.ly/studymini&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bit.ly/studymini&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bICYwb/hyWVYJQYuM/q9nl5kZy8LeS6I1L0VfD8k/img.jpg?width=400&amp;amp;height=229&amp;amp;face=0_0_400_229,https://scrap.kakaocdn.net/dn/npRqG/hyWVTaHw0o/9mf6yqtRXjPOPKswjMwhS1/img.jpg?width=2560&amp;amp;height=941&amp;amp;face=0_0_2560_941,https://scrap.kakaocdn.net/dn/mvRAJ/hyWScXgOCF/NqFF3QVxr0kh7UIADDWC1K/img.png?width=1280&amp;amp;height=300&amp;amp;face=0_0_1280_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;미니학습지 &amp;ndash; 즐거운 외국어 생활의 시작&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Previous Next&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;studymini.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;div style=&quot;color: #333333; text-align: left;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0593d3; text-align: left;&quot;&gt;#미니학습지 #스터디미니 #일본어인강 #성인학습지&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: left;&quot;&gt;
&lt;div&gt;
&lt;div id=&quot;reaction-37&quot; data-tistory-react-app=&quot;Reaction&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>일본어</category>
      <category>미니학습지</category>
      <category>성인학습지</category>
      <category>스터디미니</category>
      <category>일본어인강</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/42</guid>
      <comments>https://haranglog.tistory.com/42#entry42comment</comments>
      <pubDate>Sun, 25 Aug 2024 23:34:21 +0900</pubDate>
    </item>
    <item>
      <title>누적 지원자 5000명 IT 비영리단체 DND... 이제는 바꿔야할 때 feat. 마이그레이션</title>
      <link>https://haranglog.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;IT 사이드 프로젝트 동아리&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://dnd.ac/&quot;&gt;DND&lt;/a&gt;에서 운영진 활동을 하고 있고, 11번째 기수가 바쁘게 진행 중인 와중에 누적 지원자수도 5000명이 넘었다.&lt;br /&gt;현재 DND의 홈페이지 관리를 하고 있고 홈페이지를 마이그레이션을 하게 되었다. 이 과정에서 왜 마이그레이션을 했어야만 했는지, 기술적으로 고민했던 포인트와 개선했던 점들을 정리해 보고자 과정기를 작성해보려고 한다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;잘 동작하는 홈페이지.. 왜 마이그레이션을 했을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이그레이션 할 수밖에 없었던 이유는 크게 4가지의 정도 있다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;1. 추가적인 기능 개발과 유지보수하기가 힘들어진 레거시 코드&lt;/h4&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;DND에서는 몇 가지 기능을 추가하고 싶은 작업을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;계획하고 있었다. 그렇지만, 기존의 홈페이지는 vue 2로 운영 중이었고 사소한 버그들 조차 수정하기 어려운 상태로 방치되어 있었다. 그로 인해서 간단한 상태 변경만 가능했고, 신규 개발이나 개선은 할 수 없는 상태였다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;2. 기존 DND 홈페이지를 유지보수하던 운영진의 부재&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 홈페이지를 운영하신 운영진의 장기 부재로 그 홈페이지 관리를 내가 담당하겠다고 했다. 이후 간단한 인수인계만 받았고 vue를 low 하게 사용해 본 내가 레거시 코드인 vue2 기반의 홈페이지를 관리하기엔 쉽지 않았다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;3. 불필요한 홈페이지 운영 리소스를 줄이기 위한 어드민 페이지 구현&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 데이터가 정적인 JSON 파일로 S3에 올려져 관리되고 있다. 관리되고 있는 것들에는 프로젝트, 운영진, 후기 등이 있다. 이 데이터들을 한번 올려두면 거의 대부분 수정될 일이 없긴 하다. 하지만, 1년에 2번씩 매 기수 시작과 끝날 때마다 JSON 파일을 수동으로 수정해서 다시 올리고 있다. 한 번 두 번.. 정도는 할만했었는데 이 과정이 너무 귀찮고 왜 이렇게 불필요하고 비효율적으로 해야 할지 모르겠어서 어드민 페이지도 같이 구현하기로 했다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;4. 성능 개선&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vue2 기반으로 구현되어 있는 코드는 기본적으로 대부분 SSG, SSR, ISR를 고려하지 않고 CSR로 동작하고 있었다. 그래서 페이지 자체가 정적인 페이지임에도 데이터를 가져오기 위해 불필요하게 api를 호출하기도 하기도 하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;1164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdklpB/btsIHGXQq0f/QkZi2i70yn6JftsT19440K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdklpB/btsIHGXQq0f/QkZi2i70yn6JftsT19440K/img.png&quot; data-alt=&quot;lighthouse로 성능 측정해 보았을 때 역시 그렇게 좋은 점수는 아니였다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdklpB/btsIHGXQq0f/QkZi2i70yn6JftsT19440K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdklpB%2FbtsIHGXQq0f%2FQkZi2i70yn6JftsT19440K%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;589&quot; height=&quot;589&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;1164&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;lighthouse로 성능 측정해 보았을 때 역시 그렇게 좋은 점수는 아니였다.&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;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;마이그레이션 전 중점적으로 고민해야 할 사항들&lt;/h2&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;&amp;nbsp;&lt;/span&gt;&lt;b&gt;수동으로 데이터를 수정하기가 너무 귀찮은 이유&lt;/b&gt;가 가장 컸다. (귀찮아.. 자동화.. 내놔 제발)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;그래서 결국은 마이그레이션을 하더라도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;admin을 고려해서 만들어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째로는 DND는 여러 운영진 있고 앞으로 내가 운영진을 안 하게 되어도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;다른 운영자분들도 유지보수를 할 수 있는&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;코드 공통화 &amp;amp; 테스트 코드와 불필요한 외부 라이브러리 의존성은 최대한 없애야 한다고 생각했다. 그렇게 하지 않으면 언젠가는 또 새로운 프로젝트가 생길 것이다. 물론 언젠가는 그렇게 되겠지만 그 시기를 최대한 늦추고 싶은 바람이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 성능&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;개선의 포인트 + 비용 이슈&lt;/b&gt;였다. 성능 개선의 포인트로는 SEO 최적화가 필수였고, 불필요하게 CSR로만 동작하는 정적인 페이지를 ISR(SSG) 방식으로 바꾸는 방식과 client side에서 호출되는 api 제거하기 위한 부분이다.&lt;br /&gt;비용 이슈는 DND는 비영리 단체로 후원을 통해서 운영되는 단체이다. 그렇지만 현재 IT 전반적인 업계 상황이 좋지 못해 후원이 들어오지 않는 상태여서 비용이 드는 부분은 최대한 제거하려고 한다. 따라서 aws에 올려둔 데이터를 최대한 제거하는 작업이 필수였다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;이제 프로젝트 구조와 기술 스택을 고민해 보자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 점들을 고려해 프로젝트 구조와 기술 스택을 고민해 보자.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;기술 스택&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;Next.js, React, TypeScript&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단, 가장 익숙하고 대중적인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;React, TypeScript&lt;/b&gt;를 선택했다.&lt;br /&gt;SEO 최적화와 잘 만들어져 있는 유용한 기능들도 사용하기 위해서&amp;nbsp;&lt;b&gt;Next.js&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;프레임워크를 선택했다. Remix나 Gatsby와 같은 친구도 고려해 볼 수 있었지만, 빠르게 개발을 해야 하는 상황에서 가장 익숙한 프레임워크를 사용하는 게 맞을 것 같았고 Gatsby나 Remix는 얕게만 해본 경험이 있어서 학습을 해야 한다는 점이 부담스러워 Next.js를 선택했다. (다음번에 기회가 된다면 Remix는 사용해보고 싶다.)&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;스타일&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스타일은 css-in-js(styled-component, emotion), scss(sass) module, tailwind css, zero runtime css-in-js(styleX, vanilla-extract, panda css, linaria...)와 같이 적용할 수 있는 방법이 굉장히 많았다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&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;2656&quot; data-origin-height=&quot;1978&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/li4JZ/btsIIYy4c7H/TIVuyAXhsz7X6kyaunDVA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/li4JZ/btsIIYy4c7H/TIVuyAXhsz7X6kyaunDVA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/li4JZ/btsIIYy4c7H/TIVuyAXhsz7X6kyaunDVA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fli4JZ%2FbtsIIYy4c7H%2FTIVuyAXhsz7X6kyaunDVA0%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;778&quot; height=&quot;579&quot; data-origin-width=&quot;2656&quot; data-origin-height=&quot;1978&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;모든 기술 선택에 있어서 정답이 없듯이 내가 정했던 기준을 바탕으로 하나씩 경우의 수를 제거해나 가보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 스타일을 javascript 코드와 같이 관리할 수 있어서 강력한 개발생산성으로 많은 사랑을 받았고, 여전히 많이 사용하는 (run time) css-in-js는 런타임에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.npmjs.com/package/stylis&quot;&gt;stylis&lt;/a&gt;를 사용해 css를 구문 분석을 하여 클래스 이름을 생성 후 &amp;lt;style&amp;gt; 태그 생성 후 &amp;lt;head&amp;gt; 태그에 삽입을 한다. 이런 과정으로 인해 next.js app router와 react의 서버 컴포넌트가 생긴 시점에 이런&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/styling/css-in-js&quot;&gt;런타임 css-in-js는 호환&lt;/a&gt;되지 않는다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;결과적으로 css-in-js를 사용하여 스타일을 적용한 컴포넌트에서는 &quot;use client&quot; 선언문을 사용해서 클라이언트 컴포넌트&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;로만 사용이 가능하다. 단지 스타일을 적용하고 싶을 뿐인데.. 클라이언트 컴포넌트로 사용을 해야 한다니..! 이건 불필요하게 클라이언트 컴포넌트를 사용하는 셈이기에 원했던 방법이 아니다. 심지어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/emotion-js/emotion/issues/2928&quot;&gt;emotion은 아직 지원하지 않는다.&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로 런타임 기반의 css-in-js는 사용하지 않았다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;그렇다면 zero runtime css-in-js는 어떨까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zero runtime css-in-js는 기존 css-in-js의 장점을 가져가면서 스타일이 런타임이 아닌 빌드 타임에 생성된다. 즉, 스타일을 주입하기 위해 추가적인 런타임 작업이 필요하지 않다. 그렇기 때문에 서버 컴포넌트에서도 충분히 사용이 가능하다. 여러 가지 zero runtime css-in-js가 있었고 그중&amp;nbsp;&lt;a href=&quot;https://vanilla-extract.style/&quot;&gt;vanilla extract&lt;/a&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://panda-css.com/&quot;&gt;panda css&lt;/a&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://stylexjs.com/&quot;&gt;styleX&lt;/a&gt;가 보였고 이들을 간단히 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;styleX&lt;/b&gt;는 메타에서 관리하고 있으며 babel을 사용해서 빌드 시에 css를 생성하며&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://stylexjs.com/docs/learn/styling-ui/defining-styles/#dynamic-styles&quot;&gt;동적 스타일링&lt;/a&gt;도 지원하기도 한다. 하지만, 아직 버전 0대이고 가장 중요한 next.js의 호환성이 좋지 못하다는 점이었다. 이유는 babel을 사용해야 하는데 next.js에서는 내부 컴파일러로 swc를 사용하고 있기 때문에 next.js에서 제공하는 기본 기능들을 사용하지 못할 수도 있다. (예를 들어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nextjs.org/docs/messages/babel-font-loader-conflict&quot;&gt;next/font&lt;/a&gt;.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;panda css&lt;/b&gt;는 ChakraUI에서 관리하고 있으며&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://panda-css.com/docs/installation/nextjs&quot;&gt;공식 문서&lt;/a&gt;에 next.js app router 세팅도 소개하고 있다.&lt;br /&gt;&lt;b&gt;vanilla extract&lt;/b&gt;는 이미 당근에서도 사용 중이었고 확장성과 타입 안정성, 객체형태로 풀어나가는 방법 등 개발자 경험에 있어서 매우 좋아 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 좋은 점들이 있음에도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;zero runtime css-in-js는 선택하지 않았다.&amp;nbsp;&lt;/b&gt;&lt;br /&gt;첫 번째 이유로 위에서 얘기했던 중점적으로 고민했던 사항들 중&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;내가 운영진 활동을 하지 않게 되어도 앞으로 관리할 다른 운영자분들을 생각하여 유지보수성을 우선적으로 생각해 볼 필요가 있었다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;그런 점에서&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;보았을 때 css-in-js의 문법에 대한 학습이 필요하다는 점과 언제 또 바뀔지 모르는 불확실한 css-in-js의 생태계, css-in-js를 사용하기 위한 해당 라이브러리에 대한 의존성이 생기는 점으로 인해 유지보수성을 떨어트리는 요소들이 분명 존재했다.&lt;br /&gt;두 번째 이유로는 css-in-js에서 느꼈던 좋았던 장점들과 개발 생산성이 뛰어나다는 점이 있었지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;결국은 styled-component나 emotion처럼 언젠가는 또 바뀔 것 같은 불확실함이 컸던 것 같다. 빠르게 변하는 프론트 생태계에서 또 다른 좋은 라이브러리들이 생겨 마이그레이션 하는 과정이 지친다고 해야 할까...? 확실히 느꼈던 계기가 회사에서 styled-component를 사용하고 있었고 next.js에서 app router로 전환하기 위해서 모든 styled-component를 걷어낼 수밖에 없었던 명확한 이유와 사용하고 있는 모든 곳에서 걷어내며 통합 테스트를 한 과정이 그렇게 쉽지만은 않았다는 점에서 지칠 때로 지쳐버렸던 게 아닐까 싶었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;세 번째로 고려했던&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;tailwind css&lt;/a&gt;&lt;/b&gt;이다. tailwind css는 사용하기 전에는 className이 길어지는 점과 react 컴포넌트의 tag의 className에 인라인 스타일과 같이 적용되어 있어서 관심사가 분리되어있지 않아 가독성이 좋지 않은 점으로 인해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;개인적으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;선호하지 않았다. 하지만 사용하기 전까지는 모른다고 직접 사용해보니 느꼈던 장점은 일관성 있는 디자인 시스템을 적용할 수 있으며 개발 생산성 또한 css나 style블록으로 이동하지 않아도 돼서 매우 좋았고, shadcn, nextui와 같은 ui 라이브러리에서 tailwind css를 기본적으로 사용하고 있다는 점도&lt;span&gt;&amp;nbsp;&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;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;하지만 tailwind css도 선택하지 않았다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;tailwind css도 zero runtime css-in-js를 선택하지 않은 이유와 비슷하다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;첫 번째로 css-in-js보단 덜 하겠지만 학습 곡선이 분명 있었고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;일부 css 속성과 고급 기술을 사용할 때에는&amp;nbsp;불필요한 인라인 스타일을 추가하거나 결국 css를 파일을 생성해야 한다.&lt;br /&gt;두 번째로는 나중에 마이그레이션을 하게 되면 tailwind css는 스타일과 코드가 매우 밀접하게 연결이 되어있어서 관심사 분리가 되어 있지 않아서 걷어내기가 쉽지 않을 수밖에 없다.&lt;br /&gt;세 번째로는 tailwind css의 장점인 특정 값만 허용하여 일관된 디자인을 적용할 수 있다는 점인데 이 부분이 지금 현 상황에서는 단점이 될 수밖에 없었다. 이유는 현재 디자인이 일관되지 않은 spacing, size로 옛날 디자인을 바탕으로 작업을 해야 하기 때문에 모든 값을 일관되게 사용할 수 없어 결국 많은 값들을 커스텀 스타일을 사용해서 작성해야 한다. 이 문제를 해결하려면 먼저 일관되게 디자인이 재작업이 되어야 하는데 지금 당장&lt;span&gt;&amp;nbsp;&lt;/span&gt;재작업을 할 수 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;디자이너 분들의 여유도 없어서 그렇게 되면 마이그레이션 진행이 늦어질 수밖에 없었다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;마지막으로 고려했던&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;module&amp;nbsp;&lt;a href=&quot;https://sass-lang.com/&quot;&gt;scss&lt;/a&gt;(sass)&lt;/b&gt;이다. sass는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/styling/sass&quot;&gt;공식적으로 next.js&lt;/a&gt;에서 제공해서 추가로 설정을 해줄 필요가 없이 바로 사용이 가능하며, 서버 컴포넌트에서도 바로 사용이 가능하다. 또한, css와의 호환성도 뛰어나고 넓은 범용성을 가진 scss는 css만 알고 있으면 다른 의존성에 영향 없이 스타일을 수정할 수 있어서 유지보수성도 좋다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722531087956&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import styles from './index.module.scss'
 
export default function Page() {
  return &amp;lt;h1 className={styles.title}&amp;gt;Hello, Scss!&amp;lt;/h1&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;물론 단점도 있는데 tailwind css나 css-in-js에서 개발 생산성을 높여주는 요소들을 scss에서 mixin 함수로 직접 구현해야 한다는 점과 스타일을 작성하기 위해서 매 컴포넌트마다 scss파일을 생성해야 하기 때문에 코드와 스타일 파일 간 스위칭이 지속적으로 일어날 수밖에 없어 비교했을 때에 생산성이 떨어지는 점이 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;하지만 이런 개발 생산성을 조금 포기하면 내가 고려했던 부분은 모두 충족하게 된다.&lt;br /&gt;sc ss는 의존성에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;가장&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;영향을 받지 않아서 유지보수하기에도 좋으며, 스타일과 코드 간의 관심사가 명확히 구분되어 나중에 마이그레이션 할 때에도 쉽다. 이런 장점이라면 css를 사용하는 게 가장 좋지 않을까?라고 생각할 수도 있겠지만 css로 직접 사용하는 건 확실히 아쉬운 부분이 있다. 변수의 사용 및 조건문, 반복문, mixin 등 우리가 가독성과 재사용성에 도움을 주는 부분에서 공통으로 관리해서 불필요한 반복적인 코드를 줄여줄 수 있어 scss를 활용하면 매우 강력한 기능을 활용할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722531087958&quot; class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;@mixin text($text, $color-code: '') {
  // ... 생략

  @if $font == 'H1' {
    font-size: var(--h1-font-size);
  } @else if $font == 'H2' {
    font-size: var(--h2-font-size);
    letter-spacing: var(--h2-letter-spacing);
  }
  
  // ... 생략
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722531087959&quot; class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;.title {
  @include text('H3/ExtraBold', 'white');

  @include xs {
    @include text('H4/ExtraBold');
  }
}

.description {
  @include text('body2/Regular', 'gray04');

  &amp;amp;:hover {
    color: color('gray03');
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;결국에는 이러한 고민들을 거쳐&amp;nbsp;&lt;b&gt;scss module를 사용하기로 결정&lt;/b&gt;했다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;zustand &amp;amp; react-query&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 상태 관리 라이브러리는 zustand와 서버 상태 관리를 도와주는 data fetching 라이브러리는 react-query로 가장 익숙한 라이브러리를 선택했다. 하지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;두 라이브러리는 거의 사용할 일이 없을 것이다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;특히 react-query를 사용해서 클라이언트 사이드에서 api를 호출할 일이 거의 없기 때문에 사용하게 된다면, 그때 추가할 예정이다. 서버 쪽에서 호출할 때에는 단순히 fetch api를 사용할 예정이다.&lt;br /&gt;&lt;a href=&quot;https://nextjs.org/docs/app/api-reference/functions/fetch&quot;&gt;nextjs app router에서는 기본 fetch api를 확장&lt;/a&gt;하여 cache를 컨트롤할 수 있기 때문에 fetch api를 사용하기를 권장하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722531087961&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fetch(`https://...`, { cache: 'force-cache' | 'no-store' });
fetch(`https://...`, { next: { revalidate: false | 0 | number } });
fetch(`https://...`, { next: { tags: ['collection'] } });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;axios를 포함한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating?ref=blog.deercorp.com#fetching-data-on-the-server-with-third-party-libraries&quot;&gt;다른 라이브러리를 사용할 경우&lt;/a&gt;에는 직접 캐싱 관련 설정을 해줘야 한다. axios가 주는 간편안 이점이 있긴 하지만, api를 호출할 일이 거의 없기 때문에 fetch api로도 충분할 거라 판단했다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;프로젝트 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 구조에서 가장 고민이 되었던 건 admin과 web 홈페이지를 함께 사용하는 모노레포 형식으로 할지, 따로따로 각 프로젝트별 레포지토리를 하나로 구성할지가 가장 고민이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고민을 했던 이유는 모노레포로 구성시 프로젝트 구조가 복잡해지고, 구조를 세팅하는 시간이 오래 걸린다는 점과 굳이라는 단어가 계속 떠다녔던 이유가 있었다. 기본적으로 lint 설정, typescript 세팅을 제외한 admin과 web 간의 공통으로 사용할만한 코드나 컴포넌트가 있을까라는 의문이 있었고, 애초에 두 프로젝트는 서로 다른 목적을 가져서 필요 없다고 생각이 들었다. 그래서 홈페이지를 먼저 개발할 때에는 모노레포를 선택하지 않고 개발하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 홈페이지 개발 막바지에 어드민 개발을 시작할 때에는 모노레포로 바꿔야겠다고 생각하여 변경하였다. 이유는 어드민은 홈페이지의 정적 데이터를 수정하거나 추가하여 홈페이지를 업데이트를 하기 위해서 만든다. 그런데 이 데이터가 수정되고 추가되는걸 단순히 form 형태가 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;미리 보기 형태&lt;/b&gt;까지도 제공하면 비개발자가 데이터를 추가하거나 수정할 때에도 더 직관적일 것이고 개인적으로 재밌겠다는 생각이 들었다.&lt;br /&gt;그러려면 홈페이지에서 사용하는 여러 컴포넌트들이 어드민에 사용되어야 하기 때문에 중복된 컴포넌트를 제거하기 위해서는 공통화시킬 필요가 있어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 하여 이후에 모노레포로 구성하였다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;turborepo with yarn v4.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모노레포 도구는 vercel에서 만든&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://turbo.build/repo&quot;&gt;turborepo&lt;/a&gt;를 사용했었는데, 이전에 사용했던 경험도 있고 개발자 경험이 좋았고&amp;nbsp;&lt;a href=&quot;https://turbo.build/repo/docs/core-concepts/remote-caching&quot;&gt;원격 캐시&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기능도 있어서 빠른 빌드를 할 수 있어서 좋았다. 버전 2.0에서는 터미널 UI가 개선되었는데 꽤나 이쁘다...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/465TK/btsILxU1KHj/EGLbPQEhEmkXYkZfxBoNSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/465TK/btsILxU1KHj/EGLbPQEhEmkXYkZfxBoNSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/465TK/btsILxU1KHj/EGLbPQEhEmkXYkZfxBoNSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F465TK%2FbtsILxU1KHj%2FEGLbPQEhEmkXYkZfxBoNSk%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;208&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;turborepo를 사용할 때 한 가지 이슈가 있었던 점은 패키지 매니저를 yarn berry의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://yarnpkg.com/features/pnp&quot;&gt;PnP&lt;/a&gt;와 zero install기능을 사용하고 있었는데, turborepo에서는 아직 이 PnP기능을 제대로 지원하지 않아서 의존성을 못 찾는 이슈가 있어 PnP 기능을 걷어내고 패키지 의존성을 node_modules로 변경하여 관리할 수밖에 없었다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087963&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// .yarnrc.yml
nodeLinker: node-modules&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://turbo.build/blog/turbo-1-1-0#partial-yarn-v2v3-support&quot;&gt;turborepo의 버전이 v1 초반에서는 공식적으로 지원하지 않았었고&lt;/a&gt;, turborepo의 메인테이너분께서 &lt;a href=&quot;https://github.com/vercel/turbo/issues/693#issuecomment-1278886166&quot;&gt;PnP의 지원을 계획하지 않는다&lt;/a&gt;고 하셨던 걸로 알고 있었는데 버전이 업데이트되면서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://turbo.build/repo/docs/getting-started/support-policy&quot;&gt;PnP의 기능은 동작하도록 업데이트된 것 같았다.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;996&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DCtt7/btsIO20TDe0/5Q2IZW0u6rn3IPs1EHjVI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DCtt7/btsIO20TDe0/5Q2IZW0u6rn3IPs1EHjVI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DCtt7/btsIO20TDe0/5Q2IZW0u6rn3IPs1EHjVI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDCtt7%2FbtsIO20TDe0%2F5Q2IZW0u6rn3IPs1EHjVI0%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;472&quot; height=&quot;406&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;996&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://turbo.build/blog/turbo-1-2-0#other-bug-fixes-and-improvements&quot;&gt;turborepo 1.2 yarn pnp turbo run 명령어에 한해서 부분지원&lt;/a&gt;&lt;br /&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://turbo.build/blog/turbo-1-5-0#prune-now-supported-on-pnpm-and-yarn-2&quot;&gt;turborepo 1.5 turbo prune 명령어까지 yarn 2+ 지원&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 로컬에서 사용할 때에는 문제없이 잘 동작했는데, vercel에 배포할 때 의존성을 못 찾는 이슈가 있는 것 같아서 몇 시간 동안 해결방법을 찾다가 결국 마음 편하게.. PnP 모드를 사용하기 않게 되었다. (매번 PnP기능의 장점 때문에 사용하고 싶은데 이런 안 되는 이슈들 또는 기존과 다르게 설정해줘야 하는 귀찮은 일들이 반복적으로 생기다 보니까 하지말까라는 생각이 들긴 하지만 또 해결했을 때 뿌듯함 때문에 계속 시도하게 되는 것 같다.)&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;2530&quot; data-origin-height=&quot;990&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OEf7H/btsIOC9xoBn/t9WHAww5TPYOb7pJEzgOTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OEf7H/btsIOC9xoBn/t9WHAww5TPYOb7pJEzgOTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OEf7H/btsIOC9xoBn/t9WHAww5TPYOb7pJEzgOTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOEf7H%2FbtsIOC9xoBn%2Ft9WHAww5TPYOb7pJEzgOTK%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;676&quot; height=&quot;265&quot; data-origin-width=&quot;2530&quot; data-origin-height=&quot;990&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://vercel.com/guides/does-vercel-support-yarn-2&quot;&gt;vercel build 파이프라인은 정적 빌드에 대해서만 yarn2 지원. 서버리스 기능 및 SSR에서는 yarn 2 지원하지 않는다.&lt;/a&gt;&lt;br /&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/vercel/vercel/discussions/4223&quot;&gt;https://github.com/vercel/vercel/discussions/4223&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 하여 전체적인 프로젝트 구조는 하나의 프로젝트 안의 apps 밑에 admin과 web으로 모노레포 형식으로 vercel에 두 프로젝트를 나누어 배포하는 형태로 구성하였다.&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/4H4Dx/btsINxuBGHN/GKLnPOV4rLeuZdzzKZpBG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4H4Dx/btsINxuBGHN/GKLnPOV4rLeuZdzzKZpBG0/img.png&quot; style=&quot;width: 26.0324%; margin-right: 10px;&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;344&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;26.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4H4Dx/btsINxuBGHN/GKLnPOV4rLeuZdzzKZpBG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4H4Dx%2FbtsINxuBGHN%2FGKLnPOV4rLeuZdzzKZpBG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyls89/btsIOB30rWN/mc2zsh0JYePRj2K9GZdAPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyls89/btsIOB30rWN/mc2zsh0JYePRj2K9GZdAPK/img.png&quot; style=&quot;width: 72.8048%;&quot; data-origin-width=&quot;1952&quot; data-origin-height=&quot;490&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;73.66&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyls89/btsIOB30rWN/mc2zsh0JYePRj2K9GZdAPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcyls89%2FbtsIOB30rWN%2Fmc2zsh0JYePRj2K9GZdAPK%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;1952&quot; height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;admin과 web에서 사용하는 공통 코드들은 packages 밑으로 이동시켜 core(api, util..), eslint, ui(component, hook)을 공통으로 사용하도록 구성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 구조는 app router로 변경된 이후&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/routing/colocation#project-organization-strategies&quot;&gt;공식문서에도 언급되어 있듯이&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;정답이 없는 것 같다. 활용할 수 있는 방법이 굉장히 많고 할 수 있는 방법 또한 일관성을 유지한 구조로 자유롭게 정하면 되는 것 같다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;There is no &quot;right&quot; or &quot;wrong&quot; way when it comes to organizing your own files and folders in a Next.js project.&lt;br /&gt;The following section lists a very high-level overview of common strategies. The simplest takeaway is to choose a strategy that works for you and your team and be consistent across the project.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;src의 역할을 하는 app을 root로 사용해서 내부 안에 모든 코드가 다 들어가는 방법도 있었는데, app을 router방식으로만 사용하는 방법을 선택했다.&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/TcPsI/btsINqvGiX3/qKsqHIOIiRZVA9u7lKAEZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TcPsI/btsINqvGiX3/qKsqHIOIiRZVA9u7lKAEZ1/img.png&quot; style=&quot;width: 36.3292%; margin-right: 10px;&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;498&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;37.19&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TcPsI/btsINqvGiX3/qKsqHIOIiRZVA9u7lKAEZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTcPsI%2FbtsINqvGiX3%2FqKsqHIOIiRZVA9u7lKAEZ1%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;390&quot; height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xlQOR/btsIPyr0EJl/zk5gefh0kDyk0KVuJBXk1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xlQOR/btsIPyr0EJl/zk5gefh0kDyk0KVuJBXk1K/img.png&quot; style=&quot;width: 30.8066%; margin-right: 10px;&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;774&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;31.54&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xlQOR/btsIPyr0EJl/zk5gefh0kDyk0KVuJBXk1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxlQOR%2FbtsIPyr0EJl%2Fzk5gefh0kDyk0KVuJBXk1K%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;514&quot; height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccww5b/btsIPLLALg8/sJOIxv0dEAjsdeOWu3si0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccww5b/btsIPLLALg8/sJOIxv0dEAjsdeOWu3si0K/img.png&quot; style=&quot;width: 30.5386%;&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;638&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;31.27&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccww5b/btsIPLLALg8/sJOIxv0dEAjsdeOWu3si0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fccww5b%2FbtsIPLLALg8%2FsJOIxv0dEAjsdeOWu3si0K%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;420&quot; height=&quot;638&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;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;Atomic Design&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트를 나누는 기준으로는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://atomicdesign.bradfrost.com/chapter-2/&quot;&gt;atomic design&lt;/a&gt;을 사용해서 구조를 나눴다.&lt;br /&gt;Atomic 디자인을 사용한 이유는 첫 번째 지금처럼 복잡하지 않은 프로젝트에서는&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;컴포넌트 간의 관계가 그렇게 복잡해지지 않아 컴포넌트를 독립적으로 잘 모듈화 하여 개발할 수 있다는 장점&lt;/b&gt;이었다. 회사에서 매우 복잡한 프로젝트에서 atomic 디자인 패턴으로 개발한 경험이 있었는데 각 컴포넌트끼리의 관계가 너무 복잡해지는 단점과 molecules와 organism 중 어디에 위치해야 할지에 대한 고민들과 팀원 간의 생각하는 위치가 다른 점들로 프로젝트가 커질수록 점점 복잡해지는 단점이 있었다. 하지만, 지금 홈페이지와 커지거나 수정될 가능성이 적고 admin과 web 두 군데의 프로젝트에서 동일하게 사용할 컴포넌트를 atomic 디자인을 사용했을 때 장점 드러날 수 있는 구조라고 생각했다.&lt;br /&gt;두 번째로는 작은 단위의 컴포넌트부터 시작하여 점진적으로 복잡한 구조를 만드는 과정은 일관된 컴포넌트를 만들어 디자인 시스템을 만들기에도 좋고 무엇보다 테스트를 작성하기 좋은 구조이다. 그로 인해서 admin과 web에서 사용될 컴포넌트를 따로 빼서 모듈화 하여 사용하기에 좋아 리팩터링 하기에도 적합해 보였다.&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;atomic 디자인의 templates 폴더는 거의 사용하지 않았다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;이유는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#templates&quot;&gt;next.js에서 제공하는 layout.js와 template.js&lt;/a&gt;를 활용했기 때문이다. atomic 디자인에서 templates가 컴포넌트들을 레이아웃의 잡고 배치하는 구조를 만들어주는 역할을 하기 때문에 그런 점에서 next.js에서 제공해 주는 l&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ayout.js와 template.js의 역할이&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;비슷했다. 공식문서에서도 layout은 여러 경로 간에&lt;span&gt;&amp;nbsp;&lt;/span&gt;공유되는&lt;span&gt;&amp;nbsp;&lt;/span&gt;UI, template은 각 자식 layout이나 page를 wrapping 한다는 점에서 역할이 비슷하다고 생각하였다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;next.js에서 제공해 주는 layout과 template를 사용하면 이점이 생기는데, 이 둘은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;다른 props를 받을 수 없고 오로지 자식 컴포넌트만 받기 때문에&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;atomic 디자인에서 template에 해당하는 컴포넌트의 기준을 명확히 나눌 수 있는 기준을 세우기 좋았다. (정말로 props의 의존하지 않는 layout만 해당하는 구조를 잡아주는 역할)&lt;br /&gt;그리고 이렇게 나누어 template을 관리하면 next.js에서 template와 layout 기준으로 code splitting 효과를 얻을 수 있어서&lt;span&gt;&amp;nbsp;&lt;/span&gt;초기 로딩 시간(First Load JS)을 줄일 수 있다.&lt;/span&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;2076&quot; data-origin-height=&quot;66&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUWf4W/btsIPXr1nao/hbLuqJ7NhffrLNrUJ5mrwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUWf4W/btsIPXr1nao/hbLuqJ7NhffrLNrUJ5mrwk/img.png&quot; data-alt=&quot;이전 -&amp;amp;gt; 이후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUWf4W/btsIPXr1nao/hbLuqJ7NhffrLNrUJ5mrwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUWf4W%2FbtsIPXr1nao%2FhbLuqJ7NhffrLNrUJ5mrwk%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;2076&quot; height=&quot;66&quot; data-origin-width=&quot;2076&quot; data-origin-height=&quot;66&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이전 -&amp;gt; 이후&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점도 있었는데 route에 template의 하위에 있는의 있는 모든 layout과 template이 적용된다는 점이었다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://rjroopal.medium.com/atomic-design-pattern-structuring-your-react-application-970dd57520f8&quot;&gt;Atomic Design Pattern: Structuring Your React Application&lt;/a&gt;&lt;br /&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://velog.io/@teo/Atomic-Design-Pattern&quot;&gt;Atomic Design Pattern의 Best Practice 여정기&lt;/a&gt;&lt;br /&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://fe-developers.kakaoent.com/2022/220505-how-page-part-use-atomic-design-system/&quot;&gt;아토믹 디자인을 활용한 디자인 시스템 도입기&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;사용했던 &amp;amp; 고민했던 기술적인 포인트&lt;/h2&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;서버컴포넌트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next.js 13에서 app router 베타를 거쳐 13.4에서 stable로 바뀌었고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/rendering/server-components&quot;&gt;서버 컴포넌트&lt;/a&gt;도 사용해 보았다. (사실 next.js 12 때부터 사용해 보긴 했었다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next.js에서는 서버 컴포넌트를 사용할 때 기본적으로 static rendering(정적 렌더링)으로 동작하고, 동적으로 변할 수 있는 쿠키나 searchParams를 사용하면 동적 렌더링으로 변한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/rendering/server-components#benefits-of-server-rendering&quot;&gt;서버 컴포넌트를 사용하면 여러 가지 이점&lt;/a&gt;을 누릴 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhU4fb/btsIThwRloH/9mcmtgbMygBrMlUiR0pNqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhU4fb/btsIThwRloH/9mcmtgbMygBrMlUiR0pNqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhU4fb/btsIThwRloH/9mcmtgbMygBrMlUiR0pNqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhU4fb%2FbtsIThwRloH%2F9mcmtgbMygBrMlUiR0pNqk%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;398&quot; height=&quot;418&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dnd 홈페이지를 build 한 내용인데 확인해보면&amp;nbsp;데이터 패칭이나 동적 함수를 사용하지 않은 소개 페이지인&amp;nbsp;&lt;i&gt;/dnd/about&lt;/i&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;/dnd/culture&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;페이지는 정적 콘텐츠로 사전 렌더링된 것으로 확인해 볼 수 있었다.&lt;br /&gt;&lt;i&gt;/organiers/[id]&lt;/i&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;/projects/[id]&lt;/i&gt;는 빌드 시점에 데이터를 가져와 페이지를 사전에 생성하는 방식인 SSG가 적용되어 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087967&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export function generateStaticParams() {
  const projects = getProjects();

  return projects.map(({ id }) =&amp;gt; ({
    id: String(id),
  }));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적렌더링이 적용되어 있는 페이지들은 searchParams를 사용하거나 데이터 패칭이 발생하는 곳이다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;compound component pattern&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 컴포넌트의 이점을 누릴 수 있는 부분들로 인해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.patterns.dev/react/compound-pattern/&quot;&gt;compound component pattern&lt;/a&gt;을 유용하게 사용하여 서버 컴포넌트와 클라이언트 컴포넌트를 잘 조합하여 사용할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 예로 modal 컴포넌트를 들어보자.&lt;br /&gt;먼저 modal을 전역적으로 관리할 것인가, local context로 관리할 것인가에 대한 부분에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=iMoAviG_ZUE&amp;amp;ab_channel=UIEngineering&quot;&gt;마침 참고하기 좋은 유튜브&lt;/a&gt;를 발견해서 참고하였다.&lt;br /&gt;결론만 말하자면 대부분의 경우에서는 전역적으로 관리하는 것보다 local context로 관리하는 게 더 많은 이점을 누릴 수 있다는 내용이었고 참고하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;modal을 지역적으로 관리하도록 구성&lt;/b&gt;하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4ETbv/btsISXyFuYH/EvVEKIU5dvF6f0MLWElw0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4ETbv/btsISXyFuYH/EvVEKIU5dvF6f0MLWElw0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4ETbv/btsISXyFuYH/EvVEKIU5dvF6f0MLWElw0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4ETbv%2FbtsISXyFuYH%2FEvVEKIU5dvF6f0MLWElw0k%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;522&quot; height=&quot;286&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;지역적으로 사용할 경우 모달을 열고, 닫는 로직이 중복적으로 사용될 것이다. 그리고 이렇게 사용했을 경우에 사용자의 클릭 이벤트가 발생하기 때문에 사용하는 컴포넌트에서는 서버 컴포넌트로 사용이 불가능하다. 물론 모달을 열고 닫는 로직을 가진 클라이언트 컴포넌트를 새로 생성하면 해결되긴 하지만 불필요한 컴포넌트가 생길 뿐이다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087968&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function SomeComponent() {
  const { toggle } = useModalContext();

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;title&amp;lt;/h1&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; toggle(true)}&amp;gt;모달 열기&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 로직을 가지는 모달을 여는 버튼 컴포넌트를 자식으로 받아서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;공통으로 사용되는&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;ModalOpenButton을 React.cloneElement를 사용해서 compound pattern을 조금 더 유연하게 사용할 수 있다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722531087969&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ModalOpenButton({
  children: child,
} : { children: ReactElement&amp;lt;ButtonHTMLAttributes&amp;lt;HTMLButtonElement&amp;gt;&amp;gt; }) {
  const { toggle } = useModalContext();

  return cloneElement(child, {
    onClick: (e: MouseEvent&amp;lt;HTMLButtonElement&amp;gt;) =&amp;gt; {
      toggle(true);
      child.props.onClick?.(e);
    },
  });
}

export default ModalOpenButton;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 모달의 구성으로 DND에 지원하는 모달을 구성해 봤다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087969&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ApplyModal({ children: child }: Props) {
  return (
    &amp;lt;Modal&amp;gt;
      &amp;lt;Modal.OpenButton&amp;gt;
        {cloneElement(child, child.props, [
          &amp;lt;span key=&quot;default-label&quot;&amp;gt;{`${CURRENT_FLAG}기 지원하기`}&amp;lt;/span&amp;gt;,
          child.props.children,
        ])}
      &amp;lt;/Modal.OpenButton&amp;gt;
      &amp;lt;Modal.ContentsBase title={`${CURRENT_FLAG}기 지원하기`} size=&quot;small&quot;&amp;gt;
      {/* ... */}
      &amp;lt;/Modal.ContentsBase&amp;gt;
    &amp;lt;/Modal&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실질적으로 이 ApplyModal을 사용하는 곳을 확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087970&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ShareAlarmSection() {
  /* ... */
  
  return (
    &amp;lt;div className={styles.shareAlarmSection}&amp;gt;
      /* ... */
      &amp;lt;ApplyModal&amp;gt;
        &amp;lt;Button fullWidth size=&quot;xLarge&quot; buttonType=&quot;primary&quot; /&amp;gt;
      &amp;lt;/ApplyModal&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

// ...

function HomePage() {
  /* ... */
  
  return (
    &amp;lt;div className={styles.homepage}&amp;gt;
      /* ... */
      &amp;lt;ApplyModal&amp;gt;
        &amp;lt;Button isAnimated={false}&amp;gt;
          &amp;lt;RightArrowIcon className={styles.arrowIcon} width={20} height={20} /&amp;gt;
        &amp;lt;/Button&amp;gt;
      &amp;lt;/ApplyModal&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모달을 사용하는 쪽에서는 내부 동작하는 과정은 몰라도 되는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;블랙박스 형태로 구현이 가능해졌다. 즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;각 모달을 열고 닫는 버튼의 중복되는 사용자 이벤트는 없이 각 버튼의 속성을 다르게 적용할 수 있게 되었다. 그리고 이렇게 사용하면 사용자 이벤트가 없어서 서버 컴포넌트에서 바로 임포트하여 사용이 가능하다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;- Kent C. Dodds의 글을 참고하였다.&lt;br /&gt;&lt;a href=&quot;https://www.epicreact.dev/soul-crushing-components&quot;&gt;https://www.epicreact.dev/soul-crushing-components&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://kentcdodds.com/blog/compound-components-with-react-hooks&quot;&gt;https://kentcdodds.com/blog/compound-components-with-react-hooks&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;server actions&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://react.dev/reference/rsc/server-actions&quot;&gt;server action&lt;/a&gt;도 next.js 14에서 stable 상태가 되었고, 서버에서 실행되는 비동기적 함수를 호출할 수 있다. react에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;'use server'&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/i&gt;지시문을 사용해 서버 함수에 대한 참조를 생성할 수 있다. 이번에 server action을 사용해 form과 관련한 api 호출을 할 때에 사용했는데, DND 홈페이지에서는 사용자의 form을 받아 api를 호출하는 경우가 없어서 server action을 사용하는 일은 없었다.&lt;br /&gt;홈페이지에 보이는 정적인 데이터를 수정과 사용자 인증을 하기 위한 역할로 admin 페이지에서만 server action 기능을 활용해 사용해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 어드민에서는 간단하게&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://authjs.dev/&quot;&gt;auth.js&lt;/a&gt;(next auth)를 사용해서 간단하게 인증 과정을 구현했다. auth.js는 아직 베타 버전이지만 이렇게 간단하게 인증 처리를 하기에는 문제가 없었고 이전 next auth의 사용성과 비교해 보았을 때 app router에서 너무 간단하게 사용이 가능해서 바로 적용해서 사용하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087973&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Button } from '@dnd-academy/ui';

import { signIn } from '@/auth';

function SignIn() {
  return (
    &amp;lt;form
      action={async () =&amp;gt; {
        'use server';

        await signIn('google', { redirectTo: '/' });
      }}
    &amp;gt;
      &amp;lt;Button type=&quot;submit&quot;&amp;gt;로그인&amp;lt;/Button&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}

export default SignIn;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 버튼을 클릭하면 form 양식이 server action을 통해 호출되고 로그인 인증 상태에 성공하게 된다. server action을 사용하면 안전하게 쿠키를 수정하거나 삭제할 수 있다. 그리고 server action은 비동기 함수여서 재사용 가능하도록 export 해서 사용이 가능하다. (주의해야 할 사항으로 재사용을 위해 action 파일을 만들고 export 시킬 때에는 해당 파일에 동기 함수가 없어야만 한다.)&lt;/p&gt;
&lt;pre id=&quot;code_1722531087974&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Button } from '@dnd-academy/ui';

import { signOutAction } from '@/auth';

function SignOut() {
  return (
    &amp;lt;form action={signOutAction}&amp;gt;
      &amp;lt;Button type=&quot;submit&quot;&amp;gt;로그아웃&amp;lt;/Button&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}

export default SignOut;&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;다음으로 admin에서 form 양식을 사용해서 server action을 활용해 본 예시를 들어보도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2558&quot; data-origin-height=&quot;914&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be7kJ1/btsITQFXfHr/pn7qTv1a9wK8HgP0owilXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be7kJ1/btsITQFXfHr/pn7qTv1a9wK8HgP0owilXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be7kJ1/btsITQFXfHr/pn7qTv1a9wK8HgP0owilXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe7kJ1%2FbtsITQFXfHr%2Fpn7qTv1a9wK8HgP0owilXK%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;196&quot; data-origin-width=&quot;2558&quot; data-origin-height=&quot;914&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;홈페이지에 다음과 같은 누적 지원자 수, 총 참가자 수, 총 프로젝트 수, 이탈자 수를 보여주는 섹션이 있다. 이 섹션은 매 기수 진행될 때마다 json파일 데이터를 수동으로 수정해서 직접 업데이트를 해주고 있었다.&lt;br /&gt;이 플로우를 내가 직접 수정하는 게 아닌 admin에서 운영자분들이 수정할 수 있게끔 form을 받아 server action 기능을 활용해 보았다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;현재 admin은 빠르게 PoC 정도로 디자인과 별개로 기능만 개발해 둔 점 참고 바란다.&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;아래 이미지는 admin에서 카드 섹션 데이터를 업데이트하는 페이지이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2294&quot; data-origin-height=&quot;990&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3XGVs/btsIS7axqQ0/VInR4gT5uAwD1TQBGSmy6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3XGVs/btsIS7axqQ0/VInR4gT5uAwD1TQBGSmy6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3XGVs/btsIS7axqQ0/VInR4gT5uAwD1TQBGSmy6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3XGVs%2FbtsIS7axqQ0%2FVInR4gT5uAwD1TQBGSmy6K%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;702&quot; height=&quot;303&quot; data-origin-width=&quot;2294&quot; data-origin-height=&quot;990&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이제 form 요소를 받아 server action 함수를 호출하는 플로우를 확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087976&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function totalCountStatusAction(formData: FormData) {
    'use server';

    const requestForm = {
      cumulativeApplicants: formData.get('cumulativeApplicants'),
      dropouts: formData.get('dropouts'),
      totalParticipants: formData.get('totalParticipants'),
      totalProjects: formData.get('totalProjects'),
    };

    const jsonString = JSON.stringify(requestForm);

    const requestBlob = new Blob([jsonString], { type: 'application/json' });

    await put('total_count_status.json', requestBlob);
}

function TotalCountStatusForm({ totalCountStatus }: Props) {
  const {
    cumulativeApplicants, dropouts, totalParticipants, totalProjects,
  } = totalCountStatus;

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form action={totalCountStatusAction}&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;cumulativeApplicants&quot; min={0} placeholder=&quot;누적 지원자 수&quot; defaultValue={cumulativeApplicants} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;totalParticipants&quot; min={0} placeholder=&quot;총 참가자 수&quot; defaultValue={totalParticipants} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;totalProjects&quot; min={0} placeholder=&quot;총 프로젝트 수&quot; defaultValue={totalProjects} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;dropouts&quot; min={0} placeholder=&quot;이탈자 수&quot; defaultValue={dropouts} required /&amp;gt;
        &amp;lt;Button type=&quot;submit&quot;&amp;gt;업데이트하기&amp;lt;/Button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 각 input의 입력된 name의 form 요소에 따라서 server action을 통해서 데이터를 처리할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzGRcJ/btsIThjMb5z/3JJR02qoXFCbrJPVELw6c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzGRcJ/btsIThjMb5z/3JJR02qoXFCbrJPVELw6c0/img.png&quot; data-alt=&quot;network tab에 post로 payload가 전달된 것을 확인할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzGRcJ/btsIThjMb5z/3JJR02qoXFCbrJPVELw6c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzGRcJ%2FbtsIThjMb5z%2F3JJR02qoXFCbrJPVELw6c0%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;584&quot; height=&quot;246&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;network tab에 post로 payload가 전달된 것을 확인할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;하지만 우리는 데이터를 처리하는 것뿐만 아니라 사용자한테도 업데이트에 성공했는지, 실패했는지를 알려줘야 한다. 사용자는 서버에서 비동기적으로 통신하는 api 상태를 알 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자에게 로딩 중인지, 성공했는지 혹은 실패했는지를 알려주자.&amp;nbsp;&lt;br /&gt;form 작업과 관련한 유용한 hook들이 생겼는데, useFormState(&lt;a href=&quot;https://react.dev/reference/react/useActionState&quot;&gt;useActionState&lt;/a&gt;),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://react.dev/reference/react-dom/hooks/useFormStatus&quot;&gt;useFormStatus&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;hook을 사용해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Progressive_enhancement&quot;&gt;progressive enhancement&lt;/a&gt;를 유지할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087978&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function TotalCountStatusForm({ initialTotalCountStatus }: Props) {
  const [state, formAction] = useFormState(totalCountStatusAction, null);

  const {
    cumulativeApplicants, dropouts, totalParticipants, totalProjects,
  } = initialTotalCountStatus;

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form action={formAction}&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;cumulativeApplicants&quot; min={0} placeholder=&quot;누적 지원자 수&quot; defaultValue={cumulativeApplicants} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;totalParticipants&quot; min={0} placeholder=&quot;총 참가자 수&quot; defaultValue={totalParticipants} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;totalProjects&quot; min={0} placeholder=&quot;총 프로젝트 수&quot; defaultValue={totalProjects} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;dropouts&quot; min={0} placeholder=&quot;이탈자 수&quot; defaultValue={dropouts} required /&amp;gt;
        &amp;lt;Button type=&quot;submit&quot;&amp;gt;업데이트하기&amp;lt;/Button&amp;gt;
      &amp;lt;/form&amp;gt;
      {state?.message &amp;amp;&amp;amp; (
        &amp;lt;div className={clsx(state.messageType &amp;amp;&amp;amp; styles[state.messageType])}&amp;gt;
          {state.message}
        &amp;lt;/div&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 useFormState를 통해서 에러 상태가 발생했는지, 성공했는지의 메시지를 사용자에게 즉각적으로 보여줄 수 있다.&lt;br /&gt;useFormState를 사용하면 prevState를 가지고 있기 때문에 server action도 수정이 필요하다. 그리고 사용자에게 보여줄 state를 return 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087982&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export async function totalCountStatusAction(
  prevState: TotalCountStatusStateType | null,
  formData: FormData,
): Promise&amp;lt;TotalCountStatusStateType&amp;gt; {
  try {
    const requestForm = {
      cumulativeApplicants: formData.get('cumulativeApplicants'),
      dropouts: formData.get('dropouts'),
      totalParticipants: formData.get('totalParticipants'),
      totalProjects: formData.get('totalProjects'),
    };

    const jsonString = JSON.stringify(requestForm);

    const requestBlob = new Blob([jsonString], { type: 'application/json' });

    await put('total_count_status.json', requestBlob);

    revalidatePath('/total-count-status');

    return {
      message: '수정사항이 반영되었습니다.',
      messageType: 'success',
    };
  } catch (error) {
    return {
      message: '수정사항이 반영되지 않았습니다. 잠시 후 다시 시도해주세요.',
      messageType: 'error',
    };
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 성공, 에러 상태를 바로 보여줄 수 있으며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://zod.dev/&quot;&gt;zod&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 라이브러리를 통해 유효성 검사도 처리할 수 있다.&lt;br /&gt;그리고 위 예시 코드를 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nextjs.org/docs/app/api-reference/functions/revalidatePath&quot;&gt;revalidatePath&lt;/a&gt;를 확인해 볼 수 있는데,&amp;nbsp;성공한 경우에는 캐시가 적용되어 있는 경우 바로 반영이 안 되는 경우가 있어 revalidatePath나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nextjs.org/docs/app/api-reference/functions/revalidateTag&quot;&gt;revalidateTag&lt;/a&gt;를 통해 캐시를 다시 검증할 수 있다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;참고로 react-dom의 useFormState는 form의 의존하는 것을 보다 보편적으로 action 함수에서 사용되는 네이밍으로 react 19에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://react.dev/reference/react/useActionState&quot;&gt;useActionState&lt;/a&gt;로 변경되었다.&lt;br /&gt;하지만, next.js(14.2.4 기준)에서는 useActionState로 변경한 경우 동작하지 않아, react-dom의 useFormState로 적용해야 한다.&lt;br /&gt;&lt;a href=&quot;https://github.com/vercel/next.js/issues/65673&quot;&gt;https://github.com/vercel/next.js/issues/65673&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 비동기적으로 동작하는 action 함수에 대한 form 로딩 상태를 useFormStatus를 사용해서 추가해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087984&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'use client';

function SubmitButton({ label }: Props) {
  const { pending } = useFormStatus();

  return (
    &amp;lt;Button type=&quot;submit&quot; disabled={pending}&amp;gt;{label}&amp;lt;/Button&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useFormStatus를 활용해서 pending상태를 확인할 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;주의할 점은 useFormStatus는 form 내부 context에 위치해야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722531087985&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function TotalCountStatusForm({ initialTotalCountStatus }: Props) {
  const [state, formAction] = useFormState(totalCountStatusAction, null);

  const {
    cumulativeApplicants, dropouts, totalParticipants, totalProjects,
  } = initialTotalCountStatus;

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form action={formAction}&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;cumulativeApplicants&quot; min={0} placeholder=&quot;누적 지원자 수&quot; defaultValue={cumulativeApplicants} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;totalParticipants&quot; min={0} placeholder=&quot;총 참가자 수&quot; defaultValue={totalParticipants} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;totalProjects&quot; min={0} placeholder=&quot;총 프로젝트 수&quot; defaultValue={totalProjects} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;dropouts&quot; min={0} placeholder=&quot;이탈자 수&quot; defaultValue={dropouts} required /&amp;gt;
        &amp;lt;SubmitButton label=&quot;업데이트하기&quot; /&amp;gt;
      &amp;lt;/form&amp;gt;
      {state?.message &amp;amp;&amp;amp; (
        &amp;lt;div className={clsx(state.messageType &amp;amp;&amp;amp; styles[state.messageType])}&amp;gt;
          {state.message}
        &amp;lt;/div&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 밖에서 action에 대한 로딩상태를 확인하고 싶으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://react.dev/reference/react/useTransition&quot;&gt;useTransition&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;hook을 사용하면 된다. pending 상태를 확인하고 싶은 함수에 startTransition을 감싸주면 된다. 이 둘의 또 다른 차이점은 useFormStatus는 form에 최적화되어 있어 form의 state도 확인이 가능하지만, useTransition은 form state를 확인할 수 없고 form의 pending 상태를 확인하기 위해서 startTransition 같은 콜백 함수를 감쌀 필요도 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087989&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function TotalCountStatusForm({ initialTotalCountStatus }: Props) {
  const [state, formAction] = useFormState(totalCountStatusAction, null);
  const [isPending, startTransition] = useTransition();

  const {
    cumulativeApplicants, dropouts, totalParticipants, totalProjects,
  } = initialTotalCountStatus;
  
  const handleSubmit = (formData: FormData) =&amp;gt; startTransition(() =&amp;gt; formAction(formData));

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form action={handleSubmit}&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;cumulativeApplicants&quot; min={0} placeholder=&quot;누적 지원자 수&quot; defaultValue={cumulativeApplicants} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;totalParticipants&quot; min={0} placeholder=&quot;총 참가자 수&quot; defaultValue={totalParticipants} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;totalProjects&quot; min={0} placeholder=&quot;총 프로젝트 수&quot; defaultValue={totalProjects} required /&amp;gt;
        &amp;lt;input type=&quot;number&quot; name=&quot;dropouts&quot; min={0} placeholder=&quot;이탈자 수&quot; defaultValue={dropouts} required /&amp;gt;
        &amp;lt;Button type=&quot;submit&quot; disabled={isPending}&amp;gt;업데이트하기&amp;lt;/Button&amp;gt;
      &amp;lt;/form&amp;gt;
      {state?.message &amp;amp;&amp;amp; (
        &amp;lt;div className={clsx(state.messageType &amp;amp;&amp;amp; styles[state.messageType])}&amp;gt;
          {state.message}
        &amp;lt;/div&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;화면 기록 2024-08-01.gif&quot; data-origin-width=&quot;1954&quot; data-origin-height=&quot;1094&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXOaRg/btsIRXT4xVj/BQrcuzWVzWf5SkHPZMiqJk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXOaRg/btsIRXT4xVj/BQrcuzWVzWf5SkHPZMiqJk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXOaRg/btsIRXT4xVj/BQrcuzWVzWf5SkHPZMiqJk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bXOaRg/btsIRXT4xVj/BQrcuzWVzWf5SkHPZMiqJk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;358&quot; data-filename=&quot;화면 기록 2024-08-01.gif&quot; data-origin-width=&quot;1954&quot; data-origin-height=&quot;1094&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;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;turborepo의 원격 캐시를 사용한 CI 속도 개선&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 CI를 원래 github actions에 캐시를 적용하여 lint와 test를 검증하였다. 캐시는 이미 적용되어있긴 해서 CI pipeline이 느린 편은 아니었다.&lt;br /&gt;실제로 캐시가 적용되어 있으면 아래와 같이 install, package build를&lt;span&gt;&amp;nbsp;&lt;/span&gt;건너뛰고 바로 lint와 test를 검증한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpyvYF/btsIScDuHA7/86fDokYR3FPfFoImtfD5RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpyvYF/btsIScDuHA7/86fDokYR3FPfFoImtfD5RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpyvYF/btsIScDuHA7/86fDokYR3FPfFoImtfD5RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpyvYF%2FbtsIScDuHA7%2F86fDokYR3FPfFoImtfD5RK%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;1908&quot; height=&quot;266&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2286&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L6Ftl/btsISM5CNCI/gklXkJLiok2lEK5AEe3BV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L6Ftl/btsISM5CNCI/gklXkJLiok2lEK5AEe3BV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L6Ftl/btsISM5CNCI/gklXkJLiok2lEK5AEe3BV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL6Ftl%2FbtsISM5CNCI%2FgklXkJLiok2lEK5AEe3BV1%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;2286&quot; height=&quot;238&quot; data-origin-width=&quot;2286&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;turborepo를 사용하게 되면서 원격 캐시 기능을 활용해보고 싶어서 이 과정도 build시에 같이 통합할 수 있지 않을까 싶어서 적용해 보았다. 사실 이건.. 뭐가 정답인지 모르겠고 굳이 안 해도 될 것 같았지만, turborepo의 원격 캐시를 활용하면 어떻게 될지 궁금해서 적용해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 전역에 turbo.json에 deploy시 실행되어야 할 각 의존성을 적용하면 turborepo가 똑똑하게 vercel에서 각 의존성에 맞춰서 실행이 된다. 예를 들어, 홈페이지(web)를 빌드를 하고 싶으면 packages 빌드가 먼저 일어나고 테스트 및 린트가 실행되고 모든 게 성공하면 build가 되어야 한다. 각 실행의 의존성을 turbo.json에 추가만 해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087993&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;tasks&quot;: {
  &quot;deploy&quot;: {
    &quot;dependsOn&quot;: [
      &quot;lint&quot;,
      &quot;stylelint&quot;,
      &quot;test:coverage&quot;,
      &quot;build&quot;
    ]
  },
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vercel에 배포 시 이 과정이 모두 성공해야지 배포가 이루어지고 만약에 중간에 실패했을 경우에도 이전에 성공한 pipeline에 대해서는 캐시가 반영이 된다. 변경사항에 대해서도 캐시 miss가 발생해야 하는 트리거를 turbo.json에 적용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087993&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &quot;test:coverage&quot;: {
      &quot;dependsOn&quot;: [&quot;@dnd-academy/ui#build&quot;, &quot;@dnd-academy/core#build&quot;], // 테스트는 다음 build에 의존되어있음. 즉, 먼저 실행되어야함.
      &quot;outputs&quot;: [&quot;coverage/**&quot;], // outputs 디렉토리의 변경사항을 추적하고 캐싱.
      &quot;inputs&quot;: [ // 테스트 파일만 확인한다.
        &quot;**/*.test.{tsx,ts}&quot;
      ]
    },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진의 홈페이지 빌드시 vercel의 빌드 로그를 보면, 6개의 작업이 성공했고 그중에 4가지의 작업에서 캐시가 발생한 것을 확인할 수 있다. 소요된 시간은 12초 정도 걸린 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1730&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zRWSA/btsIRmUmLEk/IDOTpoTugfhXePRnPIf5Nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zRWSA/btsIRmUmLEk/IDOTpoTugfhXePRnPIf5Nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zRWSA/btsIRmUmLEk/IDOTpoTugfhXePRnPIf5Nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzRWSA%2FbtsIRmUmLEk%2FIDOTpoTugfhXePRnPIf5Nk%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;1730&quot; height=&quot;236&quot; data-origin-width=&quot;1730&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;turborepo를 사용한 vercel deploy로그에서는 run summary를 통해 가시적으로 확인도 가능했고, 총 2분 12초의 소요시간이 걸리는 작업에 2분의 시간을 절약하고 12초가 걸린 것을 확인해 볼 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2828&quot; data-origin-height=&quot;1648&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9MKqe/btsITk1R4XT/mSWWHWCfJ2hG4pDtFfBAfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9MKqe/btsITk1R4XT/mSWWHWCfJ2hG4pDtFfBAfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9MKqe/btsITk1R4XT/mSWWHWCfJ2hG4pDtFfBAfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9MKqe%2FbtsITk1R4XT%2FmSWWHWCfJ2hG4pDtFfBAfk%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;784&quot; height=&quot;457&quot; data-origin-width=&quot;2828&quot; data-origin-height=&quot;1648&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;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;vercel에서 제공하는 유용한 친구들&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://vercel.com/docs/storage/vercel-blob&quot;&gt;vercel blob&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 이것저것 적용해 보면서 vercel에서 제공하고 있는 유용한 기능들을 사용해 봤다.&lt;br /&gt;특히 아직 베타 버전인 vercel blob이라는 기능을 사용해 봤는데 vercel blob은 amazon의 정적인 데이터를 관리하는 S3와 동일하게 storage 관리를 할 수 있는 vecel에서 제공해 주는 api이다. vercel blob은 일정 수준으로 사용하면 hobby 계정에서도 무료로 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;홈페이지에서는 단순히 api 호출을 통해서 데이터를 보여주는 용도로 사용하고 있고, admin에서는 이 데이터의 정보를 업데이트하기 위해서 vercel/blob의 api를 사용해서 수정 및 삭제를 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1722531087994&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'use server';

import { revalidatePath } from 'next/cache';

import { put } from '@vercel/blob';

export async function totalCountStatusAction(
  _: TotalCountStatusStateType | null,
  formData: FormData,
): Promise&amp;lt;TotalCountStatusStateType&amp;gt; {
    /* ... */

    await put('total_count_status.json', requestBlob, {
      access: 'public',
      token: process.env.DND_ACADEMY_V2_BLOB_READ_WRITE_TOKEN,
      addRandomSuffix: false,
    });
    
    /* ... */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vercel storage dashboard에서 blob 정보를 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;1774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjOvpO/btsITjon8so/OqslN2CA5WzYivgQBV9lck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjOvpO/btsITjon8so/OqslN2CA5WzYivgQBV9lck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjOvpO/btsITjon8so/OqslN2CA5WzYivgQBV9lck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjOvpO%2FbtsITjon8so%2FOqslN2CA5WzYivgQBV9lck%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;720&quot; height=&quot;501&quot; data-origin-width=&quot;2550&quot; data-origin-height=&quot;1774&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;size18&quot;&gt;&lt;a href=&quot;https://vercel.com/docs/analytics&quot;&gt;vercel analytics&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;amp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://vercel.com/docs/speed-insights&quot;&gt;speed insights&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글 애널리틱스와 web vitals와 같은 기능을 vercel에 자체적으로 제공해주고 있다. 이미 구글 애널리틱스를 적용해 두어서 굳이 써야 되나 싶어서&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이 두 개는 사용해보지는 않았다.&lt;br /&gt;적용하기가 매우 간단하고 hobby 계정에서도 일정 기능을 활용해서 사용이 가능해서 한 번쯤은 적용해볼까 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root layout에 아래와 같이 적용만 하면 끝이다.&lt;/p&gt;
&lt;pre id=&quot;code_1722512024387&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/next';


function RootLayout({ children }: { children: ReactNode }) {
  return (
    &amp;lt;html lang=&quot;ko&quot;&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;Analytics /&amp;gt;
        &amp;lt;SpeedInsights /&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 간편한 기능을 보면서&lt;span&gt;&amp;nbsp;&lt;/span&gt;느낀 점은&lt;span&gt;&amp;nbsp;&lt;/span&gt;모든 부분에서 vercel이 다&lt;span&gt;&amp;nbsp;&lt;/span&gt;해보려고&lt;span&gt;&amp;nbsp;&lt;/span&gt;하는구나.. 하는 생각이 들었다. 기존에 web vitals나 google analytics의 귀찮은 세팅과 적용을 쉽게 적용할 수&lt;span&gt;&amp;nbsp;&lt;/span&gt;있게 해둔걸&lt;span&gt;&amp;nbsp;&lt;/span&gt;보니 너무 편해지다가 vercel의 노예가 될 것 같은 느낌도 들었다.&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;아쉬웠던 점&lt;/h2&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;기존에 있던 디자인을 바탕으로 디자인 시스템을 정의했던 점&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 마이그레이션 작업은 혼자 작업을 했다. 디자인도 이전에 되어있던&lt;span&gt;&amp;nbsp;&lt;/span&gt;디자인이여서&amp;nbsp;디자인 시스템이 제대로 적용이 안되어있었다. 디자인을 수정을 요청하기에는 다들 바쁘셔서 어쩔 수 없이 임의적으로 정의하고 수정하게 되었다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;그러다 보니&lt;span&gt;&amp;nbsp;&lt;/span&gt;컴포넌트를 공통적으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt; 고민한 시간이 너무 많았고, 공통화를 시기키 위해서 props 명이나 스타일을 수정한 부분도 있었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 gap이나 padding 값도 spacing이 적용되어있지 않아서&lt;span&gt;&amp;nbsp;&lt;/span&gt;어려움 부분이&lt;span&gt;&amp;nbsp;&lt;/span&gt;있었고, 개별적으로 적용만 해두었고 추후에 공통화 작업이 필수적으로 이루어져야만 한다. 결국 일을&lt;span&gt;&amp;nbsp;&lt;/span&gt;두 번 하게 되는&lt;span&gt;&amp;nbsp;&lt;/span&gt;셈이긴 하지만&lt;span&gt;&amp;nbsp;&lt;/span&gt;현재 상황에서는 어쩔 수 없는 부분이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그나마&lt;span&gt;&amp;nbsp;&lt;/span&gt;몇몇 개의&lt;span&gt;&amp;nbsp;&lt;/span&gt;컴포넌트를 storybook에 공통화를&lt;span&gt;&amp;nbsp;&lt;/span&gt;시켜두긴 했지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;아직 덜 반영된 부분이 더 많아서 이 부분도 적용이 필요하다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://develop--65a52d0c9a3d8fefcfaccc65.chromatic.com/&quot;&gt;dnd-academy-v2 storybook&lt;/a&gt;&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;테스트 작성&lt;/h4&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;현재 유틸함수나 공통 컴포넌트들만 테스트를&lt;span&gt;&amp;nbsp;&lt;/span&gt;작성해 두었다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;결국 시간이라는 비용과 비례해서 효율적인 테스트 작성이 필요한데 앞으로 테스트 작성의 우선순위를&lt;span&gt;&amp;nbsp;&lt;/span&gt;정해 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;package/ui, core의 공통으로 사용되는 컴포넌트들이나 유틸함수에 누락된 테스트를 작성해야겠다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;오버 엔지니어링&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;개인적으로 회사에서 여러 프로젝트를&lt;span&gt;&amp;nbsp;&lt;/span&gt;진행해 보면서&lt;span&gt;&amp;nbsp;&lt;/span&gt;DND 홈페이지 마이그레이션은 작은 규모의 프로젝트이다. 근데 그 과정에서 너무 불필요하게 기술적으로 고민한 시간이 많았다는 점이 아쉬웠다. 물론 빠르게 기능만 개발했으면 더 금방 끝났을 마이그레이션임에 분명하지만 결국 내가 아니더라도 누군가는 계속 유지보수하기 위해서는 어느 정도의 기술적인 고민은 필요했다고 생각했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이런 고민의 과정을&lt;span&gt;&amp;nbsp;&lt;/span&gt;오랫동안&lt;span&gt;&amp;nbsp;&lt;/span&gt;한 것 같아서 오버 엔지니어링이 된 것 같지만 이번에 마이그레이션을 진행하면서 새로운 것들을&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용해 보고&lt;span&gt;&amp;nbsp;&lt;/span&gt;적용해 보며&lt;span&gt;&amp;nbsp;&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;마이그레이션&lt;span&gt;&amp;nbsp;&lt;/span&gt;과정기를&lt;span&gt;&amp;nbsp;&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;1938&quot; data-origin-height=&quot;1800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7dZDR/btsIOk91phW/RfnfLYdW72hjxKFFEo8Kbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7dZDR/btsIOk91phW/RfnfLYdW72hjxKFFEo8Kbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7dZDR/btsIOk91phW/RfnfLYdW72hjxKFFEo8Kbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7dZDR%2FbtsIOk91phW%2FRfnfLYdW72hjxKFFEo8Kbk%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;687&quot; height=&quot;638&quot; data-origin-width=&quot;1938&quot; data-origin-height=&quot;1800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 lighthouse 지표에 Best Practices의 점수가 높지 않은 이유는 dnd에 문의 사항 창구로 채널톡을 사용하고 있는데&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://web.dev/articles/cookie-notice-best-practices?hl=ko&quot;&gt;best practices 항목에 서드 파티 쿠키&lt;/a&gt;의 영향을 받아서 그런 것 같다. 이 부분은 내가&lt;span&gt;&amp;nbsp;&lt;/span&gt;고려해야 할&lt;span&gt;&amp;nbsp;&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;1680&quot; data-origin-height=&quot;718&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkHfX4/btsISIWssDp/YkbJJ5iFN0G1RamlaYiwY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkHfX4/btsISIWssDp/YkbJJ5iFN0G1RamlaYiwY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkHfX4/btsISIWssDp/YkbJJ5iFN0G1RamlaYiwY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkHfX4%2FbtsISIWssDp%2FYkbJJ5iFN0G1RamlaYiwY1%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;680&quot; height=&quot;291&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;718&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;전체적으로 보았을 때 만족스러운 마이그레이션이었다. 아직 admin 작업이 꽤 많이&lt;span&gt;&amp;nbsp;&lt;/span&gt;남긴 했지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;홈페이지를&lt;span&gt;&amp;nbsp;&lt;/span&gt;마이그레이션 한&lt;span&gt;&amp;nbsp;&lt;/span&gt;이 시점에 회고를 쓰기에는 매우 적합했던 것 같다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;기존에 관리되고 있던 비공개 repository도 archive처리를 해두었고, 현재&lt;span&gt;&amp;nbsp;&lt;/span&gt;마이그레이션 된&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/DNDACADEMY/dnd-academy-v2&quot;&gt;repository&lt;/a&gt;는 깃허브에 공개하는 방향으로 변경하였다. 그리고 홈페이지 성능도 향상되었고 각 페이지별로 디테일 수정 및&lt;span&gt;&amp;nbsp;&lt;/span&gt;개선해야 할&lt;span&gt;&amp;nbsp;&lt;/span&gt;포인트도 한꺼번에 적용할 수 있게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이전부터 요청사항이&lt;span&gt;&amp;nbsp;&lt;/span&gt;있을 때마다&lt;span&gt;&amp;nbsp;&lt;/span&gt;수동으로 하는 과정이 너무 귀찮고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;새로운 개발을&lt;span&gt;&amp;nbsp;&lt;/span&gt;해야 할&lt;span&gt;&amp;nbsp;&lt;/span&gt;때에는 개발조차 불가능했었는데 이번 기회에 마이그레이션을 통해서 앞으로의 발판과 성능개선을 해서 뿌듯하다. admin은 아직 개발이 남았지만 이 작업을 마무리하면 귀찮게 수동으로 데이터를 업데이트하는 작업은 내가&lt;span&gt;&amp;nbsp;&lt;/span&gt;안 해도&lt;span&gt;&amp;nbsp;&lt;/span&gt;된다..!! ㅠ&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;두&lt;span&gt;&amp;nbsp;&lt;/span&gt;달간 vercel을&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용해 봤는데&lt;span&gt;&amp;nbsp;&lt;/span&gt;무료로 계속 유지되고 있어서 비용적인 부분도 확실히 해소가 되었다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DND에서 참여자로 시작해 운영진으로 활동하면서 DND에 대한 동아리의 애정이 생기게 되었고, 홈페이지를&lt;span&gt;&amp;nbsp;&lt;/span&gt;마이그레이션 한&lt;span&gt;&amp;nbsp;&lt;/span&gt;이유도 이 애정 중 하나가&lt;span&gt;&amp;nbsp;&lt;/span&gt;아닐까 싶다.&lt;br /&gt;DND는 비영리단체여서 운영진활동을 해서 얻는 것도 없어서 왜&lt;span&gt;&amp;nbsp;&lt;/span&gt;그런 걸&lt;span&gt;&amp;nbsp;&lt;/span&gt;하냐고 물어보는 사람들이 많기도 하지만, 매 기수 참여자분들에게 긍정적인 영향을 끼치고 함께 자라는 환경을 만들어주는 과정에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;나는&lt;span&gt;&amp;nbsp;&lt;/span&gt;보람을 느끼는 것 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;나를 포함한 DND 운영진분들은 1년에 두 기수씩 참여하는 모든 분들에게 더 나은 환경을 제공하기 위해 8주 간의 유의미한 DND 활동을 위해서 많은 노력을&lt;span&gt;&amp;nbsp;&lt;/span&gt;하고 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;지금도 이미 많은 지원자들이 DND에 지원하고 있지만, 앞으로도 더 많은 관심과&lt;span&gt;&amp;nbsp;&lt;/span&gt;지원 바란다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;쓰다 보니&lt;span&gt;&amp;nbsp;&lt;/span&gt;모든 내용을 담고 싶어서 너무 길어진 것 같다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;마지막으로  DND는 참여자분들에게 따로 각 기수별 참여비도 받지 않고 있고 참여하는 8주간의 정규 과정은 전부 무료로 운영하고 있습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;그렇다 보니&lt;span&gt;&amp;nbsp;&lt;/span&gt;참여자분들에게 더 좋은 환경과 유의미한 활동을&lt;span&gt;&amp;nbsp;&lt;/span&gt;드리는 데에&lt;span&gt;&amp;nbsp;&lt;/span&gt;있어서&lt;span&gt;&amp;nbsp;&lt;/span&gt;지원하는 데에&lt;span&gt;&amp;nbsp;&lt;/span&gt;한계가 있을&lt;span&gt;&amp;nbsp;&lt;/span&gt;수밖에&lt;span&gt;&amp;nbsp;&lt;/span&gt;없습니다. (오프라인 장소 대관, 서버비, 행사 운영비) 이런 부분을 해소하기 위해서는 후원이 필요한데 요즘은 업계 시장이 좋지 않아서&lt;span&gt;&amp;nbsp;&lt;/span&gt;후원해 주는&lt;span&gt;&amp;nbsp;&lt;/span&gt;곳이&lt;span&gt;&amp;nbsp;&lt;/span&gt;한 군데도&lt;span&gt;&amp;nbsp;&lt;/span&gt;없어 DND 운영도 어려움이 큰 것 같습니다. 혹시나 하는 마음으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;mailto:http://contact.dnd.ac@gmail.com&quot;&gt;후원&lt;span&gt;&amp;nbsp;&lt;/span&gt;문의&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;주시면&lt;span&gt;&amp;nbsp;&lt;/span&gt;너무나 감사하겠습니다.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1722513674824&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - DNDACADEMY/dnd-academy-v2: DND는 개발자와 디자이너라면 누구나 참여할 수 있는 IT비영리단체입&quot; data-og-description=&quot;DND는 개발자와 디자이너라면 누구나 참여할 수 있는 IT비영리단체입니다. Contribute to DNDACADEMY/dnd-academy-v2 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/DNDACADEMY/dnd-academy-v2&quot; data-og-url=&quot;https://github.com/DNDACADEMY/dnd-academy-v2&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/SsCmd/hyWGQ09EPF/QNp4PxycvvxKxjJ0T8k9jK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/DNDACADEMY/dnd-academy-v2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/DNDACADEMY/dnd-academy-v2&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/SsCmd/hyWGQ09EPF/QNp4PxycvvxKxjJ0T8k9jK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - DNDACADEMY/dnd-academy-v2: DND는 개발자와 디자이너라면 누구나 참여할 수 있는 IT비영리단체입&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;DND는 개발자와 디자이너라면 누구나 참여할 수 있는 IT비영리단체입니다. Contribute to DNDACADEMY/dnd-academy-v2 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1722515148638&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;DND - 프로젝트에 즐거움을 모두에게 기회를&quot; data-og-description=&quot;DND는 개발자와 디자이너라면 누구나 참여할 수 있는 IT비영리단체입니다.&quot; data-og-host=&quot;dnd.ac&quot; data-og-source-url=&quot;http://dnd.ac&quot; data-og-url=&quot;https://www.dnd.ac&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/07Lpf/hyWKCNF0IV/EtqfOqCuhLlkQOKtSgIhCk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/mZ1LS/hyWG1avnMY/2TC213zSZU4kEqZJALDr01/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/c04yLm/hyWKy5z440/MazkUkVZkKXNmQCo9N3ghk/img.png?width=1156&amp;amp;height=464&amp;amp;face=0_0_1156_464&quot;&gt;&lt;a href=&quot;http://dnd.ac&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://dnd.ac&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/07Lpf/hyWKCNF0IV/EtqfOqCuhLlkQOKtSgIhCk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/mZ1LS/hyWG1avnMY/2TC213zSZU4kEqZJALDr01/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/c04yLm/hyWKy5z440/MazkUkVZkKXNmQCo9N3ghk/img.png?width=1156&amp;amp;height=464&amp;amp;face=0_0_1156_464');&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;DND - 프로젝트에 즐거움을 모두에게 기회를&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;DND는 개발자와 디자이너라면 누구나 참여할 수 있는 IT비영리단체입니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dnd.ac&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>DnD</category>
      <category>운영진</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/41</guid>
      <comments>https://haranglog.tistory.com/41#entry41comment</comments>
      <pubDate>Fri, 2 Aug 2024 02:08:26 +0900</pubDate>
    </item>
    <item>
      <title>[회고] IT 커뮤니티 동아리 SIPE 2기를 마무리하며...</title>
      <link>https://haranglog.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;3월부터 무려 4개월 동안 활동해 왔던 &lt;a href=&quot;https://sipe.team/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;IT 커뮤니티 동아리 SIPE&lt;/a&gt; 2기 활동을 마무리했다. 이번 SIPE 2기 활동이 꽤나 알차게 많은 활동들을 해온 것 같고 개발자로서 또 다른 동기부여를 얻을 수 있는 의미 있던 시간이어서 2기 활동을 회고로 마무리해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 SIPE가 다른 IT 동아리와 차별화된 느낌을 받았던 부분이 기존에 있던 동아리 같은 경우에는 어떤 주제 또는 프로젝트라는 &quot;성장&quot;또는 &quot;개발자로서 해야 하는 무언가&quot; 정도의 포인트에 집중에 있던 반면, SIPE는 &quot;네트워크&quot;를 1순위에 집중하고 있던 점이 굉장히 신선하게 다가왔던 것 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1회 차: OT&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IT 업계의 많은 사람들과 만날 수 있던 SIPE 2기의 첫 번째 시간으로 오리엔테이션을 진행했었다.&lt;br /&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/bxwEGT/btsIwuRrbQR/g6BD3dlrnLsiJwKoGfpWc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxwEGT/btsIwuRrbQR/g6BD3dlrnLsiJwKoGfpWc1/img.png&quot; data-origin-width=&quot;2186&quot; data-origin-height=&quot;1562&quot; data-is-animation=&quot;false&quot; data-filename=&quot;edited_DSC00139.JPG&quot; style=&quot;width: 37.9689%; margin-right: 10px;&quot; data-widthpercent=&quot;38.87&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxwEGT/btsIwuRrbQR/g6BD3dlrnLsiJwKoGfpWc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxwEGT%2FbtsIwuRrbQR%2Fg6BD3dlrnLsiJwKoGfpWc1%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;2186&quot; height=&quot;1562&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSQbaC/btsIwyTKaRD/8fNEJGgXocAAJ4SyQ0mkFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSQbaC/btsIwyTKaRD/8fNEJGgXocAAJ4SyQ0mkFk/img.png&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2068&quot; data-is-animation=&quot;false&quot; data-filename=&quot;edited_IMG_5585.JPG&quot; width=&quot;666&quot; height=&quot;459&quot; style=&quot;width: 39.3577%; margin-right: 10px;&quot; data-widthpercent=&quot;40.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSQbaC/btsIwyTKaRD/8fNEJGgXocAAJ4SyQ0mkFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSQbaC%2FbtsIwyTKaRD%2F8fNEJGgXocAAJ4SyQ0mkFk%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;3000&quot; height=&quot;2068&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cev8QP/btsIvsfNsjW/KAdfBSZkoOcDm1Ob8VatKk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cev8QP/btsIvsfNsjW/KAdfBSZkoOcDm1Ob8VatKk/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_5592.JPG&quot; width=&quot;505&quot; height=&quot;673&quot; style=&quot;width: 20.3479%;&quot; data-widthpercent=&quot;20.84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cev8QP/btsIvsfNsjW/KAdfBSZkoOcDm1Ob8VatKk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcev8QP%2FbtsIvsfNsjW%2FKAdfBSZkoOcDm1Ob8VatKk%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;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;정말 다양항 개발자분들을 만날 수 있던 자리였고 앞으로의 활동이 기대되는 그런 시간이었다.&lt;br /&gt;가벼운 레크리에이션을 시작으로 여러 사람들과 짧지만 많은 얘기들을 나눌 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OT때 느꼈던 점으로는 행사를 준비하기 위해 많은 고민과 시간을 투자했을 것 같은 운영진분들의 열정과 후원사의 여부였다. DND라는 동아리를 운영하는 입장에서 굉장히 부러웠던 포인트였다. 인프런에서 물과 강의 할인 쿠폰, 엘리스에서 장소 대관 등. 요즘 같은 불경기에 컨택하기도 힘들었을 텐데 후원을 받아낸 2기 운영진분들이 대단한..&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2회 차: 사이프챗 feat. Rust 붐은 온다...&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이프에는 정규적으로 진행되는 1차, 2차 2번의 미션이 있다.&lt;br /&gt;만들고 싶은 프로젝트나 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;하고 싶은 것들을 주제로 내서 해당 주제에 2기 분들과 같이 약 8주 동안 미션을 하는데, 그 첫 번째로 사이프챗이란 오프라인 세션을 진행하면서 팀별로 각 주제를 어떻게 진행할 것 인지에 대해서 발표하는 시간이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이번 오프라인 장소는 우아한 형제들에서 진행됐었고, 내가 선택했던 미션으로는 기술 서적 스터디, 프론트엔드 devOps 스터디, 면접 준비 스터디, 생산형 AI를 통해 콘텐츠 생성하기 등 굉장히 다양한 주제들 중 러스트 스터디를 선택했다.&lt;br /&gt;요즘 프론트엔드 쪽에서도 그렇고 전반적인 업계에서 굉장히 핫하게 떠오르고 있는 언어인 것 같아서 배우고 싶었다. 마침 딱 러스트를 학습할 수 있는 주제가 있어서 고민하지 않고 바로 선택하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_IMG 9971.JPG&quot; data-origin-width=&quot;2259&quot; data-origin-height=&quot;2000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAkPRD/btsIvcYA1zB/QYpudU2xgs1oLEgyk6WhK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAkPRD/btsIvcYA1zB/QYpudU2xgs1oLEgyk6WhK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAkPRD/btsIvcYA1zB/QYpudU2xgs1oLEgyk6WhK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAkPRD%2FbtsIvcYA1zB%2FQYpudU2xgs1oLEgyk6WhK1%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;539&quot; height=&quot;477&quot; data-filename=&quot;edited_IMG 9971.JPG&quot; data-origin-width=&quot;2259&quot; data-origin-height=&quot;2000&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: #333333; text-align: start;&quot;&gt;개인적인 목표로는 프론트엔드에서 트랜스파일러, 컴파일러를 러스트를 사용해 빌드 성능 개선한 SWC 같은 프로젝트처럼 javascript로 되어있는 스크립트를 러스트&lt;span style=&quot;color: #333333; 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://github.com/rust-lang/rustlings&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;rustlings&lt;/a&gt;의 연습문제들을 매주 자유 형식으로 공부한 내용, 공부하면서 느낀 점 등 자유롭게 정리하여 repository에 올리고, 궁금했던 점들은 스터디를 통해 다시 학습하는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 2회 차도 뒤풀이와 함께 마무리!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0136.JPG&quot; data-origin-width=&quot;5184&quot; data-origin-height=&quot;3456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SdQDH/btsIvyG4pxf/jDgF7KwUFvD0PU8Zk74thK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SdQDH/btsIvyG4pxf/jDgF7KwUFvD0PU8Zk74thK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SdQDH/btsIvyG4pxf/jDgF7KwUFvD0PU8Zk74thK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSdQDH%2FbtsIvyG4pxf%2FjDgF7KwUFvD0PU8Zk74thK%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;572&quot; height=&quot;381&quot; data-filename=&quot;IMG_0136.JPG&quot; data-origin-width=&quot;5184&quot; data-origin-height=&quot;3456&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;h3 data-ke-size=&quot;size23&quot;&gt;3회 차: 사이데이션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 미션을 같이할 팀원들과 어떻게 미션을 진행할지 정하고, 3회 차에서는 1회 차 미션을 진행할 팀원들과 함께 즐거운 시간을 보내는 사이데이션을 진행했다. 사이데이션을 진행하면서 미션보드를 통해 가장 많이 진행한 팀한테 상품도 주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_2947.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbTi1N/btsIvuEH7uT/5PUMNoAWMHNxckNQvjANv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbTi1N/btsIvuEH7uT/5PUMNoAWMHNxckNQvjANv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbTi1N/btsIvuEH7uT/5PUMNoAWMHNxckNQvjANv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbTi1N%2FbtsIvuEH7uT%2F5PUMNoAWMHNxckNQvjANv0%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;406&quot; height=&quot;406&quot; data-filename=&quot;IMG_2947.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날씨가 좋아서 노들로 나들이를 갔었고, 이후 카페로 이동해서 전반적인 IT 업계에 대한 얘기를 대체적으로 많이 했던 것 같다.&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/bmK9ca/btsIu2aWZac/Dt3Li2eQGC5BW6kNWvnGZ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmK9ca/btsIu2aWZac/Dt3Li2eQGC5BW6kNWvnGZ1/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG 4917.jpg&quot; width=&quot;371&quot; height=&quot;278&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmK9ca/btsIu2aWZac/Dt3Li2eQGC5BW6kNWvnGZ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmK9ca%2FbtsIu2aWZac%2FDt3Li2eQGC5BW6kNWvnGZ1%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/AsRPS/btsIu2aWYHA/oebLq2URtOEFkifcY350kK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AsRPS/btsIu2aWYHA/oebLq2URtOEFkifcY350kK/img.jpg&quot; data-origin-width=&quot;3648&quot; data-origin-height=&quot;2736&quot; data-is-animation=&quot;false&quot; data-filename=&quot;dji_mimo_20240413_134614_20240413134614_1712983732714_photo.jpg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AsRPS/btsIu2aWYHA/oebLq2URtOEFkifcY350kK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAsRPS%2FbtsIu2aWYHA%2FoebLq2URtOEFkifcY350kK%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;3648&quot; height=&quot;2736&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7in9g/btsIvQ8Cjrg/NSScIj9D3s44Z7Y666Qn1k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7in9g/btsIvQ8Cjrg/NSScIj9D3s44Z7Y666Qn1k/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_7333.jpg&quot; width=&quot;579&quot; height=&quot;434&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7in9g/btsIvQ8Cjrg/NSScIj9D3s44Z7Y666Qn1k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7in9g%2FbtsIvQ8Cjrg%2FNSScIj9D3s44Z7Y666Qn1k%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;h3 data-ke-size=&quot;size23&quot;&gt;4회 차: 사담콘&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사담콘이란 사이프 내부 사람들끼리 지식이나 경험을 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;컨퍼런스 형태로 발표하는 것으로,&lt;/span&gt;&amp;nbsp;이번 2기에서는 &quot;우리들의 성장통&quot;이라는 주제로 겪었던 경험을 공유하는 자리였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서울 시민청에서 태평홀에서 진행되었었고, 외부 사람들도 &lt;a href=&quot;https://festa.io/events/5084&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;페스타&lt;/a&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/q7IVF/btsIy0DWW8V/vkQZI4CISSENV0xTnemOWK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q7IVF/btsIy0DWW8V/vkQZI4CISSENV0xTnemOWK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_6080.JPG&quot; width=&quot;543&quot; height=&quot;724&quot; style=&quot;width: 21.4407%; margin-right: 10px;&quot; data-widthpercent=&quot;21.95&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q7IVF/btsIy0DWW8V/vkQZI4CISSENV0xTnemOWK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq7IVF%2FbtsIy0DWW8V%2FvkQZI4CISSENV0xTnemOWK%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;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbACUZ/btsIzwP0PJD/ySZbcqNEBKHmieFdvekzS1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbACUZ/btsIzwP0PJD/ySZbcqNEBKHmieFdvekzS1/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_6081 2.JPG&quot; style=&quot;width: 38.1168%; margin-right: 10px;&quot; data-widthpercent=&quot;39.02&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbACUZ/btsIzwP0PJD/ySZbcqNEBKHmieFdvekzS1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbACUZ%2FbtsIzwP0PJD%2FySZbcqNEBKHmieFdvekzS1%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/baUC9L/btsIBhDZsF1/VGuvBFi57E4pZ10CVZv4K0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baUC9L/btsIBhDZsF1/VGuvBFi57E4pZ10CVZv4K0/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_6089.jpg&quot; style=&quot;width: 38.1168%;&quot; data-widthpercent=&quot;39.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baUC9L/btsIBhDZsF1/VGuvBFi57E4pZ10CVZv4K0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaUC9L%2FbtsIBhDZsF1%2FVGuvBFi57E4pZ10CVZv4K0%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;여러 다양한 분들의 경험들을 들으며 인사이트와 좋은 경험담들을 들을 수 있었고, 그러한 발표들을 들으니 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;또 다른&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;동기부여를 받았다.&amp;nbsp;&lt;br /&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/bOdSyi/btsIAw2NauQ/tWZsRT5C6qbqaaEo9kLQ3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOdSyi/btsIAw2NauQ/tWZsRT5C6qbqaaEo9kLQ3K/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_6085 2.JPG&quot; width=&quot;551&quot; height=&quot;413&quot; data-widthpercent=&quot;47.06&quot; style=&quot;width: 46.5116%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOdSyi/btsIAw2NauQ/tWZsRT5C6qbqaaEo9kLQ3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOdSyi%2FbtsIAw2NauQ%2FtWZsRT5C6qbqaaEo9kLQ3K%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/cy8mx8/btsIzx2oien/C9SmrTQhILktLAFNIklDQK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cy8mx8/btsIzx2oien/C9SmrTQhILktLAFNIklDQK/img.jpg&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot; data-is-animation=&quot;false&quot; data-filename=&quot;4th session photo.JPG&quot; style=&quot;width: 52.3256%;&quot; data-widthpercent=&quot;52.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cy8mx8/btsIzx2oien/C9SmrTQhILktLAFNIklDQK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcy8mx8%2FbtsIzx2oien%2FC9SmrTQhILktLAFNIklDQK%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;6960&quot; height=&quot;4640&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;러스트팀 기준님의 발표 중.. 러스트 홍보까지.. 완벽..&lt;br /&gt;알찬 사담콘도 뒤풀이로 마무리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5회 차: 1차 미션 발표&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차 미션 발표는 2회 차 때 정한 팀 미션을 얼마나 잘 진행했는지 그동안 했던 과정을 각 팀별로 발표하는 자리이다.&lt;br /&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/ee8tfm/btsIzoYDUFT/G1j3zoUP1bBPm4Tu4A84Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ee8tfm/btsIzoYDUFT/G1j3zoUP1bBPm4Tu4A84Rk/img.png&quot; data-origin-width=&quot;1859&quot; data-origin-height=&quot;2250&quot; data-is-animation=&quot;false&quot; data-filename=&quot;edited_IMG_6180.JPG&quot; data-widthpercent=&quot;52.42&quot; style=&quot;width: 51.8084%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ee8tfm/btsIzoYDUFT/G1j3zoUP1bBPm4Tu4A84Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fee8tfm%2FbtsIzoYDUFT%2FG1j3zoUP1bBPm4Tu4A84Rk%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;1859&quot; height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qk0oz/btsIBbcNsbd/qgOmUicLrxguO6B5WLwrEK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qk0oz/btsIBbcNsbd/qgOmUicLrxguO6B5WLwrEK/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_6183.jpg&quot; style=&quot;width: 47.0288%;&quot; data-widthpercent=&quot;47.58&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qk0oz/btsIBbcNsbd/qgOmUicLrxguO6B5WLwrEK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQk0oz%2FbtsIBbcNsbd%2FqgOmUicLrxguO6B5WLwrEK%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;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rust팀에서는 각 주차별로 rustlings의 연습문제를 풀어보고 &lt;a href=&quot;https://github.com/sipe-team/2-1_rust&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃허브&lt;/a&gt;에 TIL과 공부한 내용을 기록하여 올려 인증하는 방식이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;1796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBOC1Z/btsIy4lPU5F/JO9l1TnFLPhP0OxtZUblNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBOC1Z/btsIy4lPU5F/JO9l1TnFLPhP0OxtZUblNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBOC1Z/btsIy4lPU5F/JO9l1TnFLPhP0OxtZUblNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBOC1Z%2FbtsIy4lPU5F%2FJO9l1TnFLPhP0OxtZUblNK%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;479&quot; height=&quot;463&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;1796&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1721031516553&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - sipe-team/2-1_rust: Rust를 부시고 백악관에 채용당합니다&quot; data-og-description=&quot;Rust를 부시고 백악관에 채용당합니다. Contribute to sipe-team/2-1_rust development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/sipe-team/2-1_rust&quot; data-og-url=&quot;https://github.com/sipe-team/2-1_rust&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xmJm0/hyWzxUDE8P/ZDbdnKn8QRrfZMhAdig011/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/sipe-team/2-1_rust&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/sipe-team/2-1_rust&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xmJm0/hyWzxUDE8P/ZDbdnKn8QRrfZMhAdig011/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - sipe-team/2-1_rust: Rust를 부시고 백악관에 채용당합니다&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Rust를 부시고 백악관에 채용당합니다. Contribute to sipe-team/2-1_rust development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에 rust의 개념에 어려운 부분이 있었고, 무작정 연습문제를 푸는 게 좋은 건지에 대한 의문도 있어서 마무리는 각자 정한 파트를 공부한 내용을 세미나 형식으로 발표도 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 rust팀의 팀장 헌겸님의 기깔난 PPT와 함께 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;완벽한 발표를 마쳤고, 개인적으로 1등을 확신했었다.&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;IMG_6186.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4dP5d/btsIzb6pwun/esYkWptLIhKsPK7e5H41UK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4dP5d/btsIzb6pwun/esYkWptLIhKsPK7e5H41UK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4dP5d/btsIzb6pwun/esYkWptLIhKsPK7e5H41UK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4dP5d%2FbtsIzb6pwun%2FesYkWptLIhKsPK7e5H41UK%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;465&quot; height=&quot;349&quot; data-filename=&quot;IMG_6186.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시나 1등으로 1차 미션을 마무리할 수 있었고, 회식비도 5만원을 지원받을 수 있었다.&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/7Umiq/btsIzxVHu7n/tkADskTxkStcgeKxVAvrk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Umiq/btsIzxVHu7n/tkADskTxkStcgeKxVAvrk1/img.png&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;1999&quot; data-is-animation=&quot;false&quot; data-filename=&quot;edited_5회차.JPG&quot; data-widthpercent=&quot;34.99&quot; style=&quot;width: 34.585%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Umiq/btsIzxVHu7n/tkADskTxkStcgeKxVAvrk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7Umiq%2FbtsIzxVHu7n%2FtkADskTxkStcgeKxVAvrk1%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;1614&quot; height=&quot;1999&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mojac/btsIzAESAbL/SLcBXH8zkq0yJ2ao5FAbS1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mojac/btsIzAESAbL/SLcBXH8zkq0yJ2ao5FAbS1/img.jpg&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot; data-is-animation=&quot;false&quot; data-filename=&quot;5회차 (1).JPG&quot; style=&quot;width: 64.2522%;&quot; data-widthpercent=&quot;65.01&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mojac/btsIzAESAbL/SLcBXH8zkq0yJ2ao5FAbS1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMojac%2FbtsIzAESAbL%2FSLcBXH8zkq0yJ2ao5FAbS1%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;6960&quot; height=&quot;4640&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;h3 data-ke-size=&quot;size23&quot;&gt;6회 차: 사이프톤&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이프톤은 사이프 + 해커톤의 줄임말로 참여하신 분들과 함께 무박 2일 동안 해커톤을 진행하는 세션이다. 사이프톤 세선 당일날에는 성수 엘리스랩에서 행사가 진행됐다.&lt;br /&gt;먼저 사전에 슬랙을 통해 하고 싶은 주제를 선착순으로 정했고, 나는 오랜만에 차트를 만들어보고 싶어서 트레이딩과 관련한 주제를 선택했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2076&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uenaC/btsIzKN14yC/biXJhVUCmeHUTGneUySoW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uenaC/btsIzKN14yC/biXJhVUCmeHUTGneUySoW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uenaC/btsIzKN14yC/biXJhVUCmeHUTGneUySoW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuenaC%2FbtsIzKN14yC%2FbiXJhVUCmeHUTGneUySoW1%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;2076&quot; height=&quot;252&quot; data-origin-width=&quot;2076&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주제는 세부적으로 사전 논의를 통해 더 디테일하게 정해졌고, 코인과 관련된 전략 테스트 결과 Dashboard 제공해 주어 더 나아가 낙폭 과대에 따른 시세차익을 내기 위한 주제로 정했다. 개인적으로 코인을 해본 적은 없어 도메인 지식이 부족해 이해하는데 시간이 걸렸지만, 해야 할 게 명확했기 때문에, 문제는 없었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3286&quot; data-origin-height=&quot;1632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zMLys/btsIAv30ZqM/2FoseOjur0HBYYrQcrzFeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zMLys/btsIAv30ZqM/2FoseOjur0HBYYrQcrzFeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zMLys/btsIAv30ZqM/2FoseOjur0HBYYrQcrzFeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzMLys%2FbtsIAv30ZqM%2F2FoseOjur0HBYYrQcrzFeK%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;3286&quot; height=&quot;1632&quot; data-origin-width=&quot;3286&quot; data-origin-height=&quot;1632&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&gt;비트코인 살걸 생각만 하다가 현실로 옮기지 못한 우리의 모습을 반영하기 위해서 이런 팀 이름으로 정하게 되었다.&lt;/span&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/ctt05y/btsIBAwwh3O/sk7FMVzxJF6lhdKkeHOMnK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctt05y/btsIBAwwh3O/sk7FMVzxJF6lhdKkeHOMnK/img.jpg&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot; data-is-animation=&quot;false&quot; data-filename=&quot;6회차 Photo (1).JPG&quot; width=&quot;452&quot; height=&quot;301&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/ctt05y/btsIBAwwh3O/sk7FMVzxJF6lhdKkeHOMnK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fctt05y%2FbtsIBAwwh3O%2Fsk7FMVzxJF6lhdKkeHOMnK%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;6960&quot; height=&quot;4640&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SXsxB/btsIyZFdRCH/JSEqmAdcJu44Kh41BlMpm0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SXsxB/btsIyZFdRCH/JSEqmAdcJu44Kh41BlMpm0/img.jpg&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot; data-is-animation=&quot;false&quot; data-filename=&quot;6회차 313A9835.JPG&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SXsxB/btsIyZFdRCH/JSEqmAdcJu44Kh41BlMpm0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSXsxB%2FbtsIyZFdRCH%2FJSEqmAdcJu44Kh41BlMpm0%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;6960&quot; height=&quot;4640&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;해커톤 타임테이블, 방명록 &amp;amp; 맛있는 야식과 간식들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;디자이너를 외부에서 초청해서 지원받을 수 있었지만, 그냥 디자이너 없이 해보자 해서 디자이너 없는 해커톤을 진행하게 되었다. (하지만, 역시나 해커톤은 디자이너가 중요하다는 사실... 그냥 지원받을걸..)&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;디자이너 없이 디자인을 할 수 없는 FE 두 명이서 vercel에서 만든 AI&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://v0.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;v0.dev&lt;/a&gt;의 도움을 받아 가이드를 대충 그릴 수 있었고, 빠르게 개발하기 위해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://nextui.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;next ui&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;tailwind css, framer-motion 기반 UI 라이브러리를 사용하여 개발을 진행했다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;생각했던 것보다 next ui 개발 경험이 좋았고, 간편했고, 무엇보다 UI 라이브러리 치고 이뻐서 맘에 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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/cAppiI/btsIzn6CPbr/mBUwyea5qJnZ3Fdxtwy1Vk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAppiI/btsIzn6CPbr/mBUwyea5qJnZ3Fdxtwy1Vk/img.jpg&quot; data-origin-width=&quot;5184&quot; data-origin-height=&quot;3456&quot; data-is-animation=&quot;false&quot; data-filename=&quot;6회차 IMG 1842.JPG&quot; style=&quot;width: 33.7176%; margin-right: 10px;&quot; data-widthpercent=&quot;34.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAppiI/btsIzn6CPbr/mBUwyea5qJnZ3Fdxtwy1Vk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAppiI%2FbtsIzn6CPbr%2FmBUwyea5qJnZ3Fdxtwy1Vk%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;5184&quot; height=&quot;3456&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dn9GUP/btsIAs7flER/zbp2Av0lCTsjge8HIorgZK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dn9GUP/btsIAs7flER/zbp2Av0lCTsjge8HIorgZK/img.jpg&quot; data-origin-width=&quot;6000&quot; data-origin-height=&quot;4000&quot; data-is-animation=&quot;false&quot; data-filename=&quot;6회차 DSC01371.JPG&quot; style=&quot;width: 33.7176%; margin-right: 10px;&quot; data-widthpercent=&quot;34.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dn9GUP/btsIAs7flER/zbp2Av0lCTsjge8HIorgZK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdn9GUP%2FbtsIAs7flER%2Fzbp2Av0lCTsjge8HIorgZK%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;6000&quot; height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H77st/btsIAuxcV9P/JQ0LWlNcvOMqKDsxc9DHK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H77st/btsIAuxcV9P/JQ0LWlNcvOMqKDsxc9DHK0/img.png&quot; data-origin-width=&quot;1956&quot; data-origin-height=&quot;1454&quot; data-is-animation=&quot;false&quot; width=&quot;437&quot; height=&quot;325&quot; style=&quot;width: 30.2392%;&quot; data-widthpercent=&quot;30.96&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H77st/btsIAuxcV9P/JQ0LWlNcvOMqKDsxc9DHK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH77st%2FbtsIAuxcV9P%2FJQ0LWlNcvOMqKDsxc9DHK0%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;1956&quot; height=&quot;1454&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해커톤 결과발표까지 마무리했었고, 결과는 api까지 붙이지는 못했고 mock data로 실시간으로 변경하는 낙폭을 나타내는 차트까지는 구현했다. 어려운 도메인을 재밌게 즐기면서 했던 해커톤이어서 결과는 만족스럽지는 않지만 과정이 아름다워서 좋은 경험이었다.&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/bBSuwB/btsIzpJ7fCJ/u6h97ae1KrktKHR52GlJk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBSuwB/btsIzpJ7fCJ/u6h97ae1KrktKHR52GlJk1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;998&quot; data-filename=&quot;껄무새1.png&quot; style=&quot;width: 47.0476%; margin-right: 10px;&quot; data-widthpercent=&quot;47.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBSuwB/btsIzpJ7fCJ/u6h97ae1KrktKHR52GlJk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBSuwB%2FbtsIzpJ7fCJ%2Fu6h97ae1KrktKHR52GlJk1%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;1192&quot; height=&quot;998&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmHoeF/btsIzUpFORz/Ekdbwdz9U2cPrYgMlHN2Z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmHoeF/btsIzUpFORz/Ekdbwdz9U2cPrYgMlHN2Z1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2456&quot; data-origin-height=&quot;1868&quot; data-filename=&quot;껄무새2.png&quot; style=&quot;width: 51.7896%;&quot; data-widthpercent=&quot;52.4&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmHoeF/btsIzUpFORz/Ekdbwdz9U2cPrYgMlHN2Z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmHoeF%2FbtsIzUpFORz%2FEkdbwdz9U2cPrYgMlHN2Z1%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;2456&quot; height=&quot;1868&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 대부분의 해커톤 같은 경우에는 보통 서로 경쟁하면서 1등을 목표로 해커톤에 임하지만, 사이프톤은 1등을 위해서 또는 어떤 결과를 위해서 하는 게 아니라는 분위기를 느꼈다. 같이 참여한 모든 분들 모든 팀이 같이 활동하고 있는 사이프 2기 멤버들이었고, 즐거운 분위기 속에서 해커톤이 진행되었다. 그래서 그런지 &lt;b&gt;개발! PPT! 결과물!이라는 무거운 분위기가 아니라 하하 호호 장난도 치면서 즐거운 분위기 속에서 부담감 없이 진행된 것 같아서 그냥 다 같이 이 해커톤을 즐긴다는 그런 부분이 색다른 경험이었던 것 같다.&lt;/b&gt;&lt;br /&gt;그렇다고 다른 팀들의 결과물이나 주제들이 별로였나? 하면 그런 건 아니었다. 이미 모두가 현업자여서 그런지 다들 책임감이 있었고, 알잘딱깔센을 완벽하게 하는 걸 보면서 그래 이거지.. 이런 프로페셔널함에 또다시 배우게 되었던 얻는 게 참 많았던 해커톤이었다.&lt;br /&gt;그래서 참여한 모든 팀에게 각자 다양한 상을 주는 것도 취지가 참 좋지 않았나 싶다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6회차.JPG&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4cril/btsIBfzyiJc/e2vr9MxCU3psCiqtwlghEk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4cril/btsIBfzyiJc/e2vr9MxCU3psCiqtwlghEk/img.jpg&quot; data-alt=&quot;6회차 사이프톤도 마무리!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4cril/btsIBfzyiJc/e2vr9MxCU3psCiqtwlghEk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4cril%2FbtsIBfzyiJc%2Fe2vr9MxCU3psCiqtwlghEk%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;701&quot; height=&quot;467&quot; data-filename=&quot;6회차.JPG&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;6회차 사이프톤도 마무리!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1721036418193&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - sipe-team/sipethon-2_2: 껄무새 - 비트코인살&amp;rdquo;껄&amp;rdquo; 라고 생각만 하다가 현실로 옮기지 못한 우&quot; data-og-description=&quot;껄무새 - 비트코인살&amp;rdquo;껄&amp;rdquo; 라고 생각만 하다가 현실로 옮기지 못한 우리의 모습이 반영된 모습이다. - sipe-team/sipethon-2_2&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/sipe-team/sipethon-2_2&quot; data-og-url=&quot;https://github.com/sipe-team/sipethon-2_2&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/sipe-team/sipethon-2_2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/sipe-team/sipethon-2_2&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - sipe-team/sipethon-2_2: 껄무새 - 비트코인살&amp;rdquo;껄&amp;rdquo; 라고 생각만 하다가 현실로 옮기지 못한 우&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;껄무새 - 비트코인살&amp;rdquo;껄&amp;rdquo; 라고 생각만 하다가 현실로 옮기지 못한 우리의 모습이 반영된 모습이다. - sipe-team/sipethon-2_2&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7회 차: 사이데이션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5회 차의 1차 미션 발표가 끝나자마자 2차 미션을 슬랙을 통해 주제를 취합하였고, 2차 미션을 각자 선택하였다. 나는 2차 미션으로도 1차 때 배운 게 아쉽기도 했고, 완전 찍먹만 해봤던 느낌이라 rust 주제를 또 선택하게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sQcCm/btsIzASzRd2/FKgyUrX4BNLKfrLkBhKQy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sQcCm/btsIzASzRd2/FKgyUrX4BNLKfrLkBhKQy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sQcCm/btsIzASzRd2/FKgyUrX4BNLKfrLkBhKQy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsQcCm%2FbtsIzASzRd2%2FFKgyUrX4BNLKfrLkBhKQy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;647&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀은 1차 미션 팀과 똑같이 2차 미션팀으로 모두 동일 멤버로 같이 하게 되었고, 2차 미션 팀명으로 &quot;뉴비를 grep 하는 Rust의 신들린 무빙 (Rust 심화)&quot;라는 이름으로 각자 rustling의 바탕으로 mini grep 만들기를 하기로 하였고, 사이데이션은 1차 사이데이션과 동일하게 2차 미션 팀원들과 함께 모여서 친목 활동을 하는 시간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 모여서 rust 모각코를 했고, 이후에는 다른 팀과 함께 저녁을 먹고, 보드게임으로 사이데이션을 마무리하였다.&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/cuut06/btsIz7WA1qx/cAgYa5X5U3jJ86IlL9Xbyk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuut06/btsIz7WA1qx/cAgYa5X5U3jJ86IlL9Xbyk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3025&quot; data-filename=&quot;Rust IMG 8242.jpg&quot; style=&quot;width: 42.3508%; margin-right: 10px;&quot; data-widthpercent=&quot;42.85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuut06/btsIz7WA1qx/cAgYa5X5U3jJ86IlL9Xbyk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcuut06%2FbtsIz7WA1qx%2FcAgYa5X5U3jJ86IlL9Xbyk%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;3025&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csdcKY/btsIzzzmXG1/wtG1zpRRb4rbTQCpWwqMYk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csdcKY/btsIzzzmXG1/wtG1zpRRb4rbTQCpWwqMYk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;Rust IMG 8246.jpg&quot; style=&quot;width: 56.4864%;&quot; data-widthpercent=&quot;57.15&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csdcKY/btsIzzzmXG1/wtG1zpRRb4rbTQCpWwqMYk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsdcKY%2FbtsIzzzmXG1%2FwtG1zpRRb4rbTQCpWwqMYk%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;세상에서 제일 재밌던 텔레스트레이션 feat. 번개 의자...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8회 차: 내친소 - 꽁꽁 얼어붙은 IT위로 SIPE가 지나갑니다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;내친소는 내 친구를 소개합니다의 줄임말로 IT 업계에서 현업으로 일하고 있는 친구나 지인을 초대해 많은 사람들과 함께 네트워크를 할 수 있는 행사로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;사이프 활동의 마지막&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;외부&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;행사이자 가장 많은 사람들이 모이는 네트워크 행사이다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;IMG_6640.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpZbB5/btsIBowj2kH/K0nggkyMbciQ76PkrM8Ez1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpZbB5/btsIBowj2kH/K0nggkyMbciQ76PkrM8Ez1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpZbB5/btsIBowj2kH/K0nggkyMbciQ76PkrM8Ez1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpZbB5%2FbtsIBowj2kH%2FK0nggkyMbciQ76PkrM8Ez1%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;458&quot; height=&quot;611&quot; data-filename=&quot;IMG_6640.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 내친소는 다른 행사와 다르게 TF로 참여해 내친소 행사를 운영진분들과 함께 기획하고 준비했다.&lt;br /&gt;워낙 이런 행사 기획에 참여해 운영하는 걸 좋아해서 해보고 싶어서 지원했다.&lt;br /&gt;이번에도 &lt;a href=&quot;https://festa.io/events/5257&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;페스타&lt;/a&gt;를 통해서 외부 참석자 신청을 받았고, QR코드로 입장하도록 했다. 외부 참석자라고는 하지만, 초대받은 지인만 신청이 가능했고, 총 80 - 90명 정도의 인원으로 내친소 행사가 진행됐다.&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/y3mRy/btsIyJJa8LK/ikvZ9fWpK1u17LfjkmsWok/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y3mRy/btsIyJJa8LK/ikvZ9fWpK1u17LfjkmsWok/img.jpg&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot; data-is-animation=&quot;false&quot; data-filename=&quot;정규 활동 사진.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/y3mRy/btsIyJJa8LK/ikvZ9fWpK1u17LfjkmsWok/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy3mRy%2FbtsIyJJa8LK%2FikvZ9fWpK1u17LfjkmsWok%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;6960&quot; height=&quot;4640&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxnvQ8/btsIAP2oLpo/vfttpwgEwzV0vsWnv1DuR0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxnvQ8/btsIAP2oLpo/vfttpwgEwzV0vsWnv1DuR0/img.jpg&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot; data-is-animation=&quot;false&quot; data-filename=&quot;정규 활동 사진 (1).JPG&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxnvQ8/btsIAP2oLpo/vfttpwgEwzV0vsWnv1DuR0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxnvQ8%2FbtsIAP2oLpo%2FvfttpwgEwzV0vsWnv1DuR0%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;6960&quot; height=&quot;4640&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 사이프에서 어떤 것들을 해오고, 무엇을 했는지에 대한 간단한 사이프 소개와 후원사 소개 이후 본격적인 행사가 진행되었다.&lt;br /&gt;나는 내친소에서 게임 파트를 담당했어서 게임파트 레크리에이션 게임 준비와 MC를 보게 되었고, 걱정과 우려와 달리 설문을 보니 긍정적인 답변을 받아서 다행이면서 뿌듯했고 또 언제 써먹을지는 모르겠지만 게임 진행이라는... 새로운 경험을 하게 되었다.&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/bvzrJi/btsIAQAd88Q/RSQYh6Ddv0mwJn4t2kRdh0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvzrJi/btsIAQAd88Q/RSQYh6Ddv0mwJn4t2kRdh0/img.jpg&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot; data-is-animation=&quot;false&quot; data-filename=&quot;정규 활동 사진 (2).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/bvzrJi/btsIAQAd88Q/RSQYh6Ddv0mwJn4t2kRdh0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvzrJi%2FbtsIAQAd88Q%2FRSQYh6Ddv0mwJn4t2kRdh0%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;6960&quot; height=&quot;4640&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cicZVZ/btsIyX1NZcd/AlH1wY8Lc6EUVOaqhywFa0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cicZVZ/btsIyX1NZcd/AlH1wY8Lc6EUVOaqhywFa0/img.jpg&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot; data-is-animation=&quot;false&quot; data-filename=&quot;정규 활동 사진 (4).JPG&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cicZVZ/btsIyX1NZcd/AlH1wY8Lc6EUVOaqhywFa0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcicZVZ%2FbtsIyX1NZcd%2FAlH1wY8Lc6EUVOaqhywFa0%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;6960&quot; height=&quot;4640&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 레크리에이션 게임 파트 진행이 끝나고 개인전/팀전 게임 시상이 끝나고 오늘 함께 오신 모든 분들과 네트워크 할 수 있는 세션을 가졌다. 다양한 사람들과 다양한 직군을 가진 IT 업계의 많은 사람들과 함께 또 다른 이야기 꽃을 펼쳐나가니 시간 가는 줄 모르고 떠들었던 것 같다.&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/WTFG5/btsIzUJ40pB/Add7Fo0OFuRqLPs4Kyxx50/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WTFG5/btsIzUJ40pB/Add7Fo0OFuRqLPs4Kyxx50/img.jpg&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot; data-is-animation=&quot;false&quot; data-filename=&quot;정규 활동 사진 (5).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/WTFG5/btsIzUJ40pB/Add7Fo0OFuRqLPs4Kyxx50/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWTFG5%2FbtsIzUJ40pB%2FAdd7Fo0OFuRqLPs4Kyxx50%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;6960&quot; height=&quot;4640&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/de4qbh/btsIz64vicR/45cDvl5uOIhaFfWTZqMltK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/de4qbh/btsIz64vicR/45cDvl5uOIhaFfWTZqMltK/img.jpg&quot; data-origin-width=&quot;6960&quot; data-origin-height=&quot;4640&quot; data-is-animation=&quot;false&quot; data-filename=&quot;정규 활동 사진 (6).JPG&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/de4qbh/btsIz64vicR/45cDvl5uOIhaFfWTZqMltK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fde4qbh%2FbtsIz64vicR%2F45cDvl5uOIhaFfWTZqMltK%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;6960&quot; height=&quot;4640&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 행사가 끝나고, 모두들 아쉬워 뒤풀이로 이동해 더 많은 얘기들을 했고 그렇게 내친소 행사를 마무리하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;정규 활동 사진 (7).jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drnxId/btsIyIcsd9r/q10cRkp0UbNDKE69ntgyjK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drnxId/btsIyIcsd9r/q10cRkp0UbNDKE69ntgyjK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drnxId/btsIyIcsd9r/q10cRkp0UbNDKE69ntgyjK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrnxId%2FbtsIyIcsd9r%2Fq10cRkp0UbNDKE69ntgyjK%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;617&quot; height=&quot;365&quot; data-filename=&quot;정규 활동 사진 (7).jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1211&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 동아리에서 행사를 운영하고 기획하는 건 많이 힘들기는 하지만, 많은 분들께 이런 즐거움을 주고 개인적으로 잘 마무리했다는 뿌듯함을 얻을 수 있기 때문에 더 할 수 있는 힘이 나고 계속하고 싶어 하는 것 같다. 그리고 무엇보다 이런 과정이 난 꽤 재밌어하는 것 같다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;9회 차: 2차 미션 발표&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2134&quot; data-origin-height=&quot;1224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WDjw5/btsIy4Guz1N/0hCcgtZE2O50TK5mKBXZEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WDjw5/btsIy4Guz1N/0hCcgtZE2O50TK5mKBXZEK/img.png&quot; data-alt=&quot;헌겸님의 킹받는 퓌퓌티&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WDjw5/btsIy4Guz1N/0hCcgtZE2O50TK5mKBXZEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWDjw5%2FbtsIy4Guz1N%2F0hCcgtZE2O50TK5mKBXZEK%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;541&quot; height=&quot;310&quot; data-origin-width=&quot;2134&quot; data-origin-height=&quot;1224&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;2차 미션발표는 1차 미션 발표와 동일하게 2차 미션에 결과를 각 팀별로 발표하는 자리이자 마지막 정규 활동으로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;사이프 2기가 마무리된다. 2차 미션 발표는 개인 사정으로 불참해서 오프라인 행사에는 참석하지 못했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2차 미션으로 rust 팀은 minigrep 만들기를 진행했었고, 결과적으로는 현업 + 개인적으로 루즈해진 탓 때문인지 많은 시간을 투자하지 못하고 마무리한 것 같아서 아쉬움이 남는다. 아쉬움은 많이 남지만 좋은 사람들과 같이 rust라는 언어를 배웠고 시작이 반이듯이 시작을 했고, 찍먹을 해봤으니 앞으로 계속 꾸준히 공부해 볼 생각이다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;4개월 1차, 2차 미션동안 rust 팀을 이끌어준 헌겸 팀장님.. 힘드셨을 텐데 감사합니다. 덕분에 즐거운 추억도 많이 쌓을 수 있었고, 배우고 싶었던 rust라는 언어를 찍먹 해볼 수 있었습니다. 이후에 사이프에서 러스트 스터디 열면 또 바로 달려가겠습니다. 기다릴게요~ (그때는 좀 더 열심히 해볼게요...)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1721097741199&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - sipe-team/2-2_rust: 뉴비를 grep하는 Rust의 신들린 무빙 (Rust 심화)&quot; data-og-description=&quot;뉴비를 grep하는 Rust의 신들린 무빙 (Rust 심화). Contribute to sipe-team/2-2_rust development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/sipe-team/2-2_rust&quot; data-og-url=&quot;https://github.com/sipe-team/2-2_rust&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bdFXtB/hyWzuDLNZ9/POVtl6s0eUeCX8pgDahaiK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/sipe-team/2-2_rust&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/sipe-team/2-2_rust&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bdFXtB/hyWzuDLNZ9/POVtl6s0eUeCX8pgDahaiK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - sipe-team/2-2_rust: 뉴비를 grep하는 Rust의 신들린 무빙 (Rust 심화)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;뉴비를 grep하는 Rust의 신들린 무빙 (Rust 심화). Contribute to sipe-team/2-2_rust development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4개월간의 SIPE활동을 마무리하며...&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;정규 활동 사진 (3).jpg&quot; data-origin-width=&quot;1365&quot; data-origin-height=&quot;2048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceyZEk/btsIzS6JEV0/K6aJant9D2xmDrI8OAQGa1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceyZEk/btsIzS6JEV0/K6aJant9D2xmDrI8OAQGa1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceyZEk/btsIzS6JEV0/K6aJant9D2xmDrI8OAQGa1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceyZEk%2FbtsIzS6JEV0%2FK6aJant9D2xmDrI8OAQGa1%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;285&quot; height=&quot;428&quot; data-filename=&quot;정규 활동 사진 (3).jpg&quot; data-origin-width=&quot;1365&quot; data-origin-height=&quot;2048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해 초에 목표를 정했던 것 중 하나가 DND 운영진 활동하고 있는 것 말고, 다른 타 IT 커뮤니티에서 활동해 보는 것을 목표로 잡았었는데, 그 목표를 SIPE를 통해서 얻은 것 같아서 뿌듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SIPE 활동을 통해 내가 가장 얻은 것은 무엇일까? 현업자로서 한정되어 있는 지루한 회사 생활에 SIPE를 통해서 많은 사람들과의 교류와 네트워크를 할 수 있었던 점, 더 나아가 이제는 친구가 된 것 같기도 하다. SIPE 활동은 끝이 났지만, 여전히 SIPE에서 만난 사람들과 꾸준히 만나고 소통하고 있다.&lt;br /&gt;언제 어디서 볼 수 있을 정도로 생각보다 굉장히 좁은 IT 업계에서 뭐가 됐든 좋은 인연을 계속 만들어갈 수 있던 것 같아서 유의미한 4개월 SIPE 활동은 좋은 추억의 한 페이지로 남을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두가 즐길 수 있도록 SIPE 2기 활동을 위해 엄청난 열정과 시간을 투자하면서 이끌어주고 행사를 기획해 주신 2기 운영진분들께 감사함을 전달하면서 2기 활동 회고를 마무리하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 2기 때 참여자로 활동했던 과정들에 긍정적인 에너지와 많은 사람들과 대화하면서 동기부여를 얻을 수 있었고, 2기 때 느꼈던 감정과 경험을 바탕으로 SIPE 3기에는 운영진으로 참여하는 것으로 마무리하고 싶어서 3기 부회장으로 운영진 역할을 할 예정이니 앞으로 모집할 SIPE 3기도 많은 관심과 지원을 바라며 글을 마무리하겠다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;2024 인프콘에 SIPE가 커뮤니티 부스로 참여하게 되었는데 인프콘에 당첨되신 분들 부스에도 많이 와주세요.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>it 커뮤니티</category>
      <category>sipe</category>
      <category>사이프</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/40</guid>
      <comments>https://haranglog.tistory.com/40#entry40comment</comments>
      <pubDate>Tue, 16 Jul 2024 12:33:58 +0900</pubDate>
    </item>
    <item>
      <title>[단계 완료] 미니학습지 일본어 학습지 4단계</title>
      <link>https://haranglog.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;어느덧 &lt;a href=&quot;https://studymini.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미니학습지&lt;/a&gt; 환급원정대 4단계가 마무리되었다. 벌써 반이나 완료하게 되었는데, 확실히 미니학습지의 도움을 많이 받아서 점점 들리고 있다!! 역시 작지만 반복하고 꾸준히 공부하면 점점 배워가는게 사람인가 싶다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7006.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdeaNS/btsIyxal5eX/bBQ5smYi02jlzfDUfksem1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdeaNS/btsIyxal5eX/bBQ5smYi02jlzfDUfksem1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdeaNS/btsIyxal5eX/bBQ5smYi02jlzfDUfksem1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdeaNS%2FbtsIyxal5eX%2FbBQ5smYi02jlzfDUfksem1%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;662&quot; height=&quot;883&quot; data-filename=&quot;IMG_7006.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 일본 만화 컨텐츠를 봤는데 생각보다 단어랑 문법이 들려서 놀랐다. 솔직히 본업이 있다보니, 열심히 시간을 많이 투자해서 했던건 아니여서 이게 지금 내가 늘고 있는 건가 싶었는데, 처음 시작했을때랑 나를 다시 돌아봤을때 확실히 늘고있고, 지금도 늘고 있는게 보이고, 이 과정이 신기하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉬운점은 이제 들리기는하지만 말하는게 안나와서 계속 반복해서 말할려고 노력해야할 것 같다. 이게 쉽지 않기도하고 내 노력부족이라..ㅠㅠ 할말없...&lt;/p&gt;
&lt;p&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/c44eSD/btsIytsiibR/PUKyOK5xlj0X1Y3PKfWUQk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c44eSD/btsIytsiibR/PUKyOK5xlj0X1Y3PKfWUQk/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_7007.JPG&quot; width=&quot;349&quot; height=&quot;262&quot; style=&quot;width: 61.2306%; margin-right: 10px;&quot; data-widthpercent=&quot;61.95&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c44eSD/btsIytsiibR/PUKyOK5xlj0X1Y3PKfWUQk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc44eSD%2FbtsIytsiibR%2FPUKyOK5xlj0X1Y3PKfWUQk%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/cFbmZO/btsIzeVqNxg/HqsOUZwWefakF9IZr8Ggf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFbmZO/btsIzeVqNxg/HqsOUZwWefakF9IZr8Ggf1/img.png&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;1756&quot; data-is-animation=&quot;false&quot; width=&quot;482&quot; height=&quot;589&quot; style=&quot;width: 37.6066%;&quot; data-widthpercent=&quot;38.05&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFbmZO/btsIzeVqNxg/HqsOUZwWefakF9IZr8Ggf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFbmZO%2FbtsIzeVqNxg%2FHqsOUZwWefakF9IZr8Ggf1%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;1438&quot; height=&quot;1756&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;이번 4단계에서는 채감상 3단계에서 느꼈던 어려움보다 조금은 쉬운느낌이였다. 진짜 쉽다라는건 아니지만, 2단계에서 3단계 넘어갔을때랑 비교해서 쉬운느낌이였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4단계에서는&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동사의 과거형의 보통체&lt;/li&gt;
&lt;li&gt;た형 + ばかりです - 행동이 끝난지 얼마 시간이 지나지 않았다&lt;/li&gt;
&lt;li&gt;보통체 명사,な형용상의 과거형&lt;/li&gt;
&lt;li&gt;타인의 말을 전달하는 법&lt;/li&gt;
&lt;li&gt;길 안내에 대한 표현 (조건문에서 그 행동을 하면 그 일이 생긴다)&lt;/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;span&gt;&amp;nbsp;&lt;/span&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;/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;와 같은 문법적인 요소와 문장을 잇는 표현들을 많이 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직은 이 배운 모든것들을 적재적소에 쓰지는 못하고, 말도 잘 못하지만.. 처음과 비교해서 나아진것 처럼 계속 하다보면 느리지만 또 언젠간 더 발전한 내가 되어있을거라 믿는다. (언제나 그런것처럼)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벌써 8단계중 반을 끝냈는데, 5단계... 앞으로 남은 반. 6개월 잘 마무리 했으면 좋겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미니학습지 환급원정대 화이팅!&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1720962963678&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;미니학습지 &amp;ndash; 즐거운 외국어 생활의 시작&quot; data-og-description=&quot;&quot; data-og-host=&quot;studymini.com&quot; data-og-source-url=&quot;https://studymini.com/&quot; data-og-url=&quot;https://studymini.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/XZRHZ/hyWzvPQhBr/TX5XI5GkZHmO7a0eD2qyn1/img.jpg?width=400&amp;amp;height=229&amp;amp;face=0_0_400_229,https://scrap.kakaocdn.net/dn/s96Ff/hyWzsMm5K7/ASNCcqoib6AZySZ5u6q2g0/img.png?width=1280&amp;amp;height=300&amp;amp;face=0_0_1280_300,https://scrap.kakaocdn.net/dn/hCC5S/hyWzDmOCX8/8sVHMWDH7w88pEvYF9Ngvk/img.png?width=600&amp;amp;height=588&amp;amp;face=0_0_600_588&quot;&gt;&lt;a href=&quot;https://studymini.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://studymini.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/XZRHZ/hyWzvPQhBr/TX5XI5GkZHmO7a0eD2qyn1/img.jpg?width=400&amp;amp;height=229&amp;amp;face=0_0_400_229,https://scrap.kakaocdn.net/dn/s96Ff/hyWzsMm5K7/ASNCcqoib6AZySZ5u6q2g0/img.png?width=1280&amp;amp;height=300&amp;amp;face=0_0_1280_300,https://scrap.kakaocdn.net/dn/hCC5S/hyWzDmOCX8/8sVHMWDH7w88pEvYF9Ngvk/img.png?width=600&amp;amp;height=588&amp;amp;face=0_0_600_588');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;미니학습지 &amp;ndash; 즐거운 외국어 생활의 시작&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;studymini.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>일본어</category>
      <category>미니학습지</category>
      <category>성인학습지</category>
      <category>스터디미니</category>
      <category>일본어인강</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/39</guid>
      <comments>https://haranglog.tistory.com/39#entry39comment</comments>
      <pubDate>Sun, 14 Jul 2024 22:31:08 +0900</pubDate>
    </item>
    <item>
      <title>[단계 완료] 미니학습지 일본어 학습지 3단계</title>
      <link>https://haranglog.tistory.com/38</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://studymini.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미니학습지&lt;/a&gt;일본어 환급원정대 3단계가 마무리되었다. 총 8단계 12개월이라는 긴 시간동안 진행되는 환급원정대인데 3단계까지 마무리 되었고, 순조롭게? 진행중이다.&lt;br /&gt;2단계도 어려웠는데 3단계에서는 문장의 길이도 늘어나서 더 알아야할 게 많고 단어도 더 숙지가 필요해보였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_6588.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8LJYy/btsHLHxncPc/Jq6M8J6XCqAiGLMaOSJDAK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8LJYy/btsHLHxncPc/Jq6M8J6XCqAiGLMaOSJDAK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8LJYy/btsHLHxncPc/Jq6M8J6XCqAiGLMaOSJDAK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8LJYy%2FbtsHLHxncPc%2FJq6M8J6XCqAiGLMaOSJDAK%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;434&quot; height=&quot;579&quot; data-filename=&quot;IMG_6588.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 3단계에서는 문장을 잇는 것들과 끝나는 형식이 다른 부분들이 많았다.&lt;br /&gt;이번 3단계의 배웠던것들은&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;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;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;이런 것들을 배웠다. 지금보니 한단계에 안에서도 정말 많은 문법적인 내용들과 단어들이 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 일본어를 배우면서 느끼는건데 그냥 한국말을 사용하고 있을때에는 몰랐던 보이지 않는 문법적인 요소들과 다양한 끝단어처리 등 같은 단어에서도 억양에 달라지는 의미들도 있는 거를 보면서 새로운 언어를 배우기란 참 어려운것 같다는 생각이 들었다. (한국말은 어떻게 배운거지.. 0개국어일지도..)&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/cJ14gr/btsHMOJdfK0/nMJAnp7UAl55K02oTUKZ9K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJ14gr/btsHMOJdfK0/nMJAnp7UAl55K02oTUKZ9K/img.jpg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-is-animation=&quot;false&quot; data-filename=&quot;IMG_6589.jpg&quot; width=&quot;547&quot; height=&quot;410&quot; style=&quot;width: 60.7307%; margin-right: 10px;&quot; data-widthpercent=&quot;61.45&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJ14gr/btsHMOJdfK0/nMJAnp7UAl55K02oTUKZ9K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJ14gr%2FbtsHMOJdfK0%2FnMJAnp7UAl55K02oTUKZ9K%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/4UqWb/btsHKS7jPfG/xHRCcJfne0pcUqqRCDeKb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4UqWb/btsHKS7jPfG/xHRCcJfne0pcUqqRCDeKb0/img.png&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;1824&quot; data-is-animation=&quot;false&quot; width=&quot;537&quot; height=&quot;642&quot; style=&quot;width: 38.1065%;&quot; data-widthpercent=&quot;38.55&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4UqWb/btsHKS7jPfG/xHRCcJfne0pcUqqRCDeKb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4UqWb%2FbtsHKS7jPfG%2FxHRCcJfne0pcUqqRCDeKb0%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;1526&quot; height=&quot;1824&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직도 한참 부족하고 지금 배운 문법이나 단어들도 사실 하나도 모르겠는데.. 그래도 3단계가 끝난 무렵 뭔가 들리는 것 같고, 아는 것들이 나올때마다 신기하다. 이런게 언어를 배우는 재미인가...? 그럼에도 많은 시간을 투자하기 쉽지는 않아서 학습지와 강의만 하다보니 이렇게 해서 늘고있는게 맞는지 싶기도하다.. 특히 계속 접하고 직접 단어를 조합해서 말해봐야 확실히 늘것 같은데.. 아직 일본어가 입에서 잘 안나온다. 꾸준히 하다보면 되려나..?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;사실&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;꾸준히가 가장 어려운건데 그래도 미니학습지 환급원정대의 도움이 많이크기 때문에.. 억지로라도 하게되어 계속 접할 수 있도록 도와주고 있는게 아닌가 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2단계 끝났을때도 시간을 더 투자해보자 했지만, 그 약속과 다짐은 멸망했지만, 지금 이대로만이라도 꾸준히 해보자. 환급원정대 끝날때가지 화이팅.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(일본어를 공부하다보면,, 일본을 가고싶단 말이지..)&lt;/p&gt;
&lt;figure id=&quot;og_1717333967608&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;미니학습지 &amp;ndash; 즐거운 외국어 생활의 시작&quot; data-og-description=&quot;하루 1장으로 구성되어 밀리지 않는 학습지 1일 1장, 1달에 1권, 미니의 커리큘럼만 따라오세요! 단계별로 나누어져 있어서, 시각적으로 레벨 업을 확인할 수 있어요. 10분 이내 숏폼 강의 구성으&quot; data-og-host=&quot;studymini.com&quot; data-og-source-url=&quot;https://studymini.com/&quot; data-og-url=&quot;https://studymini.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cgSlbT/hyWg53ThTp/dpQ6B0YkRo965KVBKESkc1/img.jpg?width=400&amp;amp;height=229&amp;amp;face=0_0_400_229,https://scrap.kakaocdn.net/dn/vqDoI/hyWgXkt9wM/9fG5UqA0uLC74EXGO06MB0/img.png?width=740&amp;amp;height=640&amp;amp;face=0_0_740_640,https://scrap.kakaocdn.net/dn/cukpz7/hyWg78sRgl/0aCzkjl6HNs4581usPYOVK/img.png?width=740&amp;amp;height=640&amp;amp;face=375_313_572_510&quot;&gt;&lt;a href=&quot;https://studymini.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://studymini.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cgSlbT/hyWg53ThTp/dpQ6B0YkRo965KVBKESkc1/img.jpg?width=400&amp;amp;height=229&amp;amp;face=0_0_400_229,https://scrap.kakaocdn.net/dn/vqDoI/hyWgXkt9wM/9fG5UqA0uLC74EXGO06MB0/img.png?width=740&amp;amp;height=640&amp;amp;face=0_0_740_640,https://scrap.kakaocdn.net/dn/cukpz7/hyWg78sRgl/0aCzkjl6HNs4581usPYOVK/img.png?width=740&amp;amp;height=640&amp;amp;face=375_313_572_510');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;미니학습지 &amp;ndash; 즐거운 외국어 생활의 시작&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;하루 1장으로 구성되어 밀리지 않는 학습지 1일 1장, 1달에 1권, 미니의 커리큘럼만 따라오세요! 단계별로 나누어져 있어서, 시각적으로 레벨 업을 확인할 수 있어요. 10분 이내 숏폼 강의 구성으&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;studymini.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;&lt;span style=&quot;color: #0593d3; text-align: left;&quot;&gt;#미니학습지 #스터디미니 #일본어인강 #성인학습지&lt;/span&gt;&lt;span style=&quot;color: #0593d3; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>일본어</category>
      <category>미니학습지</category>
      <category>성인학습지</category>
      <category>스터디미니</category>
      <category>일본어인강</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/38</guid>
      <comments>https://haranglog.tistory.com/38#entry38comment</comments>
      <pubDate>Sun, 2 Jun 2024 23:09:34 +0900</pubDate>
    </item>
    <item>
      <title>[단계 완료] 미니학습지 일본어 학습지 2단계</title>
      <link>https://haranglog.tistory.com/37</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://studymini.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미니학습지&lt;/a&gt; 일본어 환급원정대 2단계 마무리! 벌써 미니학습지를 수강한지도 3개월이 되었다.&amp;nbsp;&lt;br /&gt;1단계보다 확실히 어려워지고있다. 단어와 문장이 길어지고, 변칙 동사와 부정, 과거형 등을 배웠다.&lt;br /&gt;2단계가 끝나고 느낀점이 있다면, 꾸준히 공부를 한다고한들.. 복습과 말하기 연습을 같이 함께 병행하면서 학습하는게 효율이 좋은 것 같다. 추가로 일본 컨텐츠를 시청한다거나..&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;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_6065.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lobrJ/btsGMRPl3WI/fqVLKGk9rvTWgiyFR41nmk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lobrJ/btsGMRPl3WI/fqVLKGk9rvTWgiyFR41nmk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lobrJ/btsGMRPl3WI/fqVLKGk9rvTWgiyFR41nmk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlobrJ%2FbtsGMRPl3WI%2FfqVLKGk9rvTWgiyFR41nmk%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;515&quot; height=&quot;687&quot; data-filename=&quot;IMG_6065.JPG&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&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;2단계에서 배운 것들 중에 어려운것들 중 하나는 이제 단어와 조사를 가지고 하나의 문장을 만들어야하는데, 변칙동사와 형용사, 부정형, 과거형등 일본어 문법에 관한 내용일 적재적소 사용하는 포인트가 아직 어색하고 어려운부분으로 다가왔다.&lt;br /&gt;그래도 마지막에 문법특강으로 45분짜리 강의도 제공해줘서 다시 한 번 더 정리할 수 있는 기회가 있어서 좋았다. 하지만, 역시 강의에서 쌤이 말씀하시듯.. 문법을 외우려고하기보단, 문장과 문맥으로 계속 말해보며 접해보는게 가장 좋은 방법인 것 같다. (k-직장인으로 시간이 부족한게 아쉽다..)&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/bdpnxP/btsGMJ4WNGR/fnD2bkWbKHk8KCk469G00K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdpnxP/btsGMJ4WNGR/fnD2bkWbKHk8KCk469G00K/img.png&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot; data-is-animation=&quot;false&quot; data-filename=&quot;edited_IMG_6066.jpg&quot; width=&quot;600&quot; height=&quot;450&quot; style=&quot;width: 57.9954%; margin-right: 10px;&quot; data-widthpercent=&quot;58.68&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdpnxP/btsGMJ4WNGR/fnD2bkWbKHk8KCk469G00K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdpnxP%2FbtsGMJ4WNGR%2FfnD2bkWbKHk8KCk469G00K%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;3000&quot; height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwJrNJ/btsGOu6o8Xu/osdwPCcFlrNbhT9cOPKRh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwJrNJ/btsGOu6o8Xu/osdwPCcFlrNbhT9cOPKRh1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1704&quot; data-filename=&quot;스크린샷 2024-04-21 오후 9.08.46.png&quot; style=&quot;width: 40.8418%;&quot; data-widthpercent=&quot;41.32&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwJrNJ/btsGOu6o8Xu/osdwPCcFlrNbhT9cOPKRh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwJrNJ%2FbtsGOu6o8Xu%2FosdwPCcFlrNbhT9cOPKRh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;1704&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그래도 3개월전으로 돌아가보면 확실히 늘긴했다. 하지만, 벌써 2단계가 마무리되고 다음부터는 3단계가 시작될텐데.. 개인적으로 걱정이 크다. 좀 더 많은 시간을 투자해서 공부를 했어야했는데 시간이 정말 없어서 그러지 못하는게 아쉽다. 그래도 꾸준히 하고 있는거에 의미를 두고, 3단계부터는 더 어려워질테니 시간을 좀 더 투자해보도록 노력해보자~!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계에는 일본어에 조금더 친해진 내가되길바라며.. 단계 완료 후기를 마무리해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0593d3; text-align: left;&quot;&gt;#미니학습지 #스터디미니 #일본어인강 #성인학습지&lt;/span&gt;&lt;/p&gt;</description>
      <category>일본어</category>
      <category>미니학습지</category>
      <category>성인학습지</category>
      <category>스터디미니</category>
      <category>일본어인강</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/37</guid>
      <comments>https://haranglog.tistory.com/37#entry37comment</comments>
      <pubDate>Sun, 21 Apr 2024 21:28:48 +0900</pubDate>
    </item>
    <item>
      <title>[단계 완료] 미니학습지 일본어 학습지 1단계</title>
      <link>https://haranglog.tistory.com/36</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://studymini.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미니학습지&lt;/a&gt; 환급원정대를 신청하고 어느덧 6주가 지나 1단계가 마무리되었다!&lt;br /&gt;시간이 엄청빠르게 지나갔고, 그 시간동안 미니학습지 덕분에 꾸준히 하나씩 해나아가고 있는 것 같다.&lt;br /&gt;나처럼 작지만 꾸준한 습관을 만들어가는 분들이라면 환급원정대 추천!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1단계에서는 히라가나부터 시작해서 인사말과 숫자, 여러 기본적인 단어들, い형용사등 일본어의 기본적인 것들을 주로 배우고 학습했다.&lt;br /&gt;중학교때 배우고 일본어에 일자도 몰랐었는데 그때의 기억들이 새록새록 떠오르면서 재밌게 학습한 1단계가 아니였나 싶다.&lt;br /&gt;미니학습지의 1단계에서 초반에는 너무 간단해서 양이 적다고 느꼈었는데, 뒤로갈수록 이전에 나왔던 단어들과 함께 조합한 문장들이 생각보다 어려웠다. 특히 단어 같은 경우에는 까먹는 경우가 많아서 개인적인 답답합을 느끼기도 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_5564.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1MH6o/btsFEYV6eAq/2VHktKEk7gF1OQ4lvh9F50/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1MH6o/btsFEYV6eAq/2VHktKEk7gF1OQ4lvh9F50/img.jpg&quot; data-alt=&quot;온라인 강의 + 학습지 = 완벽&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1MH6o/btsFEYV6eAq/2VHktKEk7gF1OQ4lvh9F50/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1MH6o%2FbtsFEYV6eAq%2F2VHktKEk7gF1OQ4lvh9F50%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;526&quot; height=&quot;701&quot; data-filename=&quot;IMG_5564.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;온라인 강의 + 학습지 = 완벽&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습지에서 가장 좋았던건 이전에 배웠던 단어들과 문장들을 꾸준히 어느순간에 다시 리마인드를 시켜주는 포인트들이 있어서 만족스러웠다. 아! 그거 뭐였지..! 하는 단기기억들을 장기기억으로 넣어주는 과정이 학습지에 잘 녹여져있는 것 같아서 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1단계에서 아직도 잘 모르겠는건... 어느 순간에 の、が、は등 중간에 문장을 이어주는 단어의 위치가 알면서도 직접 적용해볼려하면 잘 안되는게 참 어려운 것 같다.&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/boZywM/btsFEO7shFg/xCAeRMEXu0LDiGgYyzxIv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boZywM/btsFEO7shFg/xCAeRMEXu0LDiGgYyzxIv0/img.png&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;2250&quot; data-is-animation=&quot;false&quot; data-filename=&quot;edited_IMG_5562.jpg&quot; width=&quot;469&quot; height=&quot;352&quot; style=&quot;width: 46.3745%; margin-right: 10px;&quot; data-widthpercent=&quot;46.92&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boZywM/btsFEO7shFg/xCAeRMEXu0LDiGgYyzxIv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboZywM%2FbtsFEO7shFg%2FxCAeRMEXu0LDiGgYyzxIv0%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;3000&quot; height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rBJeE/btsFFQDJvNu/ZkPfxeFaOgSs3eGmIs1zb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rBJeE/btsFFQDJvNu/ZkPfxeFaOgSs3eGmIs1zb1/img.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;1074&quot; data-is-animation=&quot;false&quot; width=&quot;596&quot; height=&quot;395&quot; style=&quot;width: 52.4627%;&quot; data-widthpercent=&quot;53.08&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rBJeE/btsFFQDJvNu/ZkPfxeFaOgSs3eGmIs1zb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrBJeE%2FbtsFFQDJvNu%2FZkPfxeFaOgSs3eGmIs1zb1%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;1620&quot; height=&quot;1074&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;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 1단계이고 이제 시작이니 급하게 생각하지말고, 꾸준하게 해보는게 중요한 것 같다. 이번에 일본에서 배운 단어와 문장들을 적용해보고 바로 사용해보니 확실히 더 빠르게 느는걸느꼈다. 이래서 유학가는 이유가 아닐까.. 생각이 들었다. 4월에도 일본가니 그때는 좀 더 나아진 나를 봤으면 좋겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 1단계 6주 동안 잘 하고 있는 것 같고, 2단계도 꾸준히 이어나가봐야겠다. 마지막으로 공부하면서 일본 컨텐츠를 접하도록 노력하자! 이게 중요한듯.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;#미니학습지 #스터디미니 #일본어인강 #성인학습지&lt;/span&gt;&lt;/p&gt;</description>
      <category>일본어</category>
      <category>미니학습지</category>
      <category>성인학습지</category>
      <category>스터디미니</category>
      <category>일본어인강</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/36</guid>
      <comments>https://haranglog.tistory.com/36#entry36comment</comments>
      <pubDate>Sun, 10 Mar 2024 22:32:46 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 3년차 프론트엔드 개발자 늦은 2023년 회고</title>
      <link>https://haranglog.tistory.com/35</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2023-3.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxrWZQ/btsEmUtM0bI/7xyg2UUsJyuuu9muH2jEEk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxrWZQ/btsEmUtM0bI/7xyg2UUsJyuuu9muH2jEEk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxrWZQ/btsEmUtM0bI/7xyg2UUsJyuuu9muH2jEEk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cxrWZQ/btsEmUtM0bI/7xyg2UUsJyuuu9muH2jEEk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;500&quot; data-filename=&quot;2023-3.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&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;늦어도 너~~~무 늦은 2023년 회고와 2024년 계획을 지금이나마 작성해보려고 한다.&amp;nbsp;&lt;br /&gt;많은 일들이 있었던 2023년은 어땠을까? 계획을 잘 지켰고 열심히 살아왔다고 할 수 있을까라면 물음표 긴 해서 2023년을 반성하는 기분으로 글을 써보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;a href=&quot;https://haranglog.tistory.com/33&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022년에 연말에 세웠던 2023년 계획&lt;/a&gt;을 되돌아보자.&lt;/p&gt;
&lt;figure id=&quot;og_1707045401059&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;벌써 2년 프론트엔드 개발자의 2022년 회고&quot; data-og-description=&quot;올해도 어김없이 찾아온 연말 회고 타임! 벌써 3번째 연말 회고이다. 회고는 하면 할수록 자기 객관화에 많은 도움을 준다. 특히 나처럼 머릿속에 생각이 가득 차있고 정리가 안될 때는 생각을 &quot; data-og-host=&quot;haranglog.tistory.com&quot; data-og-source-url=&quot;https://haranglog.tistory.com/33&quot; data-og-url=&quot;https://haranglog.tistory.com/33&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eaAGX6/hyVgbxKFqa/qJDStDY44HFmdKtm8KxOck/img.gif?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/cuMOYO/hyVfZD6UEY/5Kv1BNX0V2tDUdRhcKku3K/img.gif?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/e09Wh/hyVf9UeNZ8/JvnfbuTViHLcPDKoaDMSrK/img.png?width=1446&amp;amp;height=1946&amp;amp;face=0_0_1446_1946&quot;&gt;&lt;a href=&quot;https://haranglog.tistory.com/33&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://haranglog.tistory.com/33&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eaAGX6/hyVgbxKFqa/qJDStDY44HFmdKtm8KxOck/img.gif?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/cuMOYO/hyVfZD6UEY/5Kv1BNX0V2tDUdRhcKku3K/img.gif?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/e09Wh/hyVf9UeNZ8/JvnfbuTViHLcPDKoaDMSrK/img.png?width=1446&amp;amp;height=1946&amp;amp;face=0_0_1446_1946');&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;벌써 2년 프론트엔드 개발자의 2022년 회고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;올해도 어김없이 찾아온 연말 회고 타임! 벌써 3번째 연말 회고이다. 회고는 하면 할수록 자기 객관화에 많은 도움을 준다. 특히 나처럼 머릿속에 생각이 가득 차있고 정리가 안될 때는 생각을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;haranglog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2023년의 계획은 얼마나?&lt;/h2&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1️⃣ Github Daily Commit 계속 유지하기&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ORMdL/btsEkTvCJTB/OT9FPqWMabD82HFzUQooiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ORMdL/btsEkTvCJTB/OT9FPqWMabD82HFzUQooiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ORMdL/btsEkTvCJTB/OT9FPqWMabD82HFzUQooiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FORMdL%2FbtsEkTvCJTB%2FOT9FPqWMabD82HFzUQooiK%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;294&quot; height=&quot;52&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1586&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddc5B8/btsEmNVvTqs/YAcykod1b4qXfC1u6QSA2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddc5B8/btsEmNVvTqs/YAcykod1b4qXfC1u6QSA2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddc5B8/btsEmNVvTqs/YAcykod1b4qXfC1u6QSA2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fddc5B8%2FbtsEmNVvTqs%2FYAcykod1b4qXfC1u6QSA2k%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;1586&quot; height=&quot;418&quot; data-origin-width=&quot;1586&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Daily Commit은 다행히 2023년에도 꾸준히 유지하고 있다. 1379일째라니... 2024년 4월이 되면 벌써 4년 차다. 내가 프론트엔드 개발을 공부한 지도 4년이 되었다는 것이다. 아무것도 몰랐던 내가 매일 꾸준히 작은&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; 커밋을 하나씩 쌓아갔기 때문에 지금의 내가 된 거라 믿는다.&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Daily Commit이 누구한테는 부정적일 수도 있겠지만 나한테는 개발을 꾸준히 할 수 있게 해 준 원동력이자 티끌이 태산이 되는 걸 볼 수 있었던 자산 그 자체이다.&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 모든 게 좋은 점만 있는 게 아니듯이 단점도 존재하는 걸 4년을 해봤기에 누구보다 잘 알고 있다. 이 내용은 글의 목적에 벗어난 내용이기에 Daily Commit 4년 차 회고 때 더 자세히 글을 작성할 예정이다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2️⃣ &lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;운동 꾸준히 하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운동은 다행히 꾸준히 실천중이다.&amp;nbsp;&lt;br /&gt;개인적으로 아쉬운 부분은 이것저것 많이 해보면서 정말 내가 하고 싶은 운동은 아직 못 찾았다는 것이다.  &lt;br /&gt;헬스, 클라이밍, 스쿼시, 스키(사실 액티비티) 등등. 그래도 클라이밍은 정말 재밌게 했었다. 클라이밍은 파란색까지는 풀 수 있게 되어서 문제 푸는 즐거움을 알게 되었다.&lt;br /&gt;결국은 PT를 끊을 예정인데 솔직히 재미는 없..다.&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;1077&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZ7i4k/btsEnfRJjhV/TFfdkY3dHp1wbnUkRqkArK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZ7i4k/btsEnfRJjhV/TFfdkY3dHp1wbnUkRqkArK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZ7i4k/btsEnfRJjhV/TFfdkY3dHp1wbnUkRqkArK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZ7i4k%2FbtsEnfRJjhV%2FTFfdkY3dHp1wbnUkRqkArK%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;275&quot; height=&quot;368&quot; data-filename=&quot;클라이밍.jpeg&quot; data-origin-width=&quot;1077&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;3️⃣  Soople 런칭하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;작년의 계획과 많이 달라진 것 중에 하나가 이 계획이지 않을까 싶다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;바뀐 계획이 오히려 더 많은 일들을 이루게 해 준 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://soople.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Soople&lt;/a&gt;은 2021년부터 디자이너 제니와 함께 계속 해온 사이드 프로젝트였는데 사실상 아쉽게도 여기까지 하고 마무리하게 되었다. 오랫동안 했었기에 늘어진 부분도 없지 않았고, 앞으로 더 많은 것들을 하기 위해서 접는 거라 생각한다.&lt;br /&gt;개인적으로 코드도 꽤나 잘 작성해 두었고, 열심히 했었기에 많이 아쉽긴 하다. 언젠가 또 모를 soople런칭을 기약하며..&lt;br /&gt;관련한 얘기에 대한 내용은 밑에 작성하겠다.&lt;/p&gt;
&lt;figure id=&quot;og_1707048026011&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;스터디와 사이드 프로젝트, soople&quot; data-og-description=&quot;스터디와 사이드 프로젝트 수플과 함께 시작하세요!&quot; data-og-host=&quot;soople.io&quot; data-og-source-url=&quot;https://soople.io/&quot; data-og-url=&quot;https://soople.io&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dLkVMV/hyVfYSJJlS/1Ssez7K7EwSQ0Deuu40Oc0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://soople.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://soople.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dLkVMV/hyVfYSJJlS/1Ssez7K7EwSQ0Deuu40Oc0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;스터디와 사이드 프로젝트, soople&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스터디와 사이드 프로젝트 수플과 함께 시작하세요!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;soople.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;4️⃣  이직&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;2023년의 가장 큰 이벤트가 아닌가 싶다. 2022년 11월에 퇴사하여 2023년 3월에 글로벌 팬덤 비즈니스 플랫폼을 만드는 &lt;a href=&quot;https://bemyfriends.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;비마이프렌즈&lt;/a&gt;라는 회사에 입사하게 되었다.&lt;br /&gt;비마이프렌즈를 선택했던 이유는 &lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;여러 곳의 면접을 보면서 가장 오랜 시간 동안 면접을 보았지만 &lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;면접 경험이 가장 좋았다.&lt;span&gt; 이직할 때 가장 중요하게 생각했던 기준은 동료와 내가 재밌게 할 수 있는 도메인이었는데 가장 중요했던 두 가지가 충족이 되어서 입사를 결정하게 되었다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;그렇게 입사 후 지금은 거의 1년이 다 되어가는 시점에서 본 회사는 만족스럽다. &lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;인재밀도가 매우 높은&lt;span&gt;&amp;nbsp;&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;color: #000000; text-align: left;&quot;&gt;특히 가장 장점이라고 생각하는 건 직접 개발한 프로젝트를 사용자의 피드백을 빠르게 들을 수 있다는 점이 아닐까 싶다. 그중에서 특히 연말에 롤 팀 T1에서 진행했던 콘서트에 직접 스탭까지 참여해서 가장 기억에 남는다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&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/cuqzoD/btsEmWZsSf7/uVy9urgM4rviAFErZ2ZUU1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuqzoD/btsEmWZsSf7/uVy9urgM4rviAFErZ2ZUU1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;IMG_4732.JPG&quot; width=&quot;358&quot; height=&quot;477&quot; data-widthpercent=&quot;25.74&quot; style=&quot;width: 25.4376%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuqzoD/btsEmWZsSf7/uVy9urgM4rviAFErZ2ZUU1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuqzoD%2FbtsEmWZsSf7%2FuVy9urgM4rviAFErZ2ZUU1%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;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lA9v3/btsEnYWutTt/7ExK0twnUUFntTEttK8kSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lA9v3/btsEnYWutTt/7ExK0twnUUFntTEttK8kSk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1170&quot; data-filename=&quot;IMG_4738.PNG&quot; width=&quot;487&quot; height=&quot;225&quot; style=&quot;width: 73.3996%;&quot; data-widthpercent=&quot;74.26&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lA9v3/btsEnYWutTt/7ExK0twnUUFntTEttK8kSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlA9v3%2FbtsEnYWutTt%2F7ExK0twnUUFntTEttK8kSk%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;2532&quot; height=&quot;1170&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;T1 CON 2023 현장수령 스탭 참여 + 엄마~! 나 뉴스 나왔어 (숨은 하랑 찾기)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&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: left;&quot;&gt;좋은 시니어분들이 많이 계셔서 정말 지나치게 많이 배우고 있다. 기술적인 하드 스킬도 많이 배우고 있지만, 특히 소프트스킬의 커뮤니케이션과 어떻게 하면 일을 잘할 수 있을까? 에 대해서 많이 배우고 있는 것 같다. 2022년의 나와 비교한다면 가장 많이 성장한 부분이라고 한다면 개발자로서 일하는 방식이 아닐까 싶다. 여전히 어렵고 어렵지만 좋은 환경에서 많이 배우고 더 성장해야겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&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;IMG_2107.JPG&quot; data-origin-width=&quot;3020&quot; data-origin-height=&quot;1768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGI0IN/btsEnXwvEBL/sOUYEeKb4PGJawGSQ4Ijh0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGI0IN/btsEnXwvEBL/sOUYEeKb4PGJawGSQ4Ijh0/img.jpg&quot; data-alt=&quot;재택근무로 지금은 없어진 내자리.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGI0IN/btsEnXwvEBL/sOUYEeKb4PGJawGSQ4Ijh0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGI0IN%2FbtsEnXwvEBL%2FsOUYEeKb4PGJawGSQ4Ijh0%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;640&quot; height=&quot;375&quot; data-filename=&quot;IMG_2107.JPG&quot; data-origin-width=&quot;3020&quot; data-origin-height=&quot;1768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;재택근무로 지금은 없어진 내자리.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5️⃣ 발표나 강의 또는 멘토 해보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요거는 목표를 잘 달성했을까..? 한다면 반반이지 않을까 싶다.&lt;br /&gt;사실 남들처럼 그렇다할&amp;nbsp;발표나 강의를 해본 건 아니지만, 회사 내에서 프론트엔드에서의 테스트와 Next App Router 전환 관련해서 세미나를 진행해 보았다. 이 두 내용 관련해서는 개인적으로 블로그에 따로 정리할 예정이다. (많관부~)&lt;br /&gt;그리고 교육기관에서도 어떻게 하다보니 커리어와 멘토링 관련해서 강의를 해보는 경험을 하게 되었다. 다들 그렇듯 뭔가 해야지! 해서 진행했던 건 아니었는데 그래도 발표하는 방법이라든지 내용을 전달하는 점 이외에도 많이 배운 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로는 이따가 얘기할 내용이긴 하지만 &lt;a href=&quot;https://www.dnd.ac/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DND&lt;/a&gt;라는 IT 비영리단체에서 운영진 활동을 하게 되었는데 여기서 즐겁게 운영진 활동과 함께 개발 멘토 역할을 맡고 있다. (자세한 내용은 밑에..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 기회는 있었지만, 여러 사정이 있어서 못했던 것들도 있었는데 다음번에 또 이런 기회가 있으면 뭐든 해보고 싶다. 겁이 많아서 새로운 것들을 할 때에는 두렵기도 하지만, 그만큼 그 과정이 너무 재밌고 배우는 게 많은 것 같아서 열심히 도전하려고 노력하고 있다. 기회는 또 언제 올지 모르기에..&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6️⃣ 블로그 한 달에 한 개 쓰기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계획 새운 내용 중에 가장 못한 계획이다. 반성을 많이 한다.&lt;br /&gt;1~2개라도 썼어야 했는데 작년에 블로그 글 한 개도 못썼다. 바쁘긴 했지만 그 또한 핑계.. 할 말이 없다.&lt;br /&gt;근데 블로그 방문자수는 무슨일..? 글이 20개 정도 뿐인데 누적 방문자수가 11만명이 넘었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;268&quot; data-origin-height=&quot;80&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Amo0W/btsEJcz0r88/AKKduBz8X2Vk2cv6uwY7b0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Amo0W/btsEJcz0r88/AKKduBz8X2Vk2cv6uwY7b0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Amo0W/btsEJcz0r88/AKKduBz8X2Vk2cv6uwY7b0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAmo0W%2FbtsEJcz0r88%2FAKKduBz8X2Vk2cv6uwY7b0%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;268&quot; height=&quot;80&quot; data-origin-width=&quot;268&quot; data-origin-height=&quot;80&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 2024년은 제발 달라진 모습을..  &lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;7️⃣ 기술서적 꾸준히 읽기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기술서적도 마찬가지로 한 권도 못 읽었다. &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;기술서적&amp;nbsp;&lt;/span&gt;책만 사놨지.. 손도 못 댔다.&lt;br /&gt;개인적으로 인터넷이 아닌 책에서 얻는 인사이트가 정~~말 많다는 걸 느꼈기 때문에 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;기술서적은 꼭 무조건 읽어야 한다고 생각한다.&lt;br /&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;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이것도 2024년에는 노력해야 할 부분이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;8️⃣ 기타&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;  스위스, 일본 여행 가기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위스는 아쉽지만 좀 먼 미래를 기약해야 할 것 같다. 유럽을 가는데 스위스만 가기 아쉬워서 언제일지는 모르겠지만, 유럽 1달 여행할 때 가야겠다. 내 버킷리스트..&amp;nbsp;그렇게 이루기 쉬우면 그게 버킷리스트겠니..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일본 여행은 여러 번 갔다 왔다. 회사가 재택을 하니 워케이션도 가능해져서 길게도 갔다 올 수도 있었다. 후쿠오카 3번, 삿포로 1번 총 네 번을 다녀온 것 같다. 이제는 후쿠오카 러버가 되어 이제는 여행이 아닌 고향처럼 들락날락하는 것 같다. ㅋㅋ&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/dY5IZg/btsEFLWSvWb/tKKDEpETy4q5kaWvOuQOS1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dY5IZg/btsEFLWSvWb/tKKDEpETy4q5kaWvOuQOS1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;IMG_3342.jpg&quot; width=&quot;426&quot; height=&quot;426&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/dY5IZg/btsEFLWSvWb/tKKDEpETy4q5kaWvOuQOS1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdY5IZg%2FbtsEFLWSvWb%2FtKKDEpETy4q5kaWvOuQOS1%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/rFq4P/btsEDoa0U1B/Za0aEm3pcZGm3FDbsA3fWK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rFq4P/btsEDoa0U1B/Za0aEm3pcZGm3FDbsA3fWK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2870&quot; data-origin-height=&quot;2870&quot; data-filename=&quot;IMG_3288.JPG&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rFq4P/btsEDoa0U1B/Za0aEm3pcZGm3FDbsA3fWK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrFq4P%2FbtsEDoa0U1B%2FZa0aEm3pcZGm3FDbsA3fWK%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;2870&quot; height=&quot;2870&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;size16&quot;&gt;2022년에 일본 한 달 동안 여행했을 때 삿포로도 갔었는데, 개인적으로 꼭 가고 싶었던 곳을 못 가서 이번에 다시 갔었다. 정말 저 세상 풍경.. 자고 일어났는데 앞에 풍경이 첫 번째 사진이면..? 또 가고 싶다.. 아니 또 가야겠다.&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/wRfYP/btsEELJ4oYZ/beyaMeQlJAOcoSkrD7hYdk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wRfYP/btsEELJ4oYZ/beyaMeQlJAOcoSkrD7hYdk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot; data-filename=&quot;sapporo.jpeg&quot; width=&quot;469&quot; height=&quot;352&quot; data-widthpercent=&quot;57.14&quot; style=&quot;width: 56.4784%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wRfYP/btsEELJ4oYZ/beyaMeQlJAOcoSkrD7hYdk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwRfYP%2FbtsEELJ4oYZ%2FbeyaMeQlJAOcoSkrD7hYdk%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;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLIXID/btsEF4aNRJ3/1Uk6yCOW01pSZ8nQ48BfnK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLIXID/btsEF4aNRJ3/1Uk6yCOW01pSZ8nQ48BfnK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1440&quot; data-filename=&quot;ss.jpeg&quot; width=&quot;515&quot; height=&quot;515&quot; style=&quot;width: 42.3588%;&quot; data-widthpercent=&quot;42.86&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLIXID/btsEF4aNRJ3/1Uk6yCOW01pSZ8nQ48BfnK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLIXID%2FbtsEF4aNRJ3%2F1Uk6yCOW01pSZ8nQ48BfnK%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;figcaption&gt;네 그림아니구요. 필터 아니구요. 100% 원본 사진입니다.&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;  &amp;nbsp;보육원 후원 및 기회가 된다면 자원봉사&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 연말에 같이 하는 친구가 있는데 2023년은 아쉽게 못했다. 기부나 후원이라는 건 참 쉽지 않은 일이다. &quot;순수&quot;한 마음으로 하기는 쉽지 않은 것 같다. 왜 안 하냐고 물어볼 수도 없는지라 내년에는 그냥 혼자라도 해야 할 것 같다.&lt;br /&gt;기부는 못했지만 그래도 매달 결연후원으로 한 아이를 후원하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아... 하나 더 있구나.. 거절 못해서 어쩌다 하게 된 &lt;s&gt;유니세프&lt;/s&gt;&amp;nbsp;유엔 우크라이나 후원..ㅠ (순수한 마음이 아니었기에 5개월만 하고 몰래 취소했다.)&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  연초계획을 많이 못 지켰네. 뭐 했길래?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞다. 계획을 많이 못 지켰다. 솔직히 2022년과 비교했을 때 내가 자기 계발을 안 했거나 개발 공부를 안 한 건 아니다. 다만 2022년과 다르게 그 시간을 투자한 곳이 다를 뿐이다.&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;2023년에 시간을 의미 있게 투자했다고 생각하는 부분은 두 가지로 나눌 수 있을 것 같다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;첫 번째, DND &amp;amp; 사이드 프로젝트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;  &lt;a href=&quot;https://www.dnd.ac/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DND&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DND는 IT 동아리 사이드프로젝트 비영리단체로 100% 후원으로 운영되고 있다. 현업 개발자 &amp;amp; 디자이너뿐만 아니라 취준생, 대학생까지 각 직군별로 모집을 받고 8주간 팀을 매칭해 사이드 프로젝트를 주차별 커리큘럼에 맞춰서 진행하는 IT 단체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서도 언급했듯이, 이번에 운영진을 할 기회가 되어 6월부터 운영진을 하고 있다. 9기와 이번에 10기 두 차례 개발 운영진을 맡아서 프로젝트 진행 시 관련 조언을 드리고 있으며, 멘토링 및 FE 관련 인사이트나 코드리뷰도 해주고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_4566.JPG&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;853&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6CCNl/btsEFTHmPOs/mALNCyRH3V2B7C52Ukbw4k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6CCNl/btsEFTHmPOs/mALNCyRH3V2B7C52Ukbw4k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6CCNl/btsEFTHmPOs/mALNCyRH3V2B7C52Ukbw4k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6CCNl%2FbtsEFTHmPOs%2FmALNCyRH3V2B7C52Ukbw4k%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;289&quot; height=&quot;514&quot; data-filename=&quot;IMG_4566.JPG&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;853&quot;/&gt;&lt;/span&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/bTlXbv/btsEHpyOoLD/FPkI2Q9oozO3UPLfhJ7CyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTlXbv/btsEHpyOoLD/FPkI2Q9oozO3UPLfhJ7CyK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-filename=&quot;IMG_4567.PNG&quot; width=&quot;314&quot; height=&quot;558&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTlXbv/btsEHpyOoLD/FPkI2Q9oozO3UPLfhJ7CyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTlXbv%2FbtsEHpyOoLD%2FFPkI2Q9oozO3UPLfhJ7CyK%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XzNCx/btsEFR3Pjmh/Tq3esmkEKwc3EbKnbzPyG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XzNCx/btsEFR3Pjmh/Tq3esmkEKwc3EbKnbzPyG1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-filename=&quot;IMG_4568.PNG&quot; width=&quot;473&quot; height=&quot;841&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XzNCx/btsEFR3Pjmh/Tq3esmkEKwc3EbKnbzPyG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXzNCx%2FbtsEFR3Pjmh%2FTq3esmkEKwc3EbKnbzPyG1%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhgL9Y/btsED3YT0Po/jmA9KRTEKaSCkmDTA4y7c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhgL9Y/btsED3YT0Po/jmA9KRTEKaSCkmDTA4y7c0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-filename=&quot;IMG_4569.PNG&quot; width=&quot;151&quot; height=&quot;268&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhgL9Y/btsED3YT0Po/jmA9KRTEKaSCkmDTA4y7c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhgL9Y%2FbtsED3YT0Po%2FjmA9KRTEKaSCkmDTA4y7c0%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;인스타는 안하지만 다정님이 만들어주신 인스스 소개글. (DND 인스타도 많관부.)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 생각보다 시간이 많이 투자가 되는 것 같다. 10기 지원자는 홍보비용도 없이 거의 1000명 정도였다. (서류 검토하다가 기절할 뻔..) 그중 80명 총 10팀을 뽑고 있는데 지원자가 많았던 만큼 더 많은 분들에게 기회를 드리고 싶지만 그만큼의 사람을 감당도 안될뿐더러 비영리단체이기 때문에 무료로 진행되는 만큼 쉽지 않은 부분인 것 같다. 그렇기 때문에 더 신중하게 정말 필요한 분들에게 기회를 드리고 있다.&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;그만큼 보람을 느끼고 얼마나 절실하고 열정이 가득한지 알기 때문에 더 도움을 드리고 싶은 마음이 큰 것 같다. 그런 분들을 보면 동기부여도 얻곤 한다. 그거로 내 시간과 노력은 충분하다.&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/b0Pvkk/btsEEC7Hxr4/7ne6GNg4NhsGOLOgtPhxn1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0Pvkk/btsEEC7Hxr4/7ne6GNg4NhsGOLOgtPhxn1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;IMG_3155.JPG&quot; width=&quot;402&quot; height=&quot;402&quot; data-widthpercent=&quot;32.43&quot; style=&quot;width: 31.6782%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0Pvkk/btsEEC7Hxr4/7ne6GNg4NhsGOLOgtPhxn1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0Pvkk%2FbtsEEC7Hxr4%2F7ne6GNg4NhsGOLOgtPhxn1%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/bisPBq/btsEFedZpci/uRz3cuivCPHCOjXXrLQIhk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bisPBq/btsEFedZpci/uRz3cuivCPHCOjXXrLQIhk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;IMG_3161.JPG&quot; style=&quot;width: 42.2376%; margin-right: 10px;&quot; data-widthpercent=&quot;43.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bisPBq/btsEFedZpci/uRz3cuivCPHCOjXXrLQIhk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbisPBq%2FbtsEFedZpci%2FuRz3cuivCPHCOjXXrLQIhk%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/kFGHm/btsEDhpwfHv/JzaApmILTNc4VSyB4UeLdk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kFGHm/btsEDhpwfHv/JzaApmILTNc4VSyB4UeLdk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;IMG_3165.JPG&quot; style=&quot;width: 23.7586%;&quot; data-widthpercent=&quot;24.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kFGHm/btsEDhpwfHv/JzaApmILTNc4VSyB4UeLdk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkFGHm%2FbtsEDhpwfHv%2FJzaApmILTNc4VSyB4UeLdk%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;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;DND 굿즈 + 9기 오프라인 최종 발표 + 무한 리필 숯불 바베큐까지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9기는 후원이 좀 들어와서 좋은 곳에서 크게 진행했는데 10기는 이렇게 가능할지 모르겠다. 항상 기수별로 여러 오프라인에 대한 니즈들이 있는데 그런 부분이 사실 후원 없이는 힘든 것 같다. (예를 들어, 해커톤이나 오프라인 네트워킹 자리) 열심히 하시는 만큼 더 많은 기회를 드리고 싶지만, 어쩔 수 없는 부분에 아쉬움과 죄송한 마음이 큰 것 같다.&lt;br /&gt;그러니 많은 관심과 후원부탁드립니다. 언제나 연락 기다립니다.&amp;nbsp;  &amp;zwj;♂️ &lt;/p&gt;
&lt;figure id=&quot;og_1707406225114&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;DND&quot; data-og-description=&quot;프로젝트에 즐거움을, 모두에게 기회를&quot; data-og-host=&quot;www.dnd.ac&quot; data-og-source-url=&quot;https://www.dnd.ac/&quot; data-og-url=&quot;https://www.dnd.ac/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bayFIv/hyVgaGHujt/AFsMlNdaPASvN0uDlks7lK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400&quot;&gt;&lt;a href=&quot;https://www.dnd.ac/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.dnd.ac/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bayFIv/hyVgaGHujt/AFsMlNdaPASvN0uDlks7lK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400');&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;DND&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트에 즐거움을, 모두에게 기회를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.dnd.ac&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt; &amp;nbsp; NFTeam&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_4613.PNG&quot; data-origin-width=&quot;2400&quot; data-origin-height=&quot;2400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tnTCO/btsEEyqJJJR/A6c2LIKn0FiebfBTZvxCOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tnTCO/btsEEyqJJJR/A6c2LIKn0FiebfBTZvxCOk/img.png&quot; data-alt=&quot;뉴진스? 아니 NFTeam&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tnTCO/btsEEyqJJJR/A6c2LIKn0FiebfBTZvxCOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtnTCO%2FbtsEEyqJJJR%2FA6c2LIKn0FiebfBTZvxCOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;333&quot; height=&quot;333&quot; data-filename=&quot;IMG_4613.PNG&quot; data-origin-width=&quot;2400&quot; data-origin-height=&quot;2400&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;뉴진스? 아니 NFTeam&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Soople의 백엔드가 필요해서 이전에 같이 일했었던 마롱이 온 뒤로 제니랑 같이 했던 사이드프로젝트 멤버에&lt;span&gt; &lt;/span&gt;&lt;/span&gt;마롱도 같이하게 되면서 팀이름이 NFTeam이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀명은 정말 단순하지만 정말 우리에게 잘 어울리는 mbit nf team이다. (TMI로 마롱이 INFP, 제니가 ENFP, 내가 INFJ이다.)&lt;br /&gt;이렇게 셋이 모여서 2023년은 두 가지의 사이드 프로젝트를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나는 2월부터 꾸준히 하고 있는 프로젝트로 해외에서 찾는 로컬 맛집이라는 주제로 서비스명은 offbeat이다. 도메인 주소까지 합치면 offbeat.place로 색다른 장소. 크.. 이름 참 잘 지은 듯. (chat-gpt야 고마워!)&lt;br /&gt;이 프로젝트를 하면서 제일 많이 얻은 게 있다면, google map api 사용법이 아닐까 싶다. API 문서뿐만 아니라 사용법은 거의 대부분 알게 되었고, 대부분 기능을 사용 중이다. Google Map API 관련해서 React 라이브러리를 만들어보거나 고민했던 포인트를 블로그에 작성해 보도록 하자.&lt;br /&gt;offbeat는 아직 작업이 진행 중이고, 더 develop 하고 있어서 앞으로 많관부..&lt;/p&gt;
&lt;figure id=&quot;og_1707402885361&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - mbti-nf-team/offbeat-frontend:   WIP   해외에서 찾는 k place&quot; data-og-description=&quot;  WIP   해외에서 찾는 k place. Contribute to mbti-nf-team/offbeat-frontend development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/mbti-nf-team/offbeat-frontend&quot; data-og-url=&quot;https://github.com/mbti-nf-team/offbeat-frontend&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ftxTk/hyVjiQCG10/PnGvHw9TPa1NRHPq9pXg2k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/mbti-nf-team/offbeat-frontend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/mbti-nf-team/offbeat-frontend&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ftxTk/hyVjiQCG10/PnGvHw9TPa1NRHPq9pXg2k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - mbti-nf-team/offbeat-frontend:   WIP   해외에서 찾는 k place&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  WIP   해외에서 찾는 k place. Contribute to mbti-nf-team/offbeat-frontend development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 하나는 내 생일 1위 노래를 알려주는 정말 간단한 서비스로 서비스명은 &lt;a href=&quot;https://birth-tunes.nfteam.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Birth Tunes&lt;/a&gt;이다. 주말에 오프라인으로 모여서 거의 하루 만에 뚝딱 만든 서비스로 우리 모두 너무 뿌듯했다.&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1707403340680&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;내 생일에 1위를 한 노래 찾기&quot; data-og-description=&quot;선택한 날에 1위한 곡 확인하기&quot; data-og-host=&quot;birth-tunes.nfteam.dev&quot; data-og-source-url=&quot;https://birth-tunes.nfteam.dev/&quot; data-og-url=&quot;https://birth-tunes.nfteam.dev&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/3dTiP/hyVjdofnv6/0kOHXp8gEn5DGvVNwedhZk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/e603C/hyVf74gXPQ/grsS1DxPBuhlyqC3CWI1ik/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dJlFAb/hyVgc5ALfP/TlLVCcaxmWAucuWTDRzWc1/img.png?width=1280&amp;amp;height=1136&amp;amp;face=0_0_1280_1136&quot;&gt;&lt;a href=&quot;https://birth-tunes.nfteam.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://birth-tunes.nfteam.dev/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/3dTiP/hyVjdofnv6/0kOHXp8gEn5DGvVNwedhZk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/e603C/hyVf74gXPQ/grsS1DxPBuhlyqC3CWI1ik/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/dJlFAb/hyVgc5ALfP/TlLVCcaxmWAucuWTDRzWc1/img.png?width=1280&amp;amp;height=1136&amp;amp;face=0_0_1280_1136');&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;내 생일에 1위를 한 노래 찾기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;선택한 날에 1위한 곡 확인하기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;birth-tunes.nfteam.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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/NgD2P/btsEEDSSxgj/oFoifPVFul9XWwLVcpC6ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NgD2P/btsEEDSSxgj/oFoifPVFul9XWwLVcpC6ZK/img.png&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;1264&quot; data-is-animation=&quot;false&quot; style=&quot;width: 29.1934%; margin-right: 10px;&quot; data-widthpercent=&quot;29.54&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NgD2P/btsEEDSSxgj/oFoifPVFul9XWwLVcpC6ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNgD2P%2FbtsEEDSSxgj%2FoFoifPVFul9XWwLVcpC6ZK%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;480&quot; height=&quot;1264&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eXl8Ep/btsEDfyu7MZ/RV0ixsDovNq18Y0tRQSg2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eXl8Ep/btsEDfyu7MZ/RV0ixsDovNq18Y0tRQSg2k/img.png&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;574&quot; data-is-animation=&quot;false&quot; style=&quot;width: 69.6438%;&quot; data-widthpercent=&quot;70.46&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eXl8Ep/btsEDfyu7MZ/RV0ixsDovNq18Y0tRQSg2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeXl8Ep%2FbtsEDfyu7MZ%2FRV0ixsDovNq18Y0tRQSg2k%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;520&quot; height=&quot;574&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;offbeat + birth-tunes 미팅 log&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;a href=&quot;https://github.com/mbti-nf-team/nextjs-starter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;next.js starter 템플릿&lt;/a&gt;과 주로 쓰는 유틸 함수나 react 공통 유틸 컴포넌트와 hook, lint를 라이브러리화 시킨 &lt;a href=&quot;https://github.com/mbti-nf-team/frontend-libraries&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NFTeam 프론트엔드 라이브러리&lt;/a&gt;를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 가지를 만듦으로써 앞으로 새로운 프로젝트를 시작해도 굉장히 빠르게 초기 세팅과 사용할 라이브러리를 npm에서 가져와 사용할 수 있게 되어 생산성을 높였고 배운 점으로는 turborepo세팅하는 법과 npm에 패키지 배포하는 법을 알게 되었다.&lt;/p&gt;
&lt;figure id=&quot;og_1707405154443&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;profile&quot; data-og-title=&quot;NFTeam&quot; data-og-description=&quot;MBTI &amp;quot;NF&amp;quot; team / 디자이너 제니, 백엔드 마롱, 프론트 하랑. NFTeam has 8 repositories available. Follow their code on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/mbti-nf-team&quot; data-og-url=&quot;https://github.com/mbti-nf-team&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kVYDi/hyVjndllnm/r3qkbDmJcRRV8ao2Tkkzk1/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/cOg4BS/hyVf4NeiOa/BT7kcUXQRkVUa1ScapYFGK/img.jpg?width=1440&amp;amp;height=1440&amp;amp;face=0_0_1440_1440,https://scrap.kakaocdn.net/dn/bSPtGx/hyVfZrBYyv/puxVsXVKHe0QEg5kCdtzSK/img.png?width=398&amp;amp;height=398&amp;amp;face=52_116_250_314&quot;&gt;&lt;a href=&quot;https://github.com/mbti-nf-team&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/mbti-nf-team&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kVYDi/hyVjndllnm/r3qkbDmJcRRV8ao2Tkkzk1/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/cOg4BS/hyVf4NeiOa/BT7kcUXQRkVUa1ScapYFGK/img.jpg?width=1440&amp;amp;height=1440&amp;amp;face=0_0_1440_1440,https://scrap.kakaocdn.net/dn/bSPtGx/hyVfZrBYyv/puxVsXVKHe0QEg5kCdtzSK/img.png?width=398&amp;amp;height=398&amp;amp;face=52_116_250_314');&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;NFTeam&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;MBTI &quot;NF&quot; team / 디자이너 제니, 백엔드 마롱, 프론트 하랑. NFTeam has 8 repositories available. Follow their code on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;  Tag wonder&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약간 아픈 손가락 같지만... 그래도 2023년에 진행했던 사이드프로젝트 중 하나이다. DND에 참여했을 때 연락하고 지낸 백엔드 개발자 한분이랑 디자이너 한분이랑 같이 사이드를 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tag wonder 서비스는 &quot;상대방이 보는 나&quot;라는 주제로 시작한 프로젝트이다. 저 사람이 보는 나는 어떤 사람이지? 이런 포인트로 재밌는 이벤트성 프로젝트로 진행하게 되었다.&lt;br /&gt;현재는 UI만 그려둔 상태이고 API 붙이는 작업이 남았지만.. 완성할 수 있을지는 모르겠다.. 화이팅.. (안정균 찾으러 강남언니 폭파시키러 가야겠다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 손바닥 안에 있는 ENFP 채영님, 언젠간 API 작업 완료해주실 정균님도 고맙다고 말해주고 싶다.&lt;/p&gt;
&lt;figure id=&quot;og_1707404562583&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;profile&quot; data-og-title=&quot;Tag Wonder&quot; data-og-description=&quot;Tag Wonder has 2 repositories available. Follow their code on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/tag-wonder&quot; data-og-url=&quot;https://github.com/tag-wonder&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1L45D/hyVjaLPsZd/tJ0nbjo3cEI0C1wzHJLlA0/img.png?width=420&amp;amp;height=420&amp;amp;face=0_0_420_420&quot;&gt;&lt;a href=&quot;https://github.com/tag-wonder&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/tag-wonder&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1L45D/hyVjaLPsZd/tJ0nbjo3cEI0C1wzHJLlA0/img.png?width=420&amp;amp;height=420&amp;amp;face=0_0_420_420');&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;Tag Wonder&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Tag Wonder has 2 repositories available. Follow their code on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;두 번째, 세상을 바라보는 눈&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타이틀이 굉장히 모호하긴 하지만, 나한테는 이만한 말이 없는 것 같다.&lt;br /&gt;2023년은 모니터 속에서만 살고 있던 내가 모니터란 우물을 나와 세상으로 나간 한 해가 아니었나 싶다. 이렇게 해야겠다 생각한 계기는&lt;br /&gt;1. 2022년 11월에 회사를 퇴사하고 한 달간 떠난 일본 여행으로 얻은 여러 깨달음들.&lt;br /&gt;2. 이직한 회사가 재택 하면서 공간적인 부분에 있어서 제약이 없었던 부분.&lt;br /&gt;3. 6월 즈음에 나타난 개인적인 건강 이슈&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 부분들로 인해서 집에서 가만히 있기에는 시간이 너~~무 아까웠다. (하지만, 집이 제일 조하...)&lt;br /&gt;&lt;b&gt;일본의 문화를 좋아해 후쿠오카 휴가 + 워케이션 3번, 삿포로 1번의 여행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2023년 중에서 제일 좋았던 약 2주간의 제주도 워케이션&lt;/b&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/bVrmsW/btsEE0AHyKm/kJ7b290KOtuHL9bCU2a4wK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVrmsW/btsEE0AHyKm/kJ7b290KOtuHL9bCU2a4wK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;IMG_2913.jpg&quot; width=&quot;215&quot; height=&quot;215&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/bVrmsW/btsEE0AHyKm/kJ7b290KOtuHL9bCU2a4wK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVrmsW%2FbtsEE0AHyKm%2FkJ7b290KOtuHL9bCU2a4wK%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/oILeg/btsEFiuj6bY/kMO9ZkpBHjCkaNeTAspixK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oILeg/btsEFiuj6bY/kMO9ZkpBHjCkaNeTAspixK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot; data-filename=&quot;IMG_2864.jpg&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oILeg/btsEFiuj6bY/kMO9ZkpBHjCkaNeTAspixK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoILeg%2FbtsEFiuj6bY%2FkMO9ZkpBHjCkaNeTAspixK%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;바닷가와 함께한 제주 워케이션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게스트하우스에서 여러 사람들과 즐기기, 스쿠버다이빙 체험, 패러글라이딩 체험, 서핑 등. 즐거운 추억들을 많이 쌓았다. 그 외에 좋은 추억과 낭만으로 가득했던 제주도였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yxRGm/btsEFgJ7eBg/d3LxV11l4uyRsGzXl2Iwm1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yxRGm/btsEFgJ7eBg/d3LxV11l4uyRsGzXl2Iwm1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;986&quot; data-filename=&quot;IMG_3008.JPG&quot; width=&quot;305&quot; height=&quot;278&quot; data-widthpercent=&quot;28.24&quot; style=&quot;width: 27.5818%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yxRGm/btsEFgJ7eBg/d3LxV11l4uyRsGzXl2Iwm1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyxRGm%2FbtsEFgJ7eBg%2Fd3LxV11l4uyRsGzXl2Iwm1%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;1080&quot; height=&quot;986&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRYm5y/btsEFkln1Gw/BUmfPUQ1qHQ13TJc8jERrK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRYm5y/btsEFkln1Gw/BUmfPUQ1qHQ13TJc8jERrK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;656&quot; data-filename=&quot;IMG_5210.jpg&quot; style=&quot;width: 44.9115%; margin-right: 10px;&quot; data-widthpercent=&quot;45.98&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRYm5y/btsEFkln1Gw/BUmfPUQ1qHQ13TJc8jERrK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRYm5y%2FbtsEFkln1Gw%2FBUmfPUQ1qHQ13TJc8jERrK%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;1170&quot; height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4AuLX/btsEKFVWr37/Acp4LPJZ5GyeqUoDXUDDeK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4AuLX/btsEKFVWr37/Acp4LPJZ5GyeqUoDXUDDeK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;3456&quot; data-filename=&quot;IMG_2834.jpg&quot; width=&quot;865&quot; height=&quot;865&quot; style=&quot;width: 25.1811%;&quot; data-widthpercent=&quot;25.78&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4AuLX/btsEKFVWr37/Acp4LPJZ5GyeqUoDXUDDeK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4AuLX%2FbtsEKFVWr37%2FAcp4LPJZ5GyeqUoDXUDDeK%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;3456&quot; height=&quot;3456&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;b&gt;호기심천국: 원데이클래스 + &lt;b&gt;전시회&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;7월부터 시작한&lt;span&gt;&amp;nbsp;호기심천국 활동.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원데이 클래스 스타트!&lt;br /&gt;7월 INFJ 쌤이 거의 다 만들어준 도자기 만들기 체험&lt;br /&gt;8월 너무나 맛있었던 타르트 만들기&lt;br /&gt;9월 아직까지 잘 쓰고 있는 가죽 에어팟 케이스&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/Jqe57/btsEECG6BWd/BBwAjYyvePitignnfdHkLk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jqe57/btsEECG6BWd/BBwAjYyvePitignnfdHkLk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;IMG_2424.JPG&quot; width=&quot;357&quot; height=&quot;476&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jqe57/btsEECG6BWd/BBwAjYyvePitignnfdHkLk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJqe57%2FbtsEECG6BWd%2FBBwAjYyvePitignnfdHkLk%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;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGNwZC/btsEKECJEcq/1AcOpmWpQGaXjpBp7KR3Y0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGNwZC/btsEKECJEcq/1AcOpmWpQGaXjpBp7KR3Y0/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;IMG_2663.JPG&quot; width=&quot;326&quot; height=&quot;435&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGNwZC/btsEKECJEcq/1AcOpmWpQGaXjpBp7KR3Y0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGNwZC%2FbtsEKECJEcq%2F1AcOpmWpQGaXjpBp7KR3Y0%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;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxPpoL/btsEEYbSAQD/Kk7JFZRtHQn8JFIWzCmpy0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxPpoL/btsEEYbSAQD/Kk7JFZRtHQn8JFIWzCmpy0/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;IMG_3139.JPG&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxPpoL/btsEEYbSAQD/Kk7JFZRtHQn8JFIWzCmpy0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxPpoL%2FbtsEEYbSAQD%2FKk7JFZRtHQn8JFIWzCmpy0%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;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10월 나만의 향 핸드크림 만들기&lt;br /&gt;전시회 탐방 스타트!&lt;br /&gt;11월 마음의 평화를 가져다준 국립중앙박물관: 사유의 방&lt;br /&gt;12월 MZ 전시회 랜덤데스티니&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/bzB1KT/btsEE1fiMIr/skc6XYXFxRP5w5Q8ZYc5H0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzB1KT/btsEE1fiMIr/skc6XYXFxRP5w5Q8ZYc5H0/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;IMG_3422.JPG&quot; width=&quot;429&quot; height=&quot;572&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzB1KT/btsEE1fiMIr/skc6XYXFxRP5w5Q8ZYc5H0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzB1KT%2FbtsEE1fiMIr%2Fskc6XYXFxRP5w5Q8ZYc5H0%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;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cC5wX3/btsEIOyPOJX/mOAPY98svixPjkkDNxIPFk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cC5wX3/btsEIOyPOJX/mOAPY98svixPjkkDNxIPFk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;IMG_4208.JPG&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cC5wX3/btsEIOyPOJX/mOAPY98svixPjkkDNxIPFk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcC5wX3%2FbtsEIOyPOJX%2FmOAPY98svixPjkkDNxIPFk%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;4032&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dddB3U/btsEFe6CO9A/TZLNPBVa7QE3kSNqmkuYS1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dddB3U/btsEFe6CO9A/TZLNPBVa7QE3kSNqmkuYS1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; data-filename=&quot;IMG_4556.jpg&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dddB3U/btsEFe6CO9A/TZLNPBVa7QE3kSNqmkuYS1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdddB3U%2FbtsEFe6CO9A%2FTZLNPBVa7QE3kSNqmkuYS1%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;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년에도 더 많은 것들을 하자~&lt;/p&gt;
&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;2023년은 대박의 해.. 모든 티켓팅을 성공했다. 덕분에 백예린 콘서트, 태연 콘서트, 롤드컵 결승을 갔다 왔다.&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/bavGKs/btsEKGm0CGq/YXAwxcRAN4ROmIkJ5d1CzK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bavGKs/btsEKGm0CGq/YXAwxcRAN4ROmIkJ5d1CzK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot; data-filename=&quot;baaa.jpeg&quot; width=&quot;407&quot; height=&quot;305&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bavGKs/btsEKGm0CGq/YXAwxcRAN4ROmIkJ5d1CzK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbavGKs%2FbtsEKGm0CGq%2FYXAwxcRAN4ROmIkJ5d1CzK%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;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sL2GO/btsEEDlJ7LU/PCLoLWsQNwcMvOt04ML3t1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sL2GO/btsEEDlJ7LU/PCLoLWsQNwcMvOt04ML3t1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot; data-filename=&quot;qqqq.jpeg&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sL2GO/btsEEDlJ7LU/PCLoLWsQNwcMvOt04ML3t1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsL2GO%2FbtsEEDlJ7LU%2FPCLoLWsQNwcMvOt04ML3t1%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;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8mAE9/btsEE2yyE1R/OM599aCZusxKlMJFJWCIJk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8mAE9/btsEE2yyE1R/OM599aCZusxKlMJFJWCIJk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot; data-filename=&quot;ewrwer.jpeg&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8mAE9/btsEE2yyE1R/OM599aCZusxKlMJFJWCIJk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8mAE9%2FbtsEE2yyE1R%2FOM599aCZusxKlMJFJWCIJk%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;/&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;/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/cp5Zfe/btsEFO7HLN8/csTvtT4yJk1JDEuNc9Z3l1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cp5Zfe/btsEFO7HLN8/csTvtT4yJk1JDEuNc9Z3l1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;3840&quot; data-filename=&quot;IMG_3460.JPG&quot; width=&quot;436&quot; height=&quot;775&quot; style=&quot;width: 35.5814%; margin-right: 10px;&quot; data-widthpercent=&quot;36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cp5Zfe/btsEFO7HLN8/csTvtT4yJk1JDEuNc9Z3l1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcp5Zfe%2FbtsEFO7HLN8%2FcsTvtT4yJk1JDEuNc9Z3l1%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;2160&quot; height=&quot;3840&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7GZAU/btsEFSPIilg/RmUIj06YQS6NEow4zzNhfk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7GZAU/btsEFSPIilg/RmUIj06YQS6NEow4zzNhfk/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;3400&quot; data-origin-height=&quot;3400&quot; data-filename=&quot;IMG_4302.jpg&quot; style=&quot;width: 63.2558%;&quot; data-widthpercent=&quot;64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7GZAU/btsEFSPIilg/RmUIj06YQS6NEow4zzNhfk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7GZAU%2FbtsEFSPIilg%2FRmUIj06YQS6NEow4zzNhfk%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;3400&quot; height=&quot;3400&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 보면, 개인적인 자기 계발이나 공부가 아니겠지만 인생에 있어서 이런 귀중한 경험들을 통해서 개발자로서가 아닌 사람으로서 많은 성장을 한 것 같다. 그리고 누군가와 같이 또는 혼자 즐기면서 지금 현재에 충실하면서 즐겨야 할 때에는 최선을 다해 즐겨야겠다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  반가워! 2024&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용이 많이 길어졌다. 쓰다 보니 개발자의 회고뿐만 아니라 전반적인 2023년의 회고가 되어버린 느낌이다.&lt;br /&gt;2024년도의 계획과 함께 마무리해 보겠다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발 관련 계획&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1️⃣ Github Daily Commit 계속 유지하기 (Default)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 4년이 다되어가는데 2024년도 꾸준히 유지해 보자.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2️⃣ NFTeam&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계획은 세상 누구보다 거창하게 세운 우리... 다들 잘할 수 있겠지? &lt;br /&gt;&lt;a href=&quot;https://youtube.com/shorts/g704ZQeBcdI?si=Cr0-xUb_5_9cfSfw&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MBTI 게으른 개노답 삼형제&lt;/a&gt;여도 괜찮아 내가 열심히 해볼게.  &lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/shorts/g704ZQeBcdI&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/0Fly5/hyVjm60TwN/WSBScmKw8Cml3v3Gnrp7Y0/img.jpg?width=480&amp;amp;height=360&amp;amp;face=0_0_480_360,https://scrap.kakaocdn.net/dn/dTgwTZ/hyVjd3jhNv/jjeZKR2HAIqb9Hf5fklV8k/img.jpg?width=480&amp;amp;height=360&amp;amp;face=0_0_480_360&quot; data-video-width=&quot;480&quot; data-video-height=&quot;360&quot; data-video-origin-width=&quot;480&quot; data-video-origin-height=&quot;360&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/g704ZQeBcdI&quot; width=&quot;480&quot; height=&quot;360&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 우리만의 의미 있고 재밌는 거라면&amp;nbsp;누구보다 열심히 하는 우리. 열심히 회고도하고 로드맵 세웠다. 2024년은 진짜 뭔가 다를 것 같아.&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/bN5Sns/btsEEFRo4Yc/qKTZFEsgJMYkkPONAh3SOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN5Sns/btsEEFRo4Yc/qKTZFEsgJMYkkPONAh3SOk/img.png&quot; data-origin-width=&quot;1890&quot; data-origin-height=&quot;1812&quot; data-is-animation=&quot;false&quot; width=&quot;536&quot; height=&quot;514&quot; style=&quot;width: 52.7851%; margin-right: 10px;&quot; data-widthpercent=&quot;53.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN5Sns/btsEEFRo4Yc/qKTZFEsgJMYkkPONAh3SOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN5Sns%2FbtsEEFRo4Yc%2FqKTZFEsgJMYkkPONAh3SOk%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;1890&quot; height=&quot;1812&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/We5XS/btsEE1GwmxS/lgfn6kasRHmffEd2D8CQK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/We5XS/btsEE1GwmxS/lgfn6kasRHmffEd2D8CQK1/img.png&quot; data-origin-width=&quot;1820&quot; data-origin-height=&quot;2000&quot; data-is-animation=&quot;false&quot; width=&quot;552&quot; height=&quot;607&quot; style=&quot;width: 46.0521%;&quot; data-widthpercent=&quot;46.59&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/We5XS/btsEE1GwmxS/lgfn6kasRHmffEd2D8CQK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWe5XS%2FbtsEE1GwmxS%2Flgfn6kasRHmffEd2D8CQK1%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;1820&quot; height=&quot;2000&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Offbeat 서비스 피드백을 바탕으로 고도화하기&lt;/li&gt;
&lt;li&gt;짧은 주기로 만들 수 있는 서비스가 모여있는 다이소(가제) 프로젝트&lt;/li&gt;
&lt;li&gt;호기심천국 활동&lt;/li&gt;
&lt;li&gt;네이버 블로그 포스팅 (지금은 아무것도 없지만 &lt;a href=&quot;https://blog.naver.com/nf-team&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NFTeam 블로그&lt;/a&gt;도 많관부~)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_IMG_4640.JPG&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KoaKC/btsELpMrKE2/KoUvAnXhp5OG6rtJvkmE5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KoaKC/btsELpMrKE2/KoUvAnXhp5OG6rtJvkmE5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KoaKC/btsELpMrKE2/KoUvAnXhp5OG6rtJvkmE5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKoaKC%2FbtsELpMrKE2%2FKoUvAnXhp5OG6rtJvkmE5k%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;508&quot; height=&quot;508&quot; data-filename=&quot;edited_IMG_4640.JPG&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러다 회사 차리는 거 아닌지 몰라~ 2024년에도 늘 해왔던 것처럼 NFTeam 화이팅~!&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3️⃣ DND&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;DND 홈페이지 V2: 마이그레이션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.dnd.ac/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DND 홈페이지&lt;/a&gt;가 현재 많이 레거시코드이다. 마이그레이션하는 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 vue로 되어있고 3년 전에 만든 코드라 유지보수가 쉽지가 않다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;정적인 페이지이지만 SPA기반으로 되어있어 SEO 최적화도 아쉬운 부분이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;앞으로 새로운 기능 개발 및 자동화 작업이 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 이유로  React 기반인 Next.js로 마이그레이션을 하는 게 목표이다. 작업은 이미 진행 중이고, 정적인 페이지라 기술적으로 어려운 부분은 없어서 4월이면 충분히 가능할 것 같다. 마이그레이션한 내용도 블로그로 작성해야겠다. (늘 그랬던 것처럼 코드는 공개)&lt;/p&gt;
&lt;figure id=&quot;og_1707665649998&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - DNDACADEMY/dnd-academy-v2: DND는 개발자와 디자이너라면 누구나 참여할 수 있는 IT비영리단체입&quot; data-og-description=&quot;DND는 개발자와 디자이너라면 누구나 참여할 수 있는 IT비영리단체입니다. Contribute to DNDACADEMY/dnd-academy-v2 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/DNDACADEMY/dnd-academy-v2&quot; data-og-url=&quot;https://github.com/DNDACADEMY/dnd-academy-v2&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/o20aB/hyVjmFSJyb/Z766bbk0cTRfEEBMvFGVM1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/DNDACADEMY/dnd-academy-v2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/DNDACADEMY/dnd-academy-v2&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/o20aB/hyVjmFSJyb/Z766bbk0cTRfEEBMvFGVM1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - DNDACADEMY/dnd-academy-v2: DND는 개발자와 디자이너라면 누구나 참여할 수 있는 IT비영리단체입&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;DND는 개발자와 디자이너라면 누구나 참여할 수 있는 IT비영리단체입니다. Contribute to DNDACADEMY/dnd-academy-v2 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;DND 운영진 활동&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금처럼 운영진 활동을 계속하면서 DND가 필요한 현업자 또는 학생, 취준생분들에게 도움을 드리고 싶다.&lt;br /&gt;매 기수 운영진 활동을 하면서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;개인적으로 또는 운영진들끼리&lt;span&gt; &lt;/span&gt;&lt;/span&gt;얻는 인사이트와 개선사항이 많이 보인다. 다음기수에는 이전기수보다 더 개선해서 DND활동하는 동안 의미 있는 시간을 드릴 수 있도록 해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 내에서는 커피챗 세션과 QnA세션, 코드리뷰 등을 진행하고 있는데, 개선사항이 많이 많이 보인다. 좀 더 도움을 드릴 수 있는 부분들을 고민해 봐야겠다. 나도 주니어라 이런 걸 내가 해도 될까라는&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/Fo6TT/btsEGkeiD2V/FWXYqgRLAzOLg3ZKpfqqNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fo6TT/btsEGkeiD2V/FWXYqgRLAzOLg3ZKpfqqNK/img.png&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;858&quot; data-is-animation=&quot;false&quot; style=&quot;width: 66.2095%; margin-right: 10px;&quot; data-widthpercent=&quot;66.99&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fo6TT/btsEGkeiD2V/FWXYqgRLAzOLg3ZKpfqqNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFo6TT%2FbtsEGkeiD2V%2FFWXYqgRLAzOLg3ZKpfqqNK%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;1232&quot; height=&quot;858&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ptJ49/btsEFPFI2b3/93MnB2M3QO9v9KosFQTnxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ptJ49/btsEFPFI2b3/93MnB2M3QO9v9KosFQTnxK/img.png&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;2394&quot; data-is-animation=&quot;false&quot; width=&quot;448&quot; height=&quot;633&quot; style=&quot;width: 32.6277%;&quot; data-widthpercent=&quot;33.01&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ptJ49/btsEFPFI2b3/93MnB2M3QO9v9KosFQTnxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FptJ49%2FbtsEFPFI2b3%2F93MnB2M3QO9v9KosFQTnxK%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;1694&quot; height=&quot;2394&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;DND 많관부!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그리고 2024년은 추가로 내가 잘하고 자신 있는 세션(예를 들어, 테스트)에 대해 지식을 공유하는 세미나를 진행해 봐야겠다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4️⃣ 컨퍼런스 참여&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023년은 여행 일정과 겹치거나 추첨 광탈로 컨퍼런스를 참여하지 못했다. 2024년에는 꼭 다시 참여하고 싶다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;5️⃣ 블로그 작성 (Default)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023년에 못 지킨 블로그.. 2024년은 두 달에 한 개씩이라도 포스팅해야겠다.   6개면 할 수 있잖아..&lt;br /&gt;글을 작성할 때 힘을 빼고 작성해야 할 것 같은데 기술 블로그인지라 그럴 수 없는 부분이 큰 것 같다. 내가 작성한 글들로 인해 잘못된 지식이 전파되면 안 되기 때문에 그만큼의 시간이 오래 걸리는 건 어쩔 수 없는 것 같다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;5️⃣ 기술서적 읽기 (Default)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술서적도 꾸준히 읽어야 하는데.. 자신이 없다. ㅠ&lt;br /&gt;3권만이라도 읽어보자. 욕심안 부리고 읽고 싶은 책 딱 3권.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;6️⃣ 새로운 IT 커뮤니티 및 동아리 참여해보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 많이 하고 있는 것 같지만... 또 다른 새로운 무언가를 해보고 싶은 욕구가 스멀스멀 올라온다.&lt;br /&gt;DND와 비슷한 동아리든 다른 스타일의 IT 커뮤니티든 참여해 보고 많은 인사이트와 네트워킹 자리를 갖고 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개인적인 계획&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1️⃣ 일본어 공부하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일본을 자주 가다 보니 갈 때마다 느끼는 거지만 언어적인 부분에서 아쉬운 게 굉장히 많았다. 일본에 대한 문화를 더 알고 싶고 더 얘기하고 싶어도 언어가 안되니 망설여지고 조심스러워지는 부분들이 있어서 2024년은 정말 꾸준히 공부해 볼 생각이다. 이미 미니학습지 환급원정대를 시작했고, 꼭 100% 환급받고 일본 콘텐츠도 많이 접하고 보도록 시간을 더 쪼개봐야겠다. 이 악물었다. 진짜 이번엔.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다행히 같이할 회사 동료가 있어서 좀 더 으쌰으쌰 할 수 있을 것 같다. Cindy ありがとうございます。&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdk97I/btsEGgJJy2o/KgJfoH5mzEwwTAuuKrVo8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdk97I/btsEGgJJy2o/KgJfoH5mzEwwTAuuKrVo8K/img.png&quot; data-alt=&quot;데일리 일본어 스터디 노트: 2주차까지는 잘하고있다!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdk97I/btsEGgJJy2o/KgJfoH5mzEwwTAuuKrVo8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdk97I%2FbtsEGgJJy2o%2FKgJfoH5mzEwwTAuuKrVo8K%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;2436&quot; height=&quot;1488&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1488&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데일리 일본어 스터디 노트: 2주차까지는 잘하고있다!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1707574031240&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;미니학습지 일본어 환급원정대 언박싱! feat. 일본어 뿌시기 대작전&quot; data-og-description=&quot;계속 일본어 공부를 하고 싶어서 작년에도 돈을 내고 강의를 들었지만 결과는 대실패하였다...ㅠ 언어 공부는 일과 병행하기는 쉽지 않은 것 같다. 회사일 + 사이드 프로젝트 + 개인 공부 등으로 &quot; data-og-host=&quot;haranglog.tistory.com&quot; data-og-source-url=&quot;https://haranglog.tistory.com/34&quot; data-og-url=&quot;https://haranglog.tistory.com/34&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hWnnA/hyVjgrZEeY/A2uzp97YhsF51b85nVAdbK/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/cKP45Y/hyVf23cUu2/vUf6iTx2n4KoCW0mo8kVJ0/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/AUU49/hyVi9GnZbj/BtJkdQKifqmDWXsXgaczQk/img.jpg?width=1080&amp;amp;height=1440&amp;amp;face=0_0_1080_1440&quot;&gt;&lt;a href=&quot;https://haranglog.tistory.com/34&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://haranglog.tistory.com/34&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hWnnA/hyVjgrZEeY/A2uzp97YhsF51b85nVAdbK/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/cKP45Y/hyVf23cUu2/vUf6iTx2n4KoCW0mo8kVJ0/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/AUU49/hyVi9GnZbj/BtJkdQKifqmDWXsXgaczQk/img.jpg?width=1080&amp;amp;height=1440&amp;amp;face=0_0_1080_1440');&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;미니학습지 일본어 환급원정대 언박싱! feat. 일본어 뿌시기 대작전&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;계속 일본어 공부를 하고 싶어서 작년에도 돈을 내고 강의를 들었지만 결과는 대실패하였다...ㅠ 언어 공부는 일과 병행하기는 쉽지 않은 것 같다. 회사일 + 사이드 프로젝트 + 개인 공부 등으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;haranglog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2️⃣ 스쿠버 다이빙 or 프리다이빙 자격증 취득&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제주도에서 스쿠버다이빙 체험을 해보고 동남아의 맑은 바닷속을 보고 싶어 졌다. 얼마나 아름다울지..&lt;br /&gt;그래서 2024년 계획으로 제주도 워케이션하면서 스쿠버다이빙 또는 프리다이빙 자격증을 취득해 볼 계획이다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3️⃣ 여행 및 워케이션&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년에 꼭 가고 싶은 여행지 몽골! 몽골카페에서 열심히 좋은 동행자를 구해보자.&lt;br /&gt;그리고 여름에 꼭 가야만 하는 낭만 제주도! 2024년에도 제주도 워케이션을 즐기러 가야겠다.&lt;br /&gt;마지막으로 일본..! 또본! 또 가보자구!&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h2 style=&quot;color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;  회고를 마무리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘에는 시간이 가장 아까운 날들의 연속이다. 시간은 정말 돈으로 살 수 없어.. 나도 늙고 있다는 신호인가.. 열심히 살고 열심히 즐기며 후회 없는 하루를 보내며 살아야겠다는 생각이 든다.&lt;br /&gt;그래서 그런가 2023년은 정말로 다른 의미로 열심히 살아온 한 해였다. 2022년에는 정말 개발&quot;만&quot; 했다면, 2023년은 개발&quot;과&quot; 나를 챙기도록 많은 노력을 했던 것 같다. 노력을 많이 했지만 나를 성향까지 바꾸기에는 불가능하다. 내 기준에서는 그래도 많이 개선된 것 같으니 2023년의 나는 만족스럽다. 아마 주변에서 응원해 주고 도와주는 사람들이 없었으면 이미 무너져 내렸지 않았을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023년에 여러 체험을 했다고 해서 내가 자기 계발에 소홀했다고 생각하지는 않는다. 시간이 날 때마다 항상 코드를 치고 있으니. 개인적으로 유튜브, SNS와 같은 도파민 기계보다 개발이 더 재밌고 개발뿐만 아니라 배움에서 얻는 도파민이 더 보람차고 만족스럽다. 특히 2024년은 기술적인 부분도 중요하겠지만, 커뮤니케이션을 향상하도록 노력해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발적인 부분 회고 이외에 개인적인 회고도 많아서 내용이 많아졌는데, 오히려 한 해를 정리할 수 있던 기회가 된 것 같다. 돌아보니 정말 많은 것들을 한 것 같다. 적어도 2022년과 다르게 긍정적인 부분이라면 2022년에는 남과 많은 비교를 하여 임포스터 신드롬 증상도 많았었는데, 2023년의 나는 확실히 많이 나아진 것 같다. 다만, 강박과도 같은 &quot;성장&quot;해야 한다는 마음가짐과 부담은 여전한 것 같다. 하루라도 쉬면 안 되게 하는 부담이 여전히 나를 가장 힘들게 하지 않았나 싶다. 힘든 걸 알면서도 못 내려놓는 내가 바보 같긴 하지만 뭐.. 어쩔 수 있을까...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 2023년 힘들거나 지칠 때 항상 주변에서 도와주고 응원해 주는 사람들이 함께 있었기에 더 나은 사람이 될 수 있었고, 성장할 수 있었던 것 같다. 항상 내 편이 되어주는 주변 사람들 너무 고맙고 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2024년에는&lt;span&gt;&amp;nbsp;그 사람들에게 더 많은 고마움을 표현해야겠다. 그리고&amp;nbsp;&lt;/span&gt;&lt;/span&gt;항상 긍정적인 영향을 주는 제니에게 고마움을 전하며 길고 긴 2023년 회고를 마무리하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>2023년 회고</category>
      <category>프론트엔드 개발자 회고</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/35</guid>
      <comments>https://haranglog.tistory.com/35#entry35comment</comments>
      <pubDate>Mon, 12 Feb 2024 17:26:13 +0900</pubDate>
    </item>
    <item>
      <title>미니학습지 일본어 환급원정대 언박싱! feat. 일본어 뿌시기 대작전</title>
      <link>https://haranglog.tistory.com/34</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_KakaoTalk_Photo_2024-01-31-22-07-37 002.jpeg&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;1053&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJBbdk/btsEhIzgqo8/5ujEPk9qD7EiRkhd2cVIMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJBbdk/btsEhIzgqo8/5ujEPk9qD7EiRkhd2cVIMk/img.png&quot; data-alt=&quot;알찬 구성의 미니학습지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJBbdk/btsEhIzgqo8/5ujEPk9qD7EiRkhd2cVIMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJBbdk%2FbtsEhIzgqo8%2F5ujEPk9qD7EiRkhd2cVIMk%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;620&quot; height=&quot;827&quot; data-filename=&quot;edited_KakaoTalk_Photo_2024-01-31-22-07-37 002.jpeg&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;1053&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;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;007&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/007.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/007.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;언어 공부는 &lt;/span&gt;&lt;/span&gt;일과 병행하기는 쉽지 않은 것 같다. 회사일 + 사이드 프로젝트 + 개인 공부 등으로 인해 시간관리가 쉽지 않아서 어떻게 하면 작지만 꾸준히 할 수 있을까 생각하다가 2024년 새해도 밝았겠다~ 다시 도전해 보기로 마음먹고 여러 종류의 일본어 관련 강의 / 학습지를 찾던 와중에 미니학습지를 신청했다..!&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;미니학습지중에서 종류가 많았는데 그냥 신청만 해서는 잘 안 할 것 같아서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://studymini.com/expedition/&quot;&gt;환급원정대&lt;/a&gt;가 보이길래 신청했다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(사실... 가벼운 학습지 환급원정대도 있었지만.. 유튭 쇼츠는 자신이 없어서 바로 포기.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;거기다! 이것도 뭔가 하다가 포기할 것 같아서 채찍질해 줄 친한 회사 동료분과 일본어 공부를 같이 하기로 하였다. (먼저 포기하는 사람이 밥 맥이기~)&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&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;2230&quot; data-origin-height=&quot;1306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GGJKR/btsEeK5Hun5/63becJQU6skAq3tiNBbx41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GGJKR/btsEeK5Hun5/63becJQU6skAq3tiNBbx41/img.png&quot; data-alt=&quot;일뿌대 화이팅!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GGJKR/btsEeK5Hun5/63becJQU6skAq3tiNBbx41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGGJKR%2FbtsEeK5Hun5%2F63becJQU6skAq3tiNBbx41%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;806&quot; height=&quot;472&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;1306&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일뿌대 화이팅!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깔끔하게 포장되어 왔고, 단계별로 색상이 무지개색으로 되어있었다.&lt;br /&gt;베이직 단계뿐만 아니라, 중급 단계와 단어집 등 여러 가지의 학습지가 있었다. + 온라인 강의까지.&lt;span style=&quot;color: #333333; text-align: start;&quot; data-grammar=&quot;{&amp;quot;input&amp;quot;:&amp;quot;3일치&amp;quot;,&amp;quot;output&amp;quot;:&amp;quot;3일 치&amp;quot;,&amp;quot;etype&amp;quot;:&amp;quot;space&amp;quot;}&quot; data-grammar-id=&quot;grammar2&quot; data-grammar-focus=&quot;false&quot;&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/r5Fak/btsEf1TDmSs/tU4dNxFalbXXefsqlynwwk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r5Fak/btsEf1TDmSs/tU4dNxFalbXXefsqlynwwk/img.jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot; data-filename=&quot;KakaoTalk_Photo_2024-01-31-22-07-37 003.jpeg&quot; data-is-animation=&quot;false&quot; style=&quot;width: 35.5814%; margin-right: 10px;&quot; data-widthpercent=&quot;36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r5Fak/btsEf1TDmSs/tU4dNxFalbXXefsqlynwwk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr5Fak%2FbtsEf1TDmSs%2FtU4dNxFalbXXefsqlynwwk%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;1080&quot; height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHGIjS/btsEdJGbf80/t31kgFVIWpEf1HzIXlqLDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHGIjS/btsEdJGbf80/t31kgFVIWpEf1HzIXlqLDk/img.png&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot; data-filename=&quot;edited_KakaoTalk_Photo_2024-01-31-22-07-36 001.jpeg&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;64&quot; style=&quot;width: 63.2558%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHGIjS/btsEdJGbf80/t31kgFVIWpEf1HzIXlqLDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHGIjS%2FbtsEdJGbf80%2Ft31kgFVIWpEf1HzIXlqLDk%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;1440&quot; height=&quot;1080&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;size16&quot;&gt;3일치 해보았는데 K-직장인 입장으로써 부담 없이 할 수 있는 양이다. 그리고 강의도 있도 있어서 지루하지도 않았다. &lt;br /&gt;사실 부담이 좀 더 있어도 괜찮을 것 같다.ㅋㅋ 강의는 미리 듣는 건 가능하다길래 시간 있을 때에는 2강씩 해보려고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_KakaoTalk_Photo_2024-01-31-22-45-41.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/k6sRy/btsEdHaw3AE/gRJATuRuoPKgpVsR6NifUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k6sRy/btsEdHaw3AE/gRJATuRuoPKgpVsR6NifUK/img.png&quot; data-alt=&quot;학습지 내용이 나와도 되나..? 에라 모르겠다~ 미니학습지 1단계 어딘가..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k6sRy/btsEdHaw3AE/gRJATuRuoPKgpVsR6NifUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk6sRy%2FbtsEdHaw3AE%2FgRJATuRuoPKgpVsR6NifUK%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;615&quot; height=&quot;461&quot; data-filename=&quot;edited_KakaoTalk_Photo_2024-01-31-22-45-41.jpeg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;학습지 내용이 나와도 되나..? 에라 모르겠다~ 미니학습지 1단계 어딘가..&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;2024년 새해에 다행히 1월 말부터 시작하는 환급원정대에 신청하게 되어 올 한 해 의미 있게 시작할 수 있을 것 같다.&lt;br /&gt;일본 여행을 정말 많이 가는 1인으로써 일본어를 잘했으면 여행의 질이 더 높았을텐데라는 아쉬움이 많았다. 언젠가 나도 회화가 가능할 만큼의 일본어를 구사할 수 있었으면 좋겠다. 일본 콘텐츠도 많이 봐야겠다.&lt;br /&gt;꼭 100% 환급을 받고야 말겠어...! 미니학습지 내 돈 다 내놔..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ほんどうに　ありがとうございます。&lt;/b&gt;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;046&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/046.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/046.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&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>미니학습지</category>
      <category>스터디미니</category>
      <category>일본어인강</category>
      <category>일본어학습지</category>
      <category>일본어회화</category>
      <category>환급기원</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/34</guid>
      <comments>https://haranglog.tistory.com/34#entry34comment</comments>
      <pubDate>Wed, 31 Jan 2024 23:02:50 +0900</pubDate>
    </item>
    <item>
      <title>벌써 2년 프론트엔드 개발자의 2022년 회고</title>
      <link>https://haranglog.tistory.com/33</link>
      <description>&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;D E S I G N E R (1).gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCR37G/btrUXoSlNHG/WOk8eiKgndaM2szl11Bqu1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCR37G/btrUXoSlNHG/WOk8eiKgndaM2szl11Bqu1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCR37G/btrUXoSlNHG/WOk8eiKgndaM2szl11Bqu1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bCR37G/btrUXoSlNHG/WOk8eiKgndaM2szl11Bqu1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;500&quot; data-filename=&quot;D E S I G N E R (1).gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해도 어김없이 찾아온 연말 회고 타임! 벌써 3번째 연말 회고이다. 회고는 하면 할수록 자기 객관화에 많은 도움을 준다. 특히 나처럼 머릿속에 생각이 가득 차있고 정리가 안될 때는 생각을 글로 정리하면 나를 되돌아볼 수 있고 집중할 수 있게 되어 머릿속을 비울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;a href=&quot;https://haranglog.tistory.com/29&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021년&lt;/a&gt;에는 어떤 계획을 세웠고 잘 지켰는지 확인해 볼까?&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  작년에 세웠던&lt;span&gt;&amp;nbsp;&lt;/span&gt;계획은 잘 지켰어?&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  Github&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; Daily Commit&lt;/span&gt;&amp;nbsp;계속 유지하기&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzr8lp/btrURPckn8D/Gfv1YVbK5na0PCFu64AFaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzr8lp/btrURPckn8D/Gfv1YVbK5na0PCFu64AFaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzr8lp/btrURPckn8D/Gfv1YVbK5na0PCFu64AFaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbzr8lp%2FbtrURPckn8D%2FGfv1YVbK5na0PCFu64AFaK%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;1610&quot; height=&quot;428&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Daily commit은 여전히 꾸준하게 유지하고 있다. 올해 daily commit 2년 차가 되어서 &lt;a href=&quot;https://haranglog.tistory.com/30&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;회고&lt;/a&gt;도 작성했었다. 작년에 힘들면 그만두겠다고 했었는데 아직까지는 할만하다. 역시 습관이 되기까지가 어려운 거지 습관이 되면 매일 양치를 하는 것처럼 당연히 해야 할 일 중에 하나로 바뀌는 것 같다.(근데 요즘에는 이런 습관을 가지는 게 좋은 걸까라는 의문이 들기도 한다...) 내 기준에서는 행동이 습관이 되기까지 길어봐야 약 3달 정도였던 것 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;영국 런던대학(UCL)의 필리파 랠리 Phillippe Lally 교수 연구팀은 새로운 행동이 습관화되는 데는 최소 21일이 걸리며, 행동이 습관으로 자리 잡는 데는 66일이 걸린다는 '66일의 법칙'을 발표했습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 daily commit을 습관으로 만들어 매일 뭐라도 하자는 게 정말 좋은 걸까라는 생각도 들고 계속하는 게 맞는지라는 생각도 들지만 어쨌든 아직까지 나한테는 daily commit이 부정적인 영향보다 긍정적인 영향이 더 많다고 느껴지기에 유지해보려고 한다. 이 daily commit이 나를 지금보다 더 지치게 하고 옥죄어 올 때 그만둘 생각이다.&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;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;건강부터..!&lt;/span&gt;&lt;/h4&gt;
&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  &lt;s&gt;Conners&lt;/s&gt; Soople&amp;nbsp;서비스&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;런칭하기&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;작년 12월부터 전 회사 디자이너 분이랑 느리지만 천천히 해오고 있는 스터디와 프로젝트를 모와주도록 도와주는 &lt;a href=&quot;https://soople.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;사이드 프로젝트&lt;/a&gt;를 하고 있다. 아쉽지만 런칭에는 실패해서 계획은 못 지켰다.  우리가 사이드 프로젝트를 시작했을 때도 회사일을 첫 번째라고 생각하고 시작했고 사이드 프로젝트를 하면서 스트레스를 받으면서 하지는 말자고 했기 때문에 당연히 속도가 늦을 수밖에 없던 것 같기도 하다. 중간에 잠깐 쉬기도 했지만 그래도 1년 동안 꾸준히 미팅하며 지금까지 해오고 있으니 그걸로 만족한다.  &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;1446&quot; data-origin-height=&quot;1946&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNUzQ6/btrUNWpVc5K/4K9AqCBmDyPdvyT8yKwWiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNUzQ6/btrUNWpVc5K/4K9AqCBmDyPdvyT8yKwWiK/img.png&quot; data-alt=&quot;notion 미팅 기록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNUzQ6/btrUNWpVc5K/4K9AqCBmDyPdvyT8yKwWiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNUzQ6%2FbtrUNWpVc5K%2F4K9AqCBmDyPdvyT8yKwWiK%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;499&quot; height=&quot;672&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;1946&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;notion 미팅 기록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리의 서비스 명은 Conners에서 Soople로 바꿨고, 브랜드 컬러도 변경했다. Soople이라는 이름은 스터디와 프로젝트의 앞글자를 가져왔고 스프가 되었고, 수플이라는 이름은 나무보단 숲을 보자(나무보단 수플보자~ )라는 의미로 수플에는 스터디와 프로젝트로 가득한 숲이라는 뜻으로 NF스러운 이름으로 지어졌다.&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;2334&quot; data-origin-height=&quot;1284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x6zeC/btrUXnTao32/KKBizkXFIuh6udcECvIeEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x6zeC/btrUXnTao32/KKBizkXFIuh6udcECvIeEK/img.png&quot; data-alt=&quot;figma - user flow&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x6zeC/btrUXnTao32/KKBizkXFIuh6udcECvIeEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx6zeC%2FbtrUXnTao32%2FKKBizkXFIuh6udcECvIeEK%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;755&quot; height=&quot;415&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1284&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;figma - user flow&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능은 대부분 완성되었지만 앞으로 하고 싶은 계획들을 생각하니까 firebase만을 사용하는 데에는 한계점을 느꼈고 백엔드가 절실히 필요하다고 생각이 들어서 전에 같이 일하던 백엔드 개발자분에게 같이 해보자고 했더니 너무 좋다고 하셨다! 천군만마를 얻은 느낌이랄까? 내년에는 꼭 런칭하고 싶어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일 년 동안 같이 투닥투닥 만들어 온 프로덕트 디자이너 제니 너무 고맙고 앞으로 같이할 백엔드 마롱에게도 같이 하겠다 해줘서 고맙다고 말해주고 싶다.  &amp;zwj;♂️&lt;/p&gt;
&lt;figure id=&quot;og_1672303130302&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - jenny-harang/soople:   WIP   나무 말구 수플보자~~~ soople!!  &quot; data-og-description=&quot;  WIP   나무 말구 수플보자~~~ soople!!  . Contribute to jenny-harang/soople development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jenny-harang/soople&quot; data-og-url=&quot;https://github.com/jenny-harang/soople&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cjjZeg/hyQ5DLNkDU/TtytR3LGQKoDIRPSoIPN1k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/jenny-harang/soople&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jenny-harang/soople&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cjjZeg/hyQ5DLNkDU/TtytR3LGQKoDIRPSoIPN1k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - jenny-harang/soople:   WIP   나무 말구 수플보자~~~ soople!!  &lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  WIP   나무 말구 수플보자~~~ soople!!  . Contribute to jenny-harang/soople development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1672303100989&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;스터디와 사이드 프로젝트, soople&quot; data-og-description=&quot;스터디와 사이드 프로젝트 수플과 함께 시작하세요!&quot; data-og-host=&quot;soople.io&quot; data-og-source-url=&quot;https://soople.io&quot; data-og-url=&quot;https://soople.io&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ctMuLA/hyQ354f5RO/W4pW0r14b2ukclryrDusek/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://soople.io&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://soople.io&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ctMuLA/hyQ354f5RO/W4pW0r14b2ukclryrDusek/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;스터디와 사이드 프로젝트, soople&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;스터디와 사이드 프로젝트 수플과 함께 시작하세요!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;soople.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이거는 내 개인적인 계획이기보단 회사의 계획이기도 했지만 개인적인 계획으로 세운 이유는 런칭한다면 나에게 너무 의미가 클 것 같았다.&lt;br /&gt;역시나 스스로를 가두어두던 한계를 부수고 4월에 MVP 런칭, 11월에 V1.0을 런칭하게 되었다. 정식 런칭을 했을 때 로고에 붙어있던 베타를 때는 게 얼마나 나에게 큰 의미가 있던지.. 처음부터 이 서비스에 정이 많아서 그랬는지 더 잘할 수 있었는데 라는 아쉬움도 많았고, 좋은 경험을 쌓은 것 같다. 또다시 애정 가득한 서비스를 만들어보고 싶다.&lt;br /&gt;역시나 마지막 말은 같이 서비스를 만들어온 동료들에게 감사함을 전해주고 싶다.  &amp;zwj;♂️&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;figure id=&quot;og_1672303631911&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;[회고] 주니어 프론트엔드 개발자의 좌충우돌 웹 거래소 런칭기&quot; data-og-description=&quot;이 글은 작년(2021년) 12월부터 올해(2022년) 4월에 카사코리아의 웹 거래소 MVP(Minimum Viable Product) 런칭 과정과 런칭 이후 현재(글 작성일 기준 2022년 9월)까지의 겪었던 회고입니다. MVP 런칭 후 회고&quot; data-og-host=&quot;haranglog.tistory.com&quot; data-og-source-url=&quot;https://haranglog.tistory.com/31&quot; data-og-url=&quot;https://haranglog.tistory.com/31&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/doWJYu/hyQ36PFzWs/LNeqzNhuHQywiMHdcOLTZ0/img.png?width=800&amp;amp;height=845&amp;amp;face=0_0_800_845,https://scrap.kakaocdn.net/dn/KWrNd/hyQ34dbqVH/T4YsvIkVBdItxJSfjFkkRk/img.png?width=800&amp;amp;height=845&amp;amp;face=0_0_800_845,https://scrap.kakaocdn.net/dn/dOauI7/hyQ3VAvSCm/k1e8QuJgKWKQXeCzmxCygk/img.png?width=1732&amp;amp;height=2158&amp;amp;face=0_0_1732_2158&quot;&gt;&lt;a href=&quot;https://haranglog.tistory.com/31&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://haranglog.tistory.com/31&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/doWJYu/hyQ36PFzWs/LNeqzNhuHQywiMHdcOLTZ0/img.png?width=800&amp;amp;height=845&amp;amp;face=0_0_800_845,https://scrap.kakaocdn.net/dn/KWrNd/hyQ34dbqVH/T4YsvIkVBdItxJSfjFkkRk/img.png?width=800&amp;amp;height=845&amp;amp;face=0_0_800_845,https://scrap.kakaocdn.net/dn/dOauI7/hyQ3VAvSCm/k1e8QuJgKWKQXeCzmxCygk/img.png?width=1732&amp;amp;height=2158&amp;amp;face=0_0_1732_2158');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[회고] 주니어 프론트엔드 개발자의 좌충우돌 웹 거래소 런칭기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 작년(2021년) 12월부터 올해(2022년) 4월에 카사코리아의 웹 거래소 MVP(Minimum Viable Product) 런칭 과정과 런칭 이후 현재(글 작성일 기준 2022년 9월)까지의 겪었던 회고입니다. MVP 런칭 후 회고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;haranglog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1672303651535&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;모두를 위한 건물 투자, 카사&quot; data-og-description=&quot;꼬박꼬박 임대료 배당금부터 매매차익까지&quot; data-og-host=&quot;kasa.co.kr&quot; data-og-source-url=&quot;https://www.kasa.co.kr/&quot; data-og-url=&quot;https://kasa.co.kr&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/PMaSF/hyQ5Ghr7IT/Qe6RFKbstwSJDKi2mFKXkK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/qxJif/hyQ5AO4dtZ/dNnpJwmRqJV5o3HRIEVkUk/img.png?width=1600&amp;amp;height=708&amp;amp;face=0_0_1600_708&quot;&gt;&lt;a href=&quot;https://www.kasa.co.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.kasa.co.kr/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/PMaSF/hyQ5Ghr7IT/Qe6RFKbstwSJDKi2mFKXkK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/qxJif/hyQ5AO4dtZ/dNnpJwmRqJV5o3HRIEVkUk/img.png?width=1600&amp;amp;height=708&amp;amp;face=0_0_1600_708');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;모두를 위한 건물 투자, 카사&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;꼬박꼬박 임대료 배당금부터 매매차익까지&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kasa.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두둥! 내가 이 계획을 지킬 수 있을 줄이야! 10월까지 코로나로 인해 여행이 제한되었었는데 다행히 제한이 풀리게 되어서 무려 4주 동안 일본 여행을 갔다 왔다. 버킷리스트가 &lt;span style=&quot;color: #000000;&quot;&gt;한 달 동안&lt;/span&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;KakaoTalk_Photo_2022-12-29-18-07-55.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sKVR5/btrUSvENfFx/HdKBMjxh23Anu7bvxLK89K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sKVR5/btrUSvENfFx/HdKBMjxh23Anu7bvxLK89K/img.jpg&quot; data-alt=&quot;후쿠오카 -&amp;amp;amp;nbsp;모모치 해변&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sKVR5/btrUSvENfFx/HdKBMjxh23Anu7bvxLK89K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsKVR5%2FbtrUSvENfFx%2FHdKBMjxh23Anu7bvxLK89K%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;377&quot; height=&quot;503&quot; data-filename=&quot;KakaoTalk_Photo_2022-12-29-18-07-55.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;후쿠오카 -&amp;amp;nbsp;모모치 해변&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3년 만에 해외여행이라 더 의미 있었고 여행을 갈 때는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;항상&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;계획을 세웠던 내가 큰 계획만 세우고 여유롭게 여행을 해보며 여유롭고 자유로운 여행에 대한 매력을 알게 되었고 두려움을 떨쳐내고 혼자 해외여행을 하며 얻은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;많은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&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;zwj;♂️&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  보육원&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;후원&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;작년 연말에도 &lt;a href=&quot;http://www.swchildren.co.kr/bbs/board.php?bo_table=sw_story&amp;amp;wr_id=310&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;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&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;color: #000000;&quot;&gt;언제나 즐겁고 긍정적이고&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;행복하게!&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;흠... 긍정적이었을까? 행복했을까? 긍적의 기준은 뭐고 행복의 기준은 뭘까? 잘 모르겠다.&lt;br /&gt;머릿속에 내 생각을 멈추게 해주는 스위치 버튼이 있었으면 좋겠다.&lt;br /&gt;&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;color: #000000;&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&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  2022년 더 하고 싶은 얘기 있어?&lt;/span&gt;&lt;/span&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 style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;작년에 세웠던 계획 얘기는 여기까지 하고 올해 더 하고 싶은 얘기를 해보자.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  DND(Developer and Designer) 7기 참여&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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 style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&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&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dnd.ac/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DND&lt;/a&gt;는 개발자와 디자이너가 같이 프로젝트를 할 수 있는 커뮤니티이다. 8주간 DND를 통해서 여러 개발자들과 디자이너와 같이 협업하고 하나의 서비스를 만들고 서로 네트워킹도 할 수 있다. 예전부터 이런 커뮤니티에서 다른 여러 사람들과 함께 프로젝트를 만들어보고 싶었다. 우리 조는 현업자로 팀이 만들어졌었다. 그러다 보니 다들 현업이 제일 우선이었고 중간에 여러 번의 주제 변경과 팀원 변경들로 인해 보여줄 만한 결과물은 못 내서 아쉽긴 했지만 팀원 분들 다들 좋으신 분들이라 온오프 미팅을 하면서 재밌게 참여했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉬웠던 점은 대부분 대학생분들이고 방학 때 하는 활동이기 때문에 현업에 있는 분들이랑 대학생 분들이 시간적인 부분이 맞지 않아서 힘든 부분이 있어 보였다. 다음번엔 운영진으로 참여하거나 기회가 있다면 DND와 비슷한 다른 커뮤니티에 참여하고 싶다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;dnd.jpeg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSe1CY/btrURPRfq4R/kwD3Iw6B9u1SD23ZOuaEb1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSe1CY/btrURPRfq4R/kwD3Iw6B9u1SD23ZOuaEb1/img.jpg&quot; data-alt=&quot;DND 마지막 날&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSe1CY/btrURPRfq4R/kwD3Iw6B9u1SD23ZOuaEb1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSe1CY%2FbtrURPRfq4R%2FkwD3Iw6B9u1SD23ZOuaEb1%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;556&quot; height=&quot;417&quot; data-filename=&quot;dnd.jpeg&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DND 마지막 날&lt;/figcaption&gt;
&lt;/figure&gt;
&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;color: #000000;&quot;&gt;  나의 첫 오프라인 컨퍼런스 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FEConf 2022&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 참여&lt;/span&gt;&lt;/span&gt;&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프론트엔드 개발자가 되고 나서 코로나로 인해서 컨퍼런스는 전부 온라인으로 진행되었었는데 이번에 드디어 오프라인으로 참여할 수 있게 되었다. 참여한 컨퍼런스는 &lt;a href=&quot;https://2022.feconf.kr&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;feconf 2022&lt;/a&gt;로 국내 프론트엔드의 가장 대표적인 컨퍼런스이다. 오랜만에 오프라인 컨퍼런스라 그런지 티켓 예매 오픈 1분 만에 매진되었다고 들었다. 운 좋게 티켓팅?을 성공하게 되어서 참여할 수 있게 되었다.&lt;/span&gt;&lt;/span&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;KakaoTalk_Photo_2022-12-29-18-43-09 003.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WKSVX/btrUOQpMpxo/dsXsV1uIOkgUAZ0KqP1moK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WKSVX/btrUOQpMpxo/dsXsV1uIOkgUAZ0KqP1moK/img.jpg&quot; data-alt=&quot;잠실 롯데타워 31층 스카이 라운지에서 열린 feconf 2022&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WKSVX/btrUOQpMpxo/dsXsV1uIOkgUAZ0KqP1moK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWKSVX%2FbtrUOQpMpxo%2FdsXsV1uIOkgUAZ0KqP1moK%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;479&quot; height=&quot;639&quot; data-filename=&quot;KakaoTalk_Photo_2022-12-29-18-43-09 003.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;잠실 롯데타워 31층 스카이 라운지에서 열린 feconf 2022&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람들이 정말 많았고, 좋은 컨퍼런스 발표도 들을 수 있게 되어서 좋은 경험이었다. 내년에도 꼭 참여하고 싶다. &lt;span&gt;역시&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;제일 큰 얻음은 컨퍼런스에 오신 열정적인 프론트엔드 개발자 분들을 보면서 동기부여를 얻었다는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2022-12-29-18-43-08 001.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxTkKy/btrUQaIcvQE/a5DC7wGDjMGKHsOJdU1g50/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxTkKy/btrUQaIcvQE/a5DC7wGDjMGKHsOJdU1g50/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxTkKy/btrUQaIcvQE/a5DC7wGDjMGKHsOJdU1g50/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxTkKy%2FbtrUQaIcvQE%2Fa5DC7wGDjMGKHsOJdU1g50%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;474&quot; height=&quot;632&quot; data-filename=&quot;KakaoTalk_Photo_2022-12-29-18-43-08 001.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;color: #000000;&quot;&gt;  체크인 리얼밸리 - 한기용 멘토링 오프라인 토크 이벤트 참여&lt;/span&gt;&lt;/span&gt;&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;유튜브 &lt;a href=&quot;https://youtu.be/nLL409se8sM&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;eo에서 한기용 님의 영상&lt;/a&gt;을 보게 되었다. 한기용 님의 27년간의 영상을 통해 경험을 얼핏 느끼면서 굉장히 존경스럽다고 느껴졌고 나중에 나도 경험이 쌓이면 쌓일수록 &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;/span&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;1080&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qssTz/btrUV4Nnsfj/8RJbjkiFSTILpfZFJHGlik/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qssTz/btrUV4Nnsfj/8RJbjkiFSTILpfZFJHGlik/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qssTz/btrUV4Nnsfj/8RJbjkiFSTILpfZFJHGlik/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqssTz%2FbtrUV4Nnsfj%2F8RJbjkiFSTILpfZFJHGlik%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;633&quot; height=&quot;433&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가장 와닿았던 내용은 호기심 기반으로 된 사람들과의&amp;nbsp;&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 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;&lt;span style=&quot;color: #000000;&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&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1672322289315&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;체크인 리얼밸리 - 한기용 멘토링 오프라인 토크 이벤트 정리 및 회고&quot; data-og-description=&quot;이 글은 2022년 10월 11일 화요일에 EO 주관으로 Grepp의 CTO이신 한기용 님의 오프라인 토크이벤트를 참석 후 정리한 내용입니다. 오프라인으로 듣고 작성한 내용이라 잘못된 부분이 있을 수도 있으&quot; data-og-host=&quot;haranglog.tistory.com&quot; data-og-source-url=&quot;https://haranglog.tistory.com/32&quot; data-og-url=&quot;https://haranglog.tistory.com/32&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/TvxUG/hyQ5GaTshE/rGCPHcQCGgzdIodl5pPNu1/img.jpg?width=800&amp;amp;height=546&amp;amp;face=132_210_159_240,https://scrap.kakaocdn.net/dn/g3YRe/hyQ5wMSdpT/ZhylLeMycmBkQBoXYSmaqK/img.jpg?width=800&amp;amp;height=546&amp;amp;face=132_210_159_240,https://scrap.kakaocdn.net/dn/FggJJ/hyQ5ERF1yK/UAr3gUiLr9PRN6RS0S5SkK/img.jpg?width=1080&amp;amp;height=738&amp;amp;face=180_280_223_327&quot;&gt;&lt;a href=&quot;https://haranglog.tistory.com/32&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://haranglog.tistory.com/32&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/TvxUG/hyQ5GaTshE/rGCPHcQCGgzdIodl5pPNu1/img.jpg?width=800&amp;amp;height=546&amp;amp;face=132_210_159_240,https://scrap.kakaocdn.net/dn/g3YRe/hyQ5wMSdpT/ZhylLeMycmBkQBoXYSmaqK/img.jpg?width=800&amp;amp;height=546&amp;amp;face=132_210_159_240,https://scrap.kakaocdn.net/dn/FggJJ/hyQ5ERF1yK/UAr3gUiLr9PRN6RS0S5SkK/img.jpg?width=1080&amp;amp;height=738&amp;amp;face=180_280_223_327');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;체크인 리얼밸리 - 한기용 멘토링 오프라인 토크 이벤트 정리 및 회고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 2022년 10월 11일 화요일에 EO 주관으로 Grepp의 CTO이신 한기용 님의 오프라인 토크이벤트를 참석 후 정리한 내용입니다. 오프라인으로 듣고 작성한 내용이라 잘못된 부분이 있을 수도 있으&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;haranglog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&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&gt;&lt;/span&gt;&lt;/span&gt;&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;약 1년 4개월 간의 끝으로 11월에 카사 코리아를 퇴사하게 되었다. 카사에 입사해서 &lt;a href=&quot;https://haranglog.tistory.com/27&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;회고 글&lt;/a&gt;을 작성했던 게 엊그제&amp;nbsp;같은데 시간이 참 빠르다. 퇴사하면서 어떤 걸 가장 얻었다고 할 수 있을까라고 물어본다면 1초의 망설임도 없이 같이 일한 동료들이라고 얘기할 수 있을 것 같다. 카사에서 너무나 많은 좋은 사람들을 만났고 일하면서 좋은 추억들과 경험 어떤 거 하나 빼놓지 않고 정말 많이 배웠던 것 같다.&lt;br /&gt;언젠가는 다시 그분들과 새로운 곳에서 하나의 목표로 새로운 서비스를 만들며 즐겁게 일할 수 있는 날이 왔으면 하는 바람이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;KakaoTalk_Image_2022-12-29-23-32-06.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/DQojJ/btrUVa8aZr3/hgiEAvqIF7RIyeXY5Ebcak/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DQojJ/btrUVa8aZr3/hgiEAvqIF7RIyeXY5Ebcak/img.jpg&quot; data-alt=&quot;이제는 추억의 한페이지로...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DQojJ/btrUVa8aZr3/hgiEAvqIF7RIyeXY5Ebcak/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDQojJ%2FbtrUVa8aZr3%2FhgiEAvqIF7RIyeXY5Ebcak%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;487&quot; height=&quot;365&quot; data-filename=&quot;KakaoTalk_Image_2022-12-29-23-32-06.jpeg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이제는 추억의 한페이지로...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1672323822115&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;[회고] 프론트엔드 개발자로서의 첫 발걸음을 내딛다!&quot; data-og-description=&quot;이 글은 프론트엔드 개발자로서 취업 후 작성하는 회고 글입니다!   저의 간단한 배경은 이전에 SI 개발자로 8개월 정도 일한 경험이 있었고 SI 회사에 입사하기 전에 프론트엔드 개발자로 눈이&quot; data-og-host=&quot;haranglog.tistory.com&quot; data-og-source-url=&quot;https://haranglog.tistory.com/27&quot; data-og-url=&quot;https://haranglog.tistory.com/27&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KRCWe/hyQ5DkV9GR/E4oNT2ABVlNRZLnkR3ywAK/img.jpg?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/HMFEE/hyQ5CTSw5S/870CQ122A0IY4nnrBbpEdk/img.jpg?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/c03kbp/hyQ5v8gyxD/hdnPjTWt97pxc2wZupmbPk/img.jpg?width=1280&amp;amp;height=1280&amp;amp;face=0_0_1280_1280&quot;&gt;&lt;a href=&quot;https://haranglog.tistory.com/27&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://haranglog.tistory.com/27&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KRCWe/hyQ5DkV9GR/E4oNT2ABVlNRZLnkR3ywAK/img.jpg?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/HMFEE/hyQ5CTSw5S/870CQ122A0IY4nnrBbpEdk/img.jpg?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/c03kbp/hyQ5v8gyxD/hdnPjTWt97pxc2wZupmbPk/img.jpg?width=1280&amp;amp;height=1280&amp;amp;face=0_0_1280_1280');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[회고] 프론트엔드 개발자로서의 첫 발걸음을 내딛다!&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 프론트엔드 개발자로서 취업 후 작성하는 회고 글입니다!   저의 간단한 배경은 이전에 SI 개발자로 8개월 정도 일한 경험이 있었고 SI 회사에 입사하기 전에 프론트엔드 개발자로 눈이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;haranglog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  진짜로 2022년을 마무리해볼까?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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 style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  2022년 스스로에게&lt;span&gt;&amp;nbsp;&lt;/span&gt;칭찬하고 싶은 점&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;작년 계획을 잘 지켰는지 돌이켜보면 대부분 잘 지켜져서 만족스러운 한 해가 아니었나 싶다.&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&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&gt;&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&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&gt;&lt;/span&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 style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&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&gt;&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;항상 단점만 찾아다녔었는데 내가 &lt;a href=&quot;https://store.gallup.com/p/en-sg/10108/top-5-cliftonstrengths&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;갤럽 강점 검사&lt;/a&gt;를 통해 &lt;span style=&quot;color: #000000;&quot;&gt;내 강점에 대해서 알게 되었다는 점이다. 가격이 비싸서 &lt;span style=&quot;color: #000000;&quot;&gt;회사에서 갤럽 강점 검사를 하자고 제안을 했었고 프로덕트 전체 인원이 함께 하루 날 잡고 회고 시간에 진행하게 되었다.&lt;/span&gt; 이 시간을 통해서 나의 강점을 알게 되었는데 가장 큰 강점은 책임감이 미친 듯이 높다는 것이다. 그리고 두 번째는 공감하는 능력. 세 번째가 배움이었다. &lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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 style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결과를 보면 일을 실행하는 실행력과 대인관계에서는 강점이 있지만 나의 의사를 전달하거나 표현하는 영향력과 더 나은 결정을 위한 정보를 분석하는 전략적 사고는 아쉬운 부분이 있다고 나왔다. 어찌 보면 당연한 결과인 것 같다. 책임감이 높으니 주어진 일을 무조건 하기 위해서 나은 결정과 선택을 못할 수도 있고, 공감이 높다 보니까 나의 의견을 전달하는 것보다 들어주는 게 상대적으로 더 잘하는 게 당연한 것 같다. 어쨌든 나의 강점을 찾았으니 약점을 찾아 보완하기보다 강점을 더 키워야겠다. 어차피 단점은 쉽게 안 고쳐진다는 한기용 님의 말씀이 생각이 났다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEZzzS/btrUWamyiUk/FXHshN6hSy1HmhR1NeD221/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEZzzS/btrUWamyiUk/FXHshN6hSy1HmhR1NeD221/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEZzzS/btrUWamyiUk/FXHshN6hSy1HmhR1NeD221/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEZzzS%2FbtrUWamyiUk%2FFXHshN6hSy1HmhR1NeD221%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;422&quot; height=&quot;533&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막은 앞으로의 목표와 방향성이 더 명확해졌다는 것. (비밀  )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;  2022년에 두고 가야 할 것&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&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;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;작년에도 두고 오자고 했지만 못 두고 온 것이 있는데 성장이라는 틀이 나를 &lt;span style=&quot;color: #000000;&quot;&gt;너무&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;힘들게 한다는 것이다. 남한테는 관대하지만 나한테는 제일 엄격한 것. 두고 갈 수 있을까?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 아까 위에서 얘기했던 임포스터 신드롬과 완벽주의적 성격.&lt;br /&gt;남과 비교는 그만하고 어제의 나와 비교하자. 조금은 내려두고 여유를 가져보자.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;블로그를 너무 못썼다. 반성한다. 이 글 포함 고작 4개...? 거기다 기술적인 내용은 없다고? 개발자가? 회고글이 전부라고? 개발자 때려치울까?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  반가워! 2023&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해도 내년의 계획을 크게 세워볼까?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Github Daily Commit 계속해서 유지하기&lt;br /&gt;내년에도 해보자고~~&lt;/li&gt;
&lt;li&gt;운동 꾸준히 하기&lt;br /&gt;체력이 돼야 개발도 오래 한다. 꾸준히 운동하자.&lt;/li&gt;
&lt;li&gt;Soople 런칭하기&lt;/li&gt;
&lt;li&gt;이직!&lt;br /&gt;다음 회사는 어디일까? 너무 기대가 된다.&lt;/li&gt;
&lt;li&gt;발표나 강의 또는 멘토 해보기&lt;br /&gt;어디가 됐든 기회가 주어지면 한 번 해보고 싶다. 나를 한 단계 성장시켜 줄거라 믿는다.&lt;/li&gt;
&lt;li&gt;블로그 한 달에 한 개 쓰기&lt;br /&gt;2022년도 블로그 글 쓴 개수를 확인해보니 이 글 포함 4개라니.. 충격이다. 뭐가 됐든 어떤 글이 됐든 달에 하나씩은 써보자.&lt;/li&gt;
&lt;li&gt;기술서적 꾸준히 읽기&lt;br /&gt;이번 년도 몇 권을 읽었는지 기억이 안 난다. 대략 5권 정도 읽은 것 같은데.. 여기서 더 많이 읽겠다는 계획은 안 세우려고 한다. 솔직히 못할 것 같다. 내년에도 5권을 목표로! (&lt;a href=&quot;https://github.com/saseungmin/reading_books_record_repository&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;읽은 책 정리&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;기타&lt;br /&gt;스위스, 일본 여행 가기, 보육원 후원 및 기회가 된다면 자원봉사&lt;/li&gt;
&lt;/ol&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;p data-ke-size=&quot;size16&quot;&gt;2022년 한 해를 되돌아보면 정말 열심히 달려온 것 같다. 뿌듯하기도 하면서 가끔은 나를 이렇게까지 힘들게 하면서까지 해야 할까 싶을 때도 있었다. 조금은 내려놓고 여유롭게 살 수도 있을 것 같은데 말이다. 그렇지만 지금은 조금 더 달리고 싶다. 지금 아니면 언제 할 수 있을까? 하고 싶을 때 할 수 있을 때 더 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 목표를 깨면 더 높은 또 다른 목표가 생기고 그런 목표들을 달성해가면서 하고 싶은 것들이 더 많이 생기는 것 같다. 내가 궁극적으로 이루고자 하는 건 뭘까? 뭐를 위해 이렇게 하는 거지? 끝이 보이지 않는 우주의 신비로움처럼 그 목표의 끝은 알 수가 없다. 하지만 지금 당장 명확한 건 일단 그 목표들을 달성해봐야 한다는 것이다. 그래야 새로운 퀘스트가 나올 테니 말이다. 그리고 그 과정이 생각보다 재밌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 회사는 어디로 갈지 아직 정해지지는 않았다. 어디가 됐든 내가 잘할 수 있는 곳에서 좋은 사람들과 좋은 프로덕트를 만들고 싶다는 것이다. 내가 나를 작은 틀에 가두어 한계를 정하지 말고 내가 어디까지 할 수 있고 어디까지가 한계인지 스스로를 시험해보고 싶어졌다. 이유는 없다. 그냥 궁금해져서 도전해보고 싶어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 한 해도 별 탈 없이 잘 마무리를 짓는 것 같아서 만족스럽고 올해 달성한 계획과 목표를 두고 새로운 계획과 목표로 2023년을 새롭게 달려볼까 한다. 당연히 그 과정은 산을 오르는 것처럼 쉽지는 않겠지만 정상에 도달하는 순간 그 해냈다는 뿌듯함을 보고 산을 등산하지 않는가? 그래서 2023년도는 마라톤처럼 달리기보단 산에 오르는 것처럼 달려볼 생각이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 게으름을 두려워하는 나에게 책을 선물해준 프로덕트 디자이너 데이지에게 감사함을 전하며&amp;nbsp;&lt;a href=&quot;http://www.yes24.com/Product/Goods/108721410&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;게으르다는 착각&lt;/a&gt;이라는 책을 추천하면서 2022년 회고를 마무리하겠다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;게으르다는 착각 내용 중&lt;br /&gt;&lt;br /&gt;p.116&lt;br /&gt;해야 할 일 목록에서 몇 개를 해치웠는지로 우리의 가치를 측정하는 것을 멈출 때, 우리는 비로소 진정으로 중요한 활동을 찾아 추구한다. 사회가 부가한 &quot;당위&quot;가 아니라 진정한 느낌을 바탕으로 우선순위를 정하면, 훨씬 더 큰 진정성을 느낀다. 그리고 자유 시간을 만끽하고 더 느리고, 게으르고, 직관적인 속도로 일에 임할 때 수년간 과로가 남긴 피해를 없애고 회복할 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>2022</category>
      <category>연말 회고</category>
      <category>프론트엔드</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/33</guid>
      <comments>https://haranglog.tistory.com/33#entry33comment</comments>
      <pubDate>Fri, 30 Dec 2022 02:24:13 +0900</pubDate>
    </item>
    <item>
      <title>체크인 리얼밸리 - 한기용 멘토링 오프라인 토크 이벤트 정리 및 회고</title>
      <link>https://haranglog.tistory.com/32</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 글은 2022년 10월 11일 화요일에 EO 주관으로 Grepp의 CTO이신 &lt;a href=&quot;https://eopla.net/events/34&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;한기용 님의 오프라인 토크이벤트&lt;/a&gt;를 참석 후 정리한 내용입니다.&lt;br /&gt;오프라인으로 듣고 작성한 내용이라 잘못된 부분이 있을 수도 있으니 참고해주시고 피드백 부탁드립니다.&lt;br /&gt;아래의 총 3부의 EO 유튜브를 통해서 한기용 님을 알게 되었고 너무 감명 깊게 봐서 오프라인 토크이벤트를 신청 후 참석하게 되었습니다. 해당 유튜브 영상은 토크이벤트 내용의 영상이 아닙니다. 참고해 주세요.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=nLL409se8sM&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bDKc4m/hyQ5v7tTZb/zb9KQEhQ8AVG5xUW83BuGk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=714_178_1040_534&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/nLL409se8sM&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;EO 한기용님의 1부 (총 3부작)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  커리어 성장: 끊임없는 성자과 성숙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커리어는 정글짐&lt;br /&gt;많은 경험들이 상처로만 남지 않으면 도움이 된다. 꼭 위로 갈 필요는 없다.&lt;br /&gt;다양한 역할을 해보자. (리드 역할, 매니저 역할.. etc)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국에는 혼자서만 일하는 게 아니기 때문에 사람을 이끌 수 있는 소통능력이 중요하다.&amp;nbsp;&lt;br /&gt;but, 해보기 전까지는 모른다. 변화를 두려워하지 않는 능력이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성취하는 경험을 통해서 자신감을 얻을 수 있고 나를 바라보는 시선이 달라진다.&lt;br /&gt;어떻게 하는 게 성공인지 물어보자. (의사결정권자랑 얘기를 많이 하자.)&lt;br /&gt;모든 일을 열심히 할 필요는 없다. (잘할 수 있는 것을 열심히 하자.)&lt;br /&gt;중요한 건 임팩트가 큰일을 맡게 되고 하게되는냐다. (중요하지 않은 일은 대충 하자.)&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;br /&gt;이렇게 나이를 비교하는 이유는 결국 남과 비교해서 그렇고 나에 대한 고정관념 때문에 그렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 과거의 상처로 인해 성장을 저해하는 경우가 있다. 사는 과정에서 상처가 있다거나 과거의 프로젝트가 잘 안 됐거나 그로 인해서 무시를 당했다. 이러한 문제를 인지를 하고 있거나 없거나가 중요하다. 자기 자신을 객관화하는 게 인생을 행복하게 한다.&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;br /&gt;회고를 통해 실수를 인정하는 여유가 필요하다. 특히 리더로서 중요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  시작과 꾸준함의 중요성 - 복리가 있는 일 하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Practice Makes Perfect. (연습이 완벽을 만든다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꾸준히 매일 하다 보면 발전하는 게 보이는데 조금 더 오랜 시간을 두고 보이게 된다.&lt;br /&gt;이때 주변에서 나를 서포트해주는 사람이 있느냐가 중요하다.&lt;br /&gt;처음에 실패가 있더라도 계속 시도해 보기&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;&amp;nbsp;책 읽기 및 글쓰기 / 회고 1주일에 한 번&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  네트워크의 중요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람들을 만날 때 호기심 갖기이다. 이때 중요한 건 첫인상에 사로잡히지 말자.&lt;br /&gt;좋은 평판을 유지하자.&lt;br /&gt;좋은 사람과 함께 일하자. 내 매니저가 누구고 동료들이 누구인지 면접이나 커피챗 때 확인해볼 필요가 있다.&lt;br /&gt;좋은 사람들과 함께 일하다 보면 좋은 사람들이 하는 긍정적인 행동들을 보고 배우게 된다.&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;br /&gt;사실 약점은 쉽게 못 고친다.&lt;br /&gt;커리어가 이어지면 강점이 약점이 되는 경우가 있는데 이것을 알고 있느냐가 중요하다. (자기 객관화)&lt;br /&gt;예를 들어, 완벽주의를 가진 사람은 주니어에서 시니어까지는 빠른 속도로 올라간다. 하지만 시니어에서 그 이상은 올라가기 힘들어진다. 이유는 우선순위를 못 따져서 항상 모든 일을 열심히 하려고 하는 문제가 발생한다. 그렇기 때문에 선택적인 완벽이 필요하다.&lt;br /&gt;그리고 실행을 잘하는 사람은 일을 실행하는 것보다 일의 방향이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 장점이 아직도 장점인가?라는 물음표를 항상 생각하자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  사기 증후군(Imposter Syndrome) 극복하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동료들이 나보다 잘나보인다면 또는 주변 친구들이 모두 나보다 잘나 보인다라고 생각하면 자신감이 떨어지게 된다.&lt;br /&gt;자신감을 갖고 남과 비교하지 않고 어제의 나와 오늘의 나를 비교하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 레벨이 올라갈수록 중요하진다. 굳이 내가 이 사람보다 뛰어날 필요가 없다. 더 중요한 건 다 같이 한 방향으로 가는 게 중요하다.&lt;br /&gt;팀원들이 성공할 수 있게끔 서포트해 준다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  개발자 성장&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  기본기 확실히 하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자기가 가장 잘하는 프로그래밍 언어가 있어야 한다.&lt;br /&gt;테스트 작성하기&lt;br /&gt;본인이 사용하고 있는 기본 툴들의 자유롭게 사용이 가능해야 한다.&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;br /&gt;정말로 내가 어떤 문제가 막혀서 필요할 때 빠르게 습득할 수 있느냐가 중요하다.&lt;br /&gt;내가 지금 하는 일에 필요한 학습을 해야 한다.&lt;br /&gt;돈과 시간을 교환해서 학습하자.&lt;br /&gt;필요한 만큼만 알고 문제를 해결하자.&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;br /&gt;혼자가 아니라 다수를 끌고 가는 게 더 중요하다.&lt;br /&gt;나는 결과를 내는 사람이 되겠다. 선한 영향력을 끼치자.&lt;br /&gt;무작적 코딩만 하는 게 커리어를 길게 가져갈 거라는 생각은 하지 말자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;에스티메이션(Estimation)을 잘하는 훈련을 하자. 한 가지 답만 주지 말고 여러 상황을 고려해서 일정을 산정하는 연습이 필요하다.&lt;br /&gt;혼자서 잘하는 건 길게 가지 못한다. 능력 있는 매니저 수가 부족해서 기회가 있다면 도전해 보자. 하기 전까지는 절대 모른다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;  소통 잘하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;발표 훈련해 보자. 회사에서 기회가 있다면 지원해서 해보자.&lt;br /&gt;영어보단 코딩을 얼마나 잘하냐 즉 결과를 만들면 결국 인정하게 된다. 영어를 잘하고 못하고는 중요하지 않다. 영향력이 얼마나 있느냐(결과를 내느냐)가 더 중요하다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;  운영을 고려한 코드 작성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;특히 내가 만든 코드를 남이 운영하는 경우가 더 중요하다.&lt;br /&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;똑같은 일이 3번째 반복되면 그때 자동화를 시작한다.&lt;/li&gt;
&lt;li&gt;부지런한 사람보다 게으른 사람이 더 잘하는 경향이 있다.  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1부 QnA&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  어떻게 하면 더 같이 잘 일할 수 있는가?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같이 일하기 좋은 사람은 사람마다 스타일이 다 다르다.&lt;/li&gt;
&lt;li&gt;의사소통에 있어서는 상대방과 내가 원하는 게 뭔가를 파악해 보자.&lt;/li&gt;
&lt;li&gt;같이 일하는 사람들과 자주 소통하자. 같이 원하는 것을 명확하고 방향성을 잘 잡자.&lt;/li&gt;
&lt;li&gt;리더나 매니저로서는 좋은 일이 생기면 팀한테 공을 넘기고 사고가 생기면 본인이 책임을 져야 한다. 그러면 좋은 매니저라고 인정받게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  변화와 성장에 열정적이지 않은 팀원을 성장하도록 유도하고 도와줘야 할까요? 아니면 열정적인 (회사 문화에 핏한) 직원을 뽑을 때까지 계속 채용을 시도해야 할까요?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사실 동기부여가 안된 케이스인 경우에는 답이 없다. 아무리 노력해도 반응이 없기 때문이다.&lt;/li&gt;
&lt;li&gt;피드백이 안 먹혔을 때 이 사람의 존재로 인해서 팀 분위기가 너무 안 좋아지고 누군가 퇴사가 발생하면 미국은 해고하는 문화가 있다. (해고가 나쁜 게 아니구나라는 것을 느꼈다. 이 사람도 나가서 발전을 할 수 있는 계기가 될 수 있다.)&lt;/li&gt;
&lt;li&gt;코딩을 못하는 주니어 개발자가 있었다. 장점은 분명했는데 개발은 못했다. 같이 일하면 즐거웠다. 굉장히 밝고 순수하고 이해가 빨랐고 말을 잘했다. 이러한 장점으로 세일즈 엔지니어를 추천하였고 결국 해고를 했다. 레퍼런스 체크를 좋게 해 줬다.&lt;/li&gt;
&lt;li&gt;최후의 수단은 더 이상 일 못준다. 네가 하는 일을 다른 사람 주겠다. 네가 변해라.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  스타트업 성장통&lt;/h2&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;h4 data-ke-size=&quot;size20&quot;&gt;새로운 방향의 팀 빌딩&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접관들이 다 모여서 오늘 면접에 대한 피드백 및 얘기를 돌아가면서 한다. 만약에 얘기(피드백)가 별로 없으면 면접을 대충 했다는 것을 알게 된다. 그런 부분에 피드백을 준다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;일하는 방식의 변화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조직 구조는 직관적으로 바꾸는 게 좋다.&lt;br /&gt;팀빌딩하는 방법이 바뀌어야 하고 의사결정하는 과정이 바뀌어야 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;유데미에서 풀타임을 결정하게 된 이유&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재밌게 일할 수 있을 것 같다는 확신이 생겼다.&lt;br /&gt;회사가 성장하는 게 보였고 사람들이 다 괜찮았다. 경험 있는 사람들 말을 잘 받아들였다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;유데미 성장통&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성장통이란? 사람은 늘어나고 프로덕트의 기능이나 수가 늘어나면서 생기는 고통이다. 소통 비용이 커지면서 결국 의사 결정 비용이 커졌다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;구체적인 성장통 예&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B2B와 B2C를 어떻게 병행할까?&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;B2C만 해도 학생과 강사 두 가지로 나뉨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B2C 가격정책&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가격 정책을 잘못하거나 혜택이 없다면 망할 수밖에 없음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B2B는 회사마다 다르기 때문에 여러 시도를 많이 해볼 수 있음.&lt;br /&gt;기술부채로 인한 사고들 그 이후 리팩터링하는데 시간도 많이 썼다.&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미팅 방식의 변경&lt;br /&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;Outbound 형태로 필요한 인력을 찍어서 공을 들여야 한다.
&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;h4 data-ke-size=&quot;size20&quot;&gt;회사의 성장과 나의 성장&lt;/h4&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;Growth Mindset(성장 마인드셋): 경험과 노력을 통해 능력과 지능을 향상시킬&amp;nbsp;수 있다는 신념. 소극적으로 일하지 말고 변화하고 능동적으로 변하자.&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;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;항상 질문을 많이 하자. 나쁜 질문은 똑같은 질문을 하는 것이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;질문을 잘하는 것
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내가 뭐를 몰랐는지 질문을 받은 사람이 이해하기 쉽게 하려면 내가 제대로 이해했는지 물어보라.&lt;/li&gt;
&lt;li&gt;내가 이해한 것을 설명하고 제대로 이해했는지 확인해 보기.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  2부 QnA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 살이라도 젊을 때 좋은 사람들이 있는 곳에서 일해보고 경험을 많이 해봐라. 그러면 그곳에서 자신의 장점을 찾게 될 것이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  능력 있는 사람을 뽑는 게 맞을까? 능력은 조금 떨어지지만 회사의 핏이 맞는 사람을 뽑는 게 좋을까?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팀워크가 있고 학습능력이 있는 주니어를 뽑는 게 더 좋다.&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;h4 data-ke-size=&quot;size20&quot;&gt;  주니어 입장에서 시니어를 어떤 부분을 보고 뽑아야 하나?&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주변 지인 한 명에게 부탁을 한다. 궁극적으로 내가 꼬시고 싶은 사람에게 부탁한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  회고&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Image_2022-12-28-17-25-15.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/KhpcF/btrUP0xIYcx/Enx52bcpiT1w9KNJcMKl10/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KhpcF/btrUP0xIYcx/Enx52bcpiT1w9KNJcMKl10/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KhpcF/btrUP0xIYcx/Enx52bcpiT1w9KNJcMKl10/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKhpcF%2FbtrUP0xIYcx%2FEnx52bcpiT1w9KNJcMKl10%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;599&quot; height=&quot;449&quot; data-filename=&quot;KakaoTalk_Image_2022-12-28-17-25-15.jpeg&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EO의 한기용 님의 3부작 동영상을 모두 보고 꼭 오프라인 토크이벤트를 가고싶다는 생각을 했다. 그 내용이 현재 나의 상황과 커리어에 도움이 되지 않는다 하더라도 꼭 가고 싶었다. 다행히도 선착순 200명 기회를 잡을 수 있었다. 전체적으로 시간과 돈이 아깝다는 생각이 전혀 들지 않던 내용이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크의 중요함이나 꾸준함, 좋은 사람들과 같이 일하면서 좋은 행동들을 따라하고 있는 나를 보면서 가치관과 말씀에 공감이 많이 가서 닭살이 돋을 정도였다. 그리고 임포스터 신드롬, 완벽주의적 성격은 현재 내가 겪고 있는 문제들인데 그 부분에 대해서도 잘 얘기를 해주시고 조언도 들을 수 있어서 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Image_2022-12-28-17-39-12.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YDtJS/btrUKYaqf8U/LE8zG8KVK70DOUDjAqN5R0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YDtJS/btrUKYaqf8U/LE8zG8KVK70DOUDjAqN5R0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YDtJS/btrUKYaqf8U/LE8zG8KVK70DOUDjAqN5R0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYDtJS%2FbtrUKYaqf8U%2FLE8zG8KVK70DOUDjAqN5R0%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;642&quot; height=&quot;439&quot; data-filename=&quot;KakaoTalk_Image_2022-12-28-17-39-12.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 2부의 내용은 어려운 부분이 있었다. 어려웠다기보단 창업과 미국에서 일하는데에는 생각은 없어서인지 관심이 있게 느껴지지는 않았다고 해야 할까? 어쨌든 QnA 시간에도 많은 사람들의 질문을 친절하게 모두 답해주시고 끝나는 시간이 21시 30분에도 불구하고 23시가 넘어서까지 모두 답해주시는 모습을 보며 감동과 동기 부여을 얻을 수 있었다. 그리고 오프라인으로 참석하신 분들의 열정도 볼 수 있어서 &lt;span&gt;좋은 경험이였고&lt;span&gt; 긍정적인 영향을 받고 온 한기용 님의 토크이벤트였다.&lt;/span&gt;&lt;/span&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;</description>
      <category>IT이것저것</category>
      <category>멘토링</category>
      <category>커리어</category>
      <category>토크이벤트</category>
      <category>한기용</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/32</guid>
      <comments>https://haranglog.tistory.com/32#entry32comment</comments>
      <pubDate>Wed, 28 Dec 2022 17:46:56 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 주니어 프론트엔드 개발자의 좌충우돌 웹 거래소 런칭기</title>
      <link>https://haranglog.tistory.com/31</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이 글은 작년(2021년) 12월부터 올해(2022년) 4월에&amp;nbsp;&lt;a href=&quot;https://www.kasa.co.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;카사코리아&lt;/a&gt;의 웹 거래소 MVP(Minimum Viable Product) 런칭 과정과 런칭 이후 현재(글 작성일 기준 2022년 9월)까지의 겪었던 회고입니다.&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;MVP 런칭 후 회고를 작성하고 싶었는데 현업과 다른 공부에 우선순위가 밀려 이제야 작성하게 되었다.&lt;br /&gt;제목처럼 나는 2년 차 주니어 프론트엔드 개발자이다. 현재 카사코리아의 프론트엔드 엔지니어로 카사코리아의 전반적인 프론트엔드 개발을 담당하고 있다. 믿기지는 않지만 나는 웹 거래소 개발을 담당하여 초기 세팅부터 런칭까지 과정을 경험을 해봤다. 지금부터 그 경험한 과정과 의사결정 과정 및 어떻게 개발을 진행해왔는지에 대해서 기억을 되새기며 회고를 해보려 한다.&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;br /&gt;카사는 그동안 개인이 투자하기 어려웠던 상업용부동산(빌딩)을 쉽게 접근할 수 있도록 만든 서비스이다. 사용자는 카사 플랫폼을 이용하여 5,000원 단위부터 상업용부동산을 투자하여 건물주처럼 임대수익을 받을 수 있다. 이는 부동산신탁의 수익증권 공유지분(DABS, 디지털 자산유동화증권)를 소유함으로 상업용부동산에 간접투자를 하게 되는 방식으로 이루어지게 된다. DABS를 소유함으로써 DABS 보유 비율에 따라 건물의 임대수익을 배당처럼 받을 수 있고, 주식처럼 자유롭게 거래하여 매매차익을 올리거나 건물 매각 시까지 보유하여 매각수익을 받을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 자세한 내용은 아래 영상을 참고하길 바란다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=G4iL2aoMKSQ&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/chJz1x/hyPJUCIxHJ/Y09CCTJY8FpKW6VyT6wyR1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;600&quot; data-video-height=&quot;338&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/G4iL2aoMKSQ&quot; width=&quot;600&quot; height=&quot;338&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;카사코리아 소개 영상&lt;/figcaption&gt;
&lt;/figure&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;웹 거래소를 런칭하기 전까지는 웹에서는 브랜드 사이트만 존재했고 거래가 불가능했으며 앱으로만 거래가 가능했다. 그렇다면 앱으로만 거래해도 충분한 거 아니야? 라고 생각이 들지도 모른다. 하지만 고액/전문 투자자들은 연령대는 대부분 40대 이상이고, 카사 서비스를 이용하는 대부분의 고객들은 이미 주식과 같은 투자를 경험한 사용자들이다. 그러므로 고액/전문 투자자들은 웹에서 거래하는 형태의 HTS(&lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;Home Trading System&lt;/span&gt;), WTS에 더 익숙하다.&lt;br /&gt;두 번째 문제는 무조건 앱을 설치해야지만 거래가 가능하다. 즉, PC에서는 거래가 불가능하다. 그렇기 때문에 다양한 환경에서의 투자할 수 있는 접근성이 낮은 문제가 있다.&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;우리의 예상과 목표는 접근성 강화로 인한 사용자의 stickiness를 증가시키는 것, 더 많은 고액 투자자 유입으로 인한 평균 예치금/투자 고객 비율을 증가시키는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리의 단기 액션으로는 2022년 3월 중순에 MVP 기능을 오픈하는 것이었다. MVP의 주요 기능은 &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;우리는 현재 어떤 상황일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 MVP를 시작할 당시에 프론트엔드 개발자는 나 포함 3명이었다. 세 명 모두 2 ~ 3년 차 주니어 개발자였다. 웹 거래소 개발뿐만 아니라 우리는 모바일 웹, 백오피스, 브랜드사이트 개발도 같이 진행되고 있었다. 어쨌든 건물은 계속 앱 서비스에서 공모가 진행되었고 그에 맞춰서 모바일에서 공모 및 투자는 지속적해서 이뤄졌기에 대응해주어야 했다. 그렇기에 우리는 각자가 담당해야 할 부분들을 정하는 의사결정이 필요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 각자 상황과 어떤 부분을 맡아야 더 좋은 시너지가 이뤄질지 생각해온 뒤 얘기를 해보았다. 솔직하게 모두가 신규 서비스 개발을 하고 싶었을 것이다. 어찌 된 건지 다들 각자 하고 싶은 것보단 모든 상황을 고려하여 생각했고 의사결정에 있어서 큰 어려움은 없었다.&lt;span&gt; 결과적으로 각자 담당하는 부분은 존재하되 모두 신규 서비스 웹 거래소 개발을 하기로 정했다. 그중에서도 나는 웹 거래소를 메인으로 담당하게 되었다. 다른 한 분은 백오피스 개발, 또 다른 한 분은 나머지 모바일 웹이나 기존에 도메인에 대한 백그라운드 지식이 필요한 부분들을 담당하게 되었다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이때의 나는 카사에 입사한 지 4개월밖에 되지 않은 상태라 &lt;span&gt;생각보다 많은 책임감과 압박감이 동시에 들었지만, 그보다 새로운 서비스를 개발할 수 있다는 사실이 나를 더 설래게하였다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;그렇게 웹 스쿼드는 프론트 1명(나), 디자이너 1명, 백엔드 1명, 입사 예정인 PO 1명으로 이뤄졌고, 백엔드가 1명인 이유는 위에서도 말했다시피 이미 카사는 앱에서 서비스를 운영하고 있다고 했다. 그래서 대부분의 앱의 기능을 웹에 구현하는 것이기 때문에 백엔드의 대부분의 기능은 이미 구현이 되어있었다. 입사 예정인 PO가 담당하는 게 이상하다는 생각이 들 수 있겠지만 이 당시에 PO는 한 분도 없는 상태였기에 어쩔 수가 없었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PO의 합류 후 우리는 정의한 MVP 기능과 아주 간단한 와이어 프레임을 바탕으로 모두가 estimation을 진행했고, 3월 중순까지 MVP 런칭을 목표로 잡을 수 있었다.&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;이 시점에 나는 카사 웹의 zero-base인 프로젝트의 초기 세팅을 구축하였다. 내가 생각한 프로젝트의 초기 세팅은 우선 프로젝트의 &lt;b&gt;동작하는 골격&lt;/b&gt;을 만드는 것이다.&lt;br /&gt;동작하는 골격이란 &lt;b&gt;전 구간을 대상으로 자동 빌드, 배포, 테스트를 할 수 있는 실제 기능을 가장 얇게 구현한 조각&lt;/b&gt;을 말한다. 즉, 우리는 이 동작하는 골격을 구축해놓으면 더 이상 개발 이외의 작업에 신경 쓸 필요가 없는 것이다. 그렇게 되면 온전히 개발에만 집중할 수 있게 된다.&amp;nbsp;&lt;br /&gt;또한, 동작하는 골격이라 하면 아주 간단한 첫 기능에 대한 인수 테스트를 작성한 상태일 것이다. 이렇게 되면 개발 시작과 동시에 빌드, 배포, 테스트까지 자동화되었고 빠른 피드백을 얻을 수 있게 되었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;동작하는 골격의 자세한 내용은 &lt;a href=&quot;https://saseungmin.github.io/reading_books_record_repository/docs/test/growing-object-oriented-software-guided-by-tests/chapter-4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;를 참고하길 바란다. 그리고&amp;nbsp;&lt;a href=&quot;http://www.yes24.com/Product/Goods/9008455&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;테스트 주도 개발로 배우는 객체 지향 설계와 실천&lt;/a&gt; 책을 읽어보시기를 추천해 드린다. (&lt;a href=&quot;https://saseungmin.github.io/reading_books_record_repository/docs/test/growing-object-oriented-software-guided-by-tests/table-of-contents&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;책 요약&lt;/a&gt;)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 추상적이게 생각할 수도 있겠다. 그러면 프론트엔드에서 동작하는 골격이란 무엇일까?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;패키지 메니저 설치 및 필요한 라이브러리 dependecy 설치&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;TypeScript 세팅 및 정적 코드 분석 도구 &lt;/span&gt;Eslint 세팅&lt;/li&gt;
&lt;li&gt;단위 테스트 환경 구축&lt;/li&gt;
&lt;li&gt;E2E(End To End) 테스트 환경 구축&lt;/li&gt;
&lt;li&gt;CI 파이프라인 구축&lt;/li&gt;
&lt;li&gt;CD 파이프라인 구축&lt;/li&gt;
&lt;li&gt;첫번째 기능의 단위 테스트 통과 및 E2E 테스트 통과&lt;/li&gt;
&lt;li&gt;CI 파이프라인 검증 통과&lt;/li&gt;
&lt;li&gt;CD 파이프라인을 통해 개발 환경에 배포 완료&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 이 상태에서 테스트는 완벽하지 않다. 여기서 중요한 점은 &lt;b&gt;테스트가 아니라 우리가 이런 시스템을 구축함으로써 앞으로 우리의 기능에 대한 요구사항은 얼마나 부합하는지 빠르게 평가를 할 수 있으며, 빠르게 피드백을 반영할 수 있다는 점이다.&lt;/b&gt;&lt;br /&gt;이렇게 초반에는 불안정 해보이고 세팅해야 할 것들이 많아 보이지만 자동화를 구축되고 나면 앞으로 이 반복적인 과정이 안정화되어 오로지 개발에만 집중할 수 있게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기술 스택 선정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술 스택 선정 과정은 혼자만 개발하는 것이 아니기에 같이 협업하는 프론트엔드 개발자 두 분과 함께 지속해서 &lt;span&gt;미팅을&lt;span&gt;&amp;nbsp;가졌다&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;우리는 여러 의사결정의 과정을 거쳐 다음 기술들을 사용하기로 하였다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Package Manager (Yarn berry, Yarn 3+)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지 매니저로 yarn, npm, pnpm, yarn berry등이 있는데 이 중에서 우리는 &lt;a href=&quot;https://yarnpkg.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Yarn Berry&lt;/a&gt;를 사용하기로 했다.&lt;br /&gt;Yarn Berry는 기존에 npm, yarn에서 사용하는 비효율적이고 종종 깨지는 의존성의 node_modules 방식을 개선한 방식이다.&lt;br /&gt;Yarn Berry의 PnP(Plug and Play), ZipFS, Zero-Install, TypeScript Plugin 등을 사용하기 위해 선택했고, Yarn Berry 사용만으로 인해 CI dependency Install 시간도 매우 단축되기 때문에 도입하지 않을 이유가 없었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;더 자세한 내용은 &lt;a href=&quot;https://haranglog.tistory.com/28&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;를 참고하길 바란다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;React.js &amp;amp; Next.js&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://reactjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React.js&lt;/a&gt;를 사용한 이유는 프론트엔드에서 가장 큰 생태계를 자랑하며, 우리가 가장 사용하기 익숙하였기에 결정하기에는 어렵지 않았다.&lt;br /&gt;리액트 프레임워크인 &lt;a href=&quot;https://nextjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Next.js&lt;/a&gt;를 사용한 이유는 쉽게 SSR, SSG를 사용할 수 있으며, SEO 최적화, 이미지 최적화 등 리액트만 사용했을 때 비해 더 쉽게 개발할 수 있어 Next.js를 사용하기로 하는 데에도 큰 어려움은 없었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Emotion&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS를 작성하는 방법은 여러 가지가 있다. 그중에서 우리는 CSS-in-JS인 &lt;a href=&quot;https://emotion.sh/docs/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Emotion&lt;/a&gt;을 사용하기로 했다.&lt;br /&gt;CSS-in-JS의 장점은 JavaScript로 CSS 스타일을 작성하도록 설계된 라이브러리라 JavaScript 코드 안에 CSS를 작업할 수 있다는 점이 개발자 경험에 매우 좋았다.&lt;br /&gt;React에서 일반 css, sass 등을 사용하는 것보다 CSS-in-JS를 사용함으로 얻을 수 있는 가장 큰 이점은 리액트 컴포넌트를 Styled 컴포넌트로 개발할 수 있기 때문에 컴포넌트가 어떤 역할을 하는지 명확하게 컴포넌트 명을 지을 수 있다. className 충돌도 나지 않을뿐더러 리액트에서 추구하는 재사용 가능한 컴포넌트도 그대로 만들 수 있으며, 해당 컴포넌트가 하는 역할에 대해서 의미 있는 이름만 짓는다면 가독성 있는 컴포넌트를 만들 수 있는 장점이 있기 때문에 CSS-in-JS를 선택했다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전역 상태 관리 (Recoil &amp;amp; react-query)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 상태 관리는 처음에는 &lt;a href=&quot;https://redux-toolkit.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;redux-toolkit&lt;/a&gt;을 사용하기로 정했었다. 가장 대중적이고, 3명 다 사용해본 경험이 있었기 때문이다. redux-toolkit과 &lt;a href=&quot;https://redux.js.org/usage/writing-logic-thunks&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;redux-thunk&lt;/a&gt;, &lt;a href=&quot;https://redux-saga.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;redux-saga&lt;/a&gt;를 사용하려고 했지만, 거래소 특성상 API 호출도 많아 캐싱 기능이 필수로 필요하다고 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 &lt;a href=&quot;https://tanstack.com/query/v4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;react-query&lt;/a&gt;나 &lt;a href=&quot;https://swr.vercel.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;swr&lt;/a&gt;과 같은 데이터 패칭 라이브러리를 학습했었고 왜 데이터 패칭 라이브러리가 탄생하게 되었는가에 대해서 학습해보다 보니 꼭 필요함을 느꼈다. 캐싱 기능과 보일러 플레이트도 적고 더 간단하게 hook으로 사용할 수 있다는 점 이외에 필요함을 느꼈던 가장 큰 이유는&amp;nbsp;&lt;b&gt;기존에 redux-toolkit만으로 모든 상태인 client state와 server state를 관리하면 &lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;서버 데이터는 항상 최신 상태임을 보장하지 않기 때문에 문제가 발생할 수 있다. &lt;/span&gt;&lt;/b&gt;이 이유는 react-query와 같은 데이터 패칭 라이브러리가 탄생하게 된 이유이기도 하다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;a href=&quot;https://tanstack.com/query/v4/docs/overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;react-query 공식 문서&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;While most traditional state management libraries are great for working with client state, they are not so great at working with async or server state. This is because server state is totally different. (대부분의 기존 상태 관리 라이브러리는 클라이언트 상태 작업에 적합하지만 비동기 또는 서버 상태 작업에는 적합하지 않습니다. 서버 상태가 완전히 다르기 때문입니다.)&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 우리는 데이터 패칭 라이브러리인 react-query를 도입하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이 이유로 redux-toolkit을 사용하지 않을 이유는 없었다. client state를 전역으로 관리할 때 redux-toolkit을 사용할 수 있기 때문이다. 여기서 의견이 살짝 갈렸다. 나는 client state 관리로 &lt;a href=&quot;https://recoiljs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Recoil&lt;/a&gt;을 사용하자고 했었고, 나머지 두 분은 redux-toolkit으로 관리하자고 했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 Recoil을 사용하자고 했던 이유는 다음과 같았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이전에 사용해봤을 때 Recoil은 러닝커브 그렇게 높지 않았다. Client State만 관리할 경우 더더욱 낮다.&lt;/li&gt;
&lt;li&gt;아주 작은 단위인 atom으로 사용되어 해당 컴포넌트에서만 hook으로 간편하게 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;아무리 redux-toolkit을 사용한다고 해도 Client State만 관리하는데 Recoil에 비해 작성해야 할 보일러 플레이트가 많다.&lt;/li&gt;
&lt;li&gt;redux-toolkit은 사용하고 싶지 않은 immer, redux-thunk의 써드파티 라이브러리가 포함(&lt;a href=&quot;https://bundlephobia.com/package/@reduxjs/toolkit@1.8.5&quot;&gt;참고&lt;/a&gt;)되어있지만 recoil은 pure dependency이다. (&lt;a href=&quot;https://bundlephobia.com/package/recoil@0.7.5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두분이 redux-toolkit을 사용하자고 했던 이유는 다음과 같았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Recoil을 사용해본적이 없었다.&lt;/li&gt;
&lt;li&gt;아직 Recoil은 &lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;experimental state이기 때문에 프로덕션 환경에서 사용하기에는 아직 시기상조다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;번들 사이즈는 recoil보다 redux-toolkit이 더 작다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;나는 Recoil을 사용하고자 하는 이유에 대한 부분에서 포기하기에는 너무 많은 장점이라 Recoil 도입을 포기하기보단 이유에 대해서 더 찾아보았다.&lt;br /&gt;첫 번째 이유는 러닝커브가 높지 않았고 이전에 Recoil을 학습하고 경험을 해본 적이 있어서 기술 미팅 시간에 Recoil에 대해서 공유하는 시간을 가졌다. 기술 미팅 후 두 분께서 굉장히 간단해 보인다고 말씀을 하셨다.&lt;br /&gt;두 번째 이유가 가장 큰 이유였는데 아직 &lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;experimental state이라도 이미 프로덕션 환경에서 사용 중인 회사가 있다면 충분히 설득할만할 거라고 생각이 들었다. 그래서 여러 큰 회사의 채용공고를 확인해보았고 그중에서 토스의 기술 스택은 이미 react-query와 recoil을 사용해서 프로덕션 환경에서 사용 중에 있었다. 그리고 카카오의 모 계열사에서도 사용 중이였다.&lt;br /&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: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;그렇게 여러 미팅을 거쳐서 react-query로 server state를 관리하고 client state는 recoil을 관리하여 사용하기로 결정하였다!  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;단위 테스트 (Jest &amp;amp; react-testing-library)&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;우리는 카사 웹 거래소 프로젝트를 시작할 때부터 TDD(Test Driven Development) 방법론으로 테스트를 작성하며 개발하기로 했다. 테스트가 필요한 이유는 명확했다. 카사의 도메인은 핀테크이기 때문에 결국엔 돈과 관련이 있다. 그 말은 최대한 기술 부채를 줄이고 발생할 수 있는 버그를 줄이도록 할 수 있는 가장 확실한 방법이 테스트라고 생각했다. 물론 TDD가 버그를 줄이기 위한 목적으로 사용되는 방법론은 아니다. &lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;TDD에 관한 얘기는 회고 글의 목적과 맞지 않기 때문에 얘기하지 않을 테지만 여태까지 경험하고 공부해왔던 것들을 바탕으로 글을 따로 하나 쓸 예정이다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;프론트엔드에서 TDD는 논란이 있고 얘기가 항상 많지만, 내가 여태까지 공부하고 여러 책을 읽어봤을 때 항상 테스트 얘기는 빠지지 않았었다.&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: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;이처럼 &lt;b&gt;TDD 도입에 실패하는 주요 이유는 기술적 내용을 잘 몰라서보다 우리가 이 TDD에 대한 인식 즉, 사회적 자본과 사회적 기술이 없어서 실패하는 경우가 대부분이다.&lt;/b&gt; 만약에 팀원들이 TDD에 대해서 부정적이었고, 맘에 들지 않아 신뢰가 없는 상태였다면 도입하지 못했을 것이다. 만약 도입한다고 하더라도 &lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;분명 사회적 측면에서 해결하지 못했기 때문에 실패할 것이 분명하다.&lt;/span&gt; 그러므로 기술적인 측면보단 사회적 자본과 사회적 기술부터 해결돼야지만 TDD를 도입할 수 있을 것이다. 다행히도 &lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;팀원들 가치관도 비슷했기에 TDD 도입에 있어서는 어려움은 없었다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;함께자라기 내용 중&lt;br /&gt;&lt;br /&gt;아무리 기술적인 실천법이라고 해도 그 기술은 사회적 맥락 속에서 실천되어야 하며 그 기술의 성공을 위해서는 사회적 자본과 사회적 기술이 함께 필요하다.&lt;br /&gt;하지만 안타깝게도 현실에서는 팀원들이 맘에 안 들고, 그들도 나를 맘에 들어 하지 않는 상황, 즉 사화적 맥락이 나쁜 상황에서 타개책으로 TDD의 기술적 측면에만 매몰되는 경우가 있다. 사실 그런 상황에서는 무엇을 골라도 실패가 보장되어 있다. 어떤 기술적 실천법이라도 그걸 현실에서 적용하기 위해서는 사회적 자본과 기술이 필요하다.&lt;br /&gt;TDD 도입에 실패하는 경우, 무엇이 병목이었나를 살펴보면 사람들이 TDD의 기술적 내용을 잘 몰라서보다도 TDD를 도입하는 데에 필요한 사회적 자본과 사회적 기술이 없어서가 훨씬 더 많았다. 그렇기 때문에 어떤 기술적 지식을 전달한다고 해도 그것을 사회적 맥락 속에서 가르치고 경험하게 해야 한다. 사회적 기술에는 도움받기, 피드백 주고받기, 영향력 미치기, 가르치고 배우기, 위임하기 등이 있다. (&lt;a href=&quot;https://saseungmin.github.io/reading_books_record_repository/docs/agile/growing-up-together/chapter-1#-%EC%82%AC%ED%9A%8C%EC%A0%81-%EC%9E%90%EB%B3%B8%EA%B3%BC-%EA%B8%B0%EC%88%A0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;요약글 참고&lt;/a&gt;)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 테스트를 작성하기 위해서 가장 많이 사용되는 JavaScript 테스팅 프레임워크로 &lt;a href=&quot;https://jestjs.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Jest&lt;/a&gt;, &lt;a href=&quot;https://mochajs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Mocha&lt;/a&gt;, &lt;a href=&quot;https://jasmine.github.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Jasmine&lt;/a&gt;이 있다. 이 중에서 가장 많이 사용되고 이미 사용해본 경험이 있는 페이스북에서 관리하는 Jest를 사용하기로 결정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2674&quot; data-origin-height=&quot;1230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cn9iZ/btrL6P68Q6d/podgO6324jBoGwKO9FEo70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cn9iZ/btrL6P68Q6d/podgO6324jBoGwKO9FEo70/img.png&quot; data-alt=&quot;npm trends Jest vs Mocha vs Jasmine&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cn9iZ/btrL6P68Q6d/podgO6324jBoGwKO9FEo70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCn9iZ%2FbtrL6P68Q6d%2FpodgO6324jBoGwKO9FEo70%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;756&quot; height=&quot;348&quot; data-origin-width=&quot;2674&quot; data-origin-height=&quot;1230&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;npm trends Jest vs Mocha vs Jasmine&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;React 컴포넌트를 테스트하기 위해서 DOM Testing 라이브러리인 &lt;a href=&quot;https://testing-library.com/docs/react-testing-library/intro/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;react-testing-library&lt;/a&gt;를 같이 사용하였다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;E2E 테스트 (Cypress)&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;E2E 테스트 프레임워크를 정하는 과정에 있어서 의견이 갈렸다.&lt;br /&gt;나는 &lt;a href=&quot;https://codecept.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CodeceptJs&lt;/a&gt;를 사용자하고 말했었고, 다른 한 분은 제일 대중적인 E2E 테스트 프레임워크인 &lt;a href=&quot;https://www.cypress.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Cypress&lt;/a&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: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;내가 CodeceptJs를 사용하자고 얘기했던 이유는 BDD 방식으로 누구나 보기 쉬운 E2E 테스트를 각자의 시나리오에 따라 좋은 가독성을 제공해주어 이전에 사용해본 경험이 좋아서 추천하게 되었다. 단점이라고 한다면 문서화가 아쉬웠었다.&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: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&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: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;그렇게 우리는 이 둘의 차이점과 어떤 프레임워크를 사용하는 게 우리 서비스에 좋을지에 대해서 찾아보았고 문서화를 했었다.&lt;/span&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;1732&quot; data-origin-height=&quot;2158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lAlmZ/btrL9jTXcmL/kDk2WM4S9ETjnaj4hUQX91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lAlmZ/btrL9jTXcmL/kDk2WM4S9ETjnaj4hUQX91/img.png&quot; data-alt=&quot;두 프레임워크를 비교한 컨플루언스 문서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lAlmZ/btrL9jTXcmL/kDk2WM4S9ETjnaj4hUQX91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlAlmZ%2FbtrL9jTXcmL%2FkDk2WM4S9ETjnaj4hUQX91%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;685&quot; height=&quot;853&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;2158&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: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;더 자세히 내용은 회사 내부 자산이기 때문에 공개가 불가능하지만 각 프레임워크의 기술 부채를 조금이나마 빠르게 줄이는 릴리즈 주기를 봤을 때 굉장히 많이 차이가 났었다. 그리고 커뮤니티 크기가 압도적으로 차이가 났기에 앞으로 우리가 해결하지 못할 문제가 발생했을 경우 더 수월하게 대응할 수 있을 것 같았던 Cypress를 선택하게 되었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;CI / CD (Github Action / Dron CI, Argocd)&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CI는 Github Action을 사용하여 단위 테스트, E2E 테스트, Lint 검증, UI 테스트(Storybook) 검증을 하도록 파이프라인을 구성했다. (현재는 번들 사이즈 체크도 하고 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CD는 프론트에서만 결정한다고 되는 문제가 아니고 Devops팀과 같이 커뮤니케이션하면서 세팅을 진행했었고, 기존에 카사에서 사용하고 있는 delivery는 drone ci, deploy는 Argocd으로 세팅하였다. (피드백 주신 전 카사 데브옵스 엔지니어 헐콩과 카사 백엔드 리더인 이완 감사드립니다.  &amp;zwj;♂️)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기타 세팅&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://eslint.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ESLint&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript 정적 코드 분석 도구인 ESLint를 사용했고, 가장 대중적인 &lt;a href=&quot;https://github.com/airbnb/javascript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;airbnb &lt;/a&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/airbnb/javascript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스타일 가이드&lt;/a&gt;를 사용했다.&lt;br /&gt;그리고 추가적으로 react, jest, react-testing-library, storybook, cypress, typescript, simple-import-sort, emotion 룰 등을 같이 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Husky &amp;amp; lint-staged&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;우리는 커밋 전에 &lt;a href=&quot;https://typicode.github.io/husky/#/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;husky&lt;/a&gt; pre commit와 &lt;a href=&quot;https://github.com/okonet/lint-staged&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;lint-staged&lt;/a&gt;를 사용해 변경된 파일에 대해서 단위 테스트와 Lint를 검증했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;TypeScript&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript 슈퍼셋인 TypeScript가 빠지면 섭하지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Sentry&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Error Tracking 도구인 &lt;a href=&quot;https://sentry.io/welcome/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Sentry&lt;/a&gt;도 세팅했다.&lt;br /&gt;환경변수에 따라 devlopment, staging 환경에서는 에러는 잡히지 않고 production 환경에서만 Error가 잡히도록 설정했다.&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;h4 data-ke-size=&quot;size20&quot;&gt;프로젝트 구조&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 구조는 자세하게 말하기는 힘들지만, 기존에 redux를 사용했을 때 사용했던 Presentational Container 패턴을 사용하지 않기로 했다. recoil과 react-query는 hook으로 &lt;span style=&quot;background-color: #ffffff;&quot;&gt;이뤄져 있기 때문에&lt;/span&gt; 해당 책임을 가지고 있는 컴포넌트에서 직접 호출하여 불필요한 props 전달을 피하는 게 더 좋을 것 같다는 판단을 했고, 지금까지도 만족하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 비즈니스 로직을 가지고 있는 Container를 대체할 곳은 어디일까? 우리는 Custom hook을 사용하여 react-query를 재사용하여 사용하도록 했고, 공통으로 사용되는 재사용 hook을 만들어서 사용하도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 테스트 파일은 각자 파일에 가장 가까이에 위치하도록 했다. 이유는 TDD를 할 때 가장 밀접하게 연관된 코드이기 때문에 가장 수정이 많이 일어나는 곳이기 때문이다. 또한, storybook 파일도 각 해당 파일과 가장 밀접한 위치에 만들어 관리했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbyBMe/btrMdGIOPdU/WNcfKLpxhFXA0BrqoMR5kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbyBMe/btrMdGIOPdU/WNcfKLpxhFXA0BrqoMR5kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbyBMe/btrMdGIOPdU/WNcfKLpxhFXA0BrqoMR5kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbyBMe%2FbtrMdGIOPdU%2FWNcfKLpxhFXA0BrqoMR5kk%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;280&quot; height=&quot;257&quot; data-origin-width=&quot;436&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;E2E 테스트 파일들은 Cypress 폴더 아래에 위치하도록 세팅했다. 이유가 있다면 기본적으로 E2E 테스트는 한 곳에 같이 위치하는 게 관리하기 좋았고 Jest랑 같이 사용할 때 Lint 설정 및 TypeScript 설정을 따로 해줘야 했고 Cypress 공식 문서상 제안하는 구조였다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드 컨벤션&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 컨벤션은 처음에 어느 정도 정의해놓고 개발을 진행하면서 필요한 부분을 추가하였다.&lt;br /&gt;기본적으로 eslint의 airbnb 컨벤션을 따랐고, 파일 네이밍과 폴더 네이밍, asset 파일 네이밍 등을 정했다.&lt;br /&gt;우리가 코드 컨벤션을 정한 이유는 읽고 관리하기 쉬운 코드를 작성하기 위해서 정했으며, 이렇게 팀 코드 컨벤션을 정해 준수하면 가독성이 좋아지고, 규모가 점차 커질 프로젝트에서 유지보수 비용도 줄이는 데 도움을 줄 수 있을 거라 믿었기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 전반적인 규칙은 리팩터링과 클린코드의 책을 많이 참고하여 코드를 작성하도록 노력했다. 6가지 정도 기준을 잡았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;첫째도 둘째도 셋째도 가독성 좋은(다른 사람이 쉽게 읽고 이해할 수 있는) 코드를 작성하자.&lt;/li&gt;
&lt;li&gt;테스트 코드도 리팩터링 대상이다. 테스트 코드는 실제 작성하는 코드보다 가독성이 더 중요하다.&lt;/li&gt;
&lt;li&gt;주석은 죽을 때까지 최대한 피한다. 의미 없는 설명 주석을 다는 이유는 코드가 지저분하다는 것이다. 코드가 하는 얘기가 너무 많은지부터 파악하자. (예외 사항은 fix, todo)&lt;/li&gt;
&lt;li&gt;함수나 컴포넌트의 하고자하는 일(책임)은 하나이도록 하자. (단일 책임 원칙)&lt;/li&gt;
&lt;li&gt;함수명이나 메소드명, 변수명, 컴포넌트명은 의미 있는 이름으로 짓자. 만약 이름이 떠오르지 않거나 짓기 어렵다고 느껴진다면 해당 함수는 책임이 너무 많다는 뜻일 가능성이 높다. 따라서 책임을 줄이고 리팩터링부터하자.&lt;/li&gt;
&lt;li&gt;DRY(Don't Repeat Yourself)원칙을 지켜 중복된 코드를 없애자.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;클린코드 내용 중 깨끗한 코드란?&lt;br /&gt;&lt;br /&gt;깨끗한 코드는 작성자가 아닌 사람도 읽기 쉽고 고치기 쉽다. 단위 테스트 케이스와 인수 테스트 케이스가 존재한다. 깨끗한 코드에는 의미 있는 이름이 붙는다. 특정 목적을 달성하는 방법은 (여러 가지가 아니라) 하나만 제공한다. 의존성은 최소이며 각 의존성은 명확히 정의한다. API는 명확하며 최소로 줄였다. 언어에 따라 필요한 모든 정보를 코드만으로 명확히 표현할 수 없기에 코드는 문학적으로 표현해야 마땅한다.테스트 케이스가 없는 코드는 깨끗한 코드가 아니다. 아무리 코드가 우아해도, 아무리 가독성이 높아도, 테스트 케이스가 없으면 깨끗하지 않다. (&lt;a href=&quot;https://saseungmin.github.io/reading_books_record_repository/docs/clean/clean-code/chapter-1#-%EA%B9%A8%EB%81%97%ED%95%9C-%EC%BD%94%EB%93%9C%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;클린코드 깨끗한 코드란? 정리&lt;/a&gt;)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;드디어 시작된 설레임&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제나 무언가 만들어 나가는 거는 너무나 설렌다. 특히 개발이 더욱더 그런 것 같다. 언제나 쉬운 길은 없어서 어려운 문제를 해결했을 때 성취감은 국가가 허락한 마약 같은 느낌...? 그렇게 앞으로 할 일들이 많은 개발을 드디어 시작할 수 있다!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 동작하는 골격을 만들고 온전히 개발에만 집중할 수 있게 될 줄 알았다...&lt;br /&gt;그럼요... 그렇게 개발에만 집중할 수 있을 줄 알았죠...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;우리에게 찾아온 위기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시나 이렇게 수월하게 가면 이상하지...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 위기&lt;/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;프론트엔드 개발자가 3명이라고 말했었다. 카사는 싱가포르와 코리아가 있는데 우리는 코리아에 소속되어있었다. 그 중 한 분이 카사 싱가포르 쪽으로 가시게 되었다. 그렇게 둘이서 코리아의 프론트엔드의 리소스 분배를 다시하게 되었고, 결국에는 코리아에 계신 한 분께서도 더욱더 웹 거래소 쪽 개발에 신경 쓸 수 없게 되어서 개발 일정에도 차질이 생길 수밖에 없는 상황이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 위기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자이너분의 갑작스러운 퇴사를 결정하게 되었다. 개인적으로 친했던 디자이너분이었고 갑작스러운 소식에 놀랐다. 그래도 디자이너분께서 웹 거래소의 MVP 디자인을 전부 끝내시고 퇴사하시기로 하셨고, 퇴사하실 때까지 고생하시면서 MVP 기능에 해당하는 디자인은 다 끝내시고 가셨다. 물론 개발이 진행하면서 디자이너와 지속적인 커뮤니케이션을 많이 했는데 그 부분을 PO와 할 수밖에 없었고, 디자인 수정이 필요해도 수정을 할 수 없고 피그마상의 디자인을 감안하면서 개발을 할 수밖에 없었다.&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;h4 data-ke-size=&quot;size20&quot;&gt;일정? 코드 품질?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일정도 매우 중요했지만, 코드 품질도 포기할 수 없었다. 지금 시간이 부족해서 테스트 코드를 작성하지 않거나, 책임이 많고 결합도가 높은 함수 및 컴포넌트를 구현한다든지, 가독성 좋지 않은 코드를 작성하는 등 이런 사소하지만 매우 중요한 것들을 놓치게 된다면 나비효과처럼 나중에는 이런 &lt;span&gt;기술 부채는&amp;nbsp;&lt;/span&gt;걷잡을 수 없이 늘어날 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 되면 어떻게 될까? 우리가 지금 당장은 빠르게 개발이 진행되어가는 게 눈에 보인다고 하자. 하지만, 우리는 이 프로젝트를 MVP 기능만 구현하고 끝일까? 아니다. 앞으로 지속적해서 유지보수가 이뤄져야만 하고 신규 피쳐도 계속해서 개발되어야만 한다. 그러므로 우리는 변경에 유연하고 리팩터링하기 쉬운 코드를 작성해야만 한다. &lt;b&gt;나는 그걸 &lt;b&gt;가장 간단하면서 효과적으로&amp;nbsp;&lt;/b&gt;도와주는 방법이 TDD라고 생각했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 이전에 CPO 분이 입사하셨을 때 두세 번의 미팅을 가진 적이 있었다. 그 당시 CPO 분이 애자일 프로세스에 대해서 여럿 얘기를 해주셨다. 그리고 앞으로 일을 재밌게 할 수 있는 속도가 있는 애자일 프로세스에서 웹이 카사에서 많은 중요한 역할을 할 것이라고 말씀하셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 말씀드릴 생각은 없었지만, 애자일에 관한 얘기가 많이 나와서 나는 CPO 님이 원하시는 애자일을 하기 위해서는 기술적으로도 그런 환경이 되어야 한다고 말씀드렸고, &lt;a href=&quot;http://www.extremeprogramming.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;XP&lt;/a&gt;(&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;Extreme programming&lt;/span&gt;) 에 대해서 얘기를 했다. 그중에서 론 제프리즈의 XP의 실천 방법 중 &quot;&lt;a href=&quot;https://saseungmin.github.io/reading_books_record_repository/docs/agile/clean-agile/chapter-5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;기술 실천 방법&lt;/a&gt;&quot; 대해서 말씀을 드렸다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;xp.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;671&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuLYSf/btrMVbOsIfP/kimoSchBLZLwDvdvYKMQxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuLYSf/btrMVbOsIfP/kimoSchBLZLwDvdvYKMQxk/img.png&quot; data-alt=&quot;론 제프리즈 XP의 실천 방법 (삶의 순환)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuLYSf/btrMVbOsIfP/kimoSchBLZLwDvdvYKMQxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuLYSf%2FbtrMVbOsIfP%2FkimoSchBLZLwDvdvYKMQxk%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;721&quot; height=&quot;472&quot; data-filename=&quot;xp.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;671&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;론 제프리즈 XP의 실천 방법 (삶의 순환)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 CPO 님은 TDD, 짝 프로그래밍 등 잘 아시지는 못했지만, 해보라고 긍정적으로 얘기를 해주셨다.&lt;br /&gt;&lt;b&gt;만약에 부정적으로 얘기하시며 하지 말라고 하셨어도 죄송하지만 나는 안 하는 척하며 했을 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;클린애자일 내용 중&lt;br /&gt;&lt;br /&gt;절대, 절대, 절대, 짝 프로그래밍을 해도 되는지 허락받지말라. 아니면 테스트를 해도 되는지, 아니면 리팩터링을 해도 되는지 등등... 당신은 전문가다. 당신이 결정하라.&lt;br /&gt;&lt;br /&gt;개발자가 테스트를 쓰는 데 허락을 받으려 해서는 안 된다. 단위 테스트나 리팩터링 작업 일정을 따로 잡아서도 안 된다. 이런 기술적인 작업은 기능 개발 과정에서 이미 이루어졌어야 한다. (선택이 아니라 필수)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;생각보다 속도가 안나는걸...?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발이 시작되고 프로젝트가 시작된 지 중간인 2월 말쯤이었을까? 할 테스크는 산더미인데 생각보다 속도가 나지 않아 보였다. 작은 단위로 일감을 쪼개 스토리 포인트를 산정하여 하나씩 하나씩 퀘스트 깨듯이 개발했고, 나는 할 수 있는 최선을 다해서 열심히 했다고 생각하지만, 어떤 이유 때문인지 생각보다 눈에 보이는 결과물이 잘 나타나지 않았다. 내가 이 정도밖에 안 되는 걸까...? 내 실력이 형편없기 때문에 속도가 나지 않는 걸까? 라는 스스로한테 의심하기 시작했다. 주니어 개발자가 이런 중요도 높은 프로젝트를 담당해서 개발하는 것부터가 말이 안 됐던 걸까? 이런저런 생각이 들었고 그에 따라서 자존감도 낮아져 가는 기분도 같이 느꼈다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 내가 지금 따르고 있는 가치관과 개발 원칙에 대해서도 의심이 들기도 했다. 테스트 코드 때문인가? 테스트를 작성함으로써 속도가 나지 않는 건가? 만약에 테스트코드를 작성하지 않았더라면 더 빨리 개발할 수 있었을까? 이런 생각이 드는 건 어쩌면 당연할지도 모른다. 책에서만 존재하는 이상적인 애자일, TDD 등을 실제로 경험해본 적이 없었기에 스스로에게도 확신이 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 지금 와서 포기하고 싶지 않았고 할 생각도 전혀 없었다. 분명히 단일책임원칙을 잘 지켜져 있었으며, 코드가 읽기 쉽게 잘 짜여있고 구조도 전체적으로 잘 갖춰졌다고 생각이 들었기 때문이다. 그리고 테스트 코드가 있어서 리팩터링 시에도 안심하고 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;다시 진행하게된&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;Estimation(에스티메이션)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;우리는 &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;매일 아침 데일리 스크럼을 진행했고 &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;매주 체크 미팅과&amp;nbsp;&lt;/span&gt;&lt;/span&gt;2주 단위의 스프린트를 돌며 회고를 진행해오면서 3월 중순인 MVP 런칭은 불가능하다고 판단했고, 다시 에스티메이션을 진행하기로 했다. 일정이 지연된 가장 큰 이유는 리소스 부족이 가장 크지 않았나 싶다. 어떻게 보면 당연했고, 상황이 좋지 않았기에 PO나 경영진 및 이해관계자들도 다 이해를 해주셨다. (물론 내가 알지 못하는 게 있을 수도...?)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;그렇게 다시 에스티메이션을 진행했고, 그래도 우리는 빠르게 MVP 런칭을 하는 게 제일 우선이었다. 그래서 처음에 우리가 생각한 MVP의 주요 기능을 생각했을 때 웹에서 기존 사용자가 로그인하여 투자를 할 수 있고, 건물들에 대한 정보를 볼 수 있도록 해주는 것이었다. 투자를 할 수 있게 끔에 중점을 두었고, MVP에 굳이 필요 없는 부분은 스팩 아웃을 시키며 최대한 빨리 런칭을 하기 위해 집중하였다. 그렇게 에스티메이션을 통해 다시 산정한 스토리 포인트 기준으로 4주 뒤인 4월 초중순이었다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&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: #202124;&quot;&gt;사실 &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;솔직히&lt;span&gt; 여태까지 진행해온 개발 진행 상황을 보면 &lt;/span&gt;&lt;/span&gt;일정을 맞출 수 있을지 자신은 없었다. 그래도 열정만큼은 누구한테 지지않았기 때문에 오히려 어려운 과제 같아서 흥미진진했었고 이 퀘스트를 깨고야 말겠다는 생각이 더 강했던 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;역시 틀리지 않았어.&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;프로젝트가 진행되면 진행될수록, 개발이 진행되고 코드량이 많아질수록 TDD의 장점은 더 확연히 보이기 시작했다. TDD로 개발해오며 설계부터 생각하는 과정을 거쳐 테스트를 작성하고 코드를 작성하면서 저절로 테스트하기 쉬운 코드가 작성되었다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&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: #202124;&quot;&gt;그래서인지 우리는 잘 분리된 &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;추상화 되어있는 공통 &lt;/span&gt;컴포넌트와 함수 등 DRY 원칙을 잘 지켜가며 작성하게 되었고, 테스트 코드를 작성하기 어렵다면 리팩터링을 통해 책임을 분리하여 테스트 짜기 쉬운 코드를 작성했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;이러한 무한 TDD 사이클을 통해서 변경에 유연한 코드를 유지할 수 있었고, 신규 피쳐를 개발하거나 요구사항이 중간중간 변경이 될 때도 두려움 없이 코드를 수정하고 리팩터링할 수 있었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;이 과정에서 개발에 속도가 붙는 게 눈에 보이기 시작했다. &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;이건 공통으로 사용하는 로직이 있는걸? 이건 이 컴포넌트를 가져가 쓰기만 하면 되잖아?&lt;/span&gt; 이 로직은 이렇게 단순하게 수정할 수 있잖아? 이렇듯 이미 잘 작성되어있는 테스트 코드와 변경에 유연한 코드를 통해 순식간에 J커브 현상처럼 개발에 속도가 붙는 게 보였다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&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: #202124;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;MVP 런칭&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;이런저런 우여곡절도 많았지만, 나의 첫 도전은 결국 끝이 보이기 시작했다. 일정에 맞춰 개발을 완료할 수 있었고, 4월 7일에 마지막 전체 프로덕트 테스트를 다 같이 진행 후 그 다음 주인 4월 14일에 MVP 런칭을 하게 되었다!&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;&lt;/span&gt;&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;1 ~ 2년 차인 주니어 개발자가 회사에서 중요도 있는 하나의 프로덕트를 담당하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;초기 세팅부터&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;개발하여 런칭한 경험은 어디 가서도&amp;nbsp; 두 번 다시 얻기 힘든 경험이였던 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;아주 큰 하나의 사이클을 끝내고 MVP 런칭까지 진행해오며 어떤 식으로 프로덕트가 만들어져가고 그 과정에서 어떻게 하면 더 좋은 커뮤니케이션을 할 수 있을지에 대해서도 많이 배웠다. 아직도 많이 부족하다고 늘 커뮤니케이션을 향상시키기 위해 고민을 많이 하고 있지만 회사에 뛰어난 동료들과 같이 일하면서 이전보다 커뮤니케이션도 많이 향상된 것 같다.&lt;/li&gt;
&lt;li&gt;내가 여태까지 공부해오고 따르는 가치관이 틀리지 않았다는 것을 느꼈다. 이번 MVP 런칭하면서 여러 우여곡절이 있었지만, 이론적으로만 배웠던 그게 실제로 가능한 거야? 했던 그 좋았던 점들을 얼핏 경험해보며 스스로 가능하다는 것을 느껴봤다는 점이다. 물론, 완전 zero-base에서 개발한 것이 아닌 이미 모바일에서 개발된 서비스를 웹에 MVP를 런칭하는 것이었기 때문에 기획이 어느 정도 완성되어 가능했을지도 모른다. 그래서 다음에 또 이런 도전할 기회가 주어진다면 완전히 새로운 서비스를 만들면서 TDD를 적용해보고 싶어졌다. 다시 또 도전하고 싶어졌다랄까...?&lt;/li&gt;
&lt;li&gt;테스트 커버리지 100%로 MVP 런칭을 했다. 물론 테스트 커버리지를 100%를 달성하기 위해 목숨 걸며 지키는 건 매우 좋지 않은 것을 알고 있다. 불필요한 부분에도 테스트 하게 되고 그만큼 어떻게든 커버리지를 채우기 위해서 시간이 소요될 수 있기 때문이다. 현재까지는 TDD로 개발해오다 보니 100%가 유지가 되었다.&lt;br /&gt;우리는 런칭 당시 Test Suites는 269개, 테스트 개수는 총 941개로 커버리지 100%를 유지할 수 있었다.&lt;/li&gt;
&lt;li&gt;마지막으로 내가 제일 좋아하는 해냈다는 뿌듯함과 성취감. 개발이 더 재밌어졌다.  &lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MVP 런칭 이후&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런칭만 하고 끝이면 이렇게 열심히 하지도 않았을 것이다. 런칭은 끝이 아니라 이제 시작이다. 이제 고작 MVP 런칭이였고 앞으로 할 테스크는 쌓이고 싸여있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런칭 전에는 보이지 않았던 버그들이 런칭 후 어마어마하게 보이기 시작했고, 한동안 그 버그를 해결해나갔다. 그러는 동시에 바로 개선 작업과 앞으로의 우선순위 및 리소스를 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Production 환경에서 Sentry에 잡힌 에러 이슈들을 해결했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-29 오전 1.13.12.png&quot; data-origin-width=&quot;2630&quot; data-origin-height=&quot;2100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TxBsN/btrNiNzeOZa/HK10PW56aEv9PobOzlUEuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TxBsN/btrNiNzeOZa/HK10PW56aEv9PobOzlUEuk/img.png&quot; data-alt=&quot;Sentry Error Jira Bug Issues&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TxBsN/btrNiNzeOZa/HK10PW56aEv9PobOzlUEuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTxBsN%2FbtrNiNzeOZa%2FHK10PW56aEv9PobOzlUEuk%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;658&quot; height=&quot;525&quot; data-filename=&quot;스크린샷 2022-09-29 오전 1.13.12.png&quot; data-origin-width=&quot;2630&quot; data-origin-height=&quot;2100&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sentry Error Jira Bug Issues&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간마다 안정화 기간도 가지며 신규 피쳐 개발에 집중해서 밀린 코드 퀄리티 개선 및 리팩터링 작업도 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런칭 후 내가 기술적으로 개선했던 작업이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기술적으로 무엇을 개선했을까?&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;React v18 마이그레이션 및 Next.js SWC 적용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런칭쯤에 React v18이 드디어 릴리즈 되었다는 소식을 접했다. 이전부터 react 18버전이 릴리즈될 것이라는 소식을 들었고 &lt;a href=&quot;https://reactjs.org/blog/2022/03/29/react-v18.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;어떤 변경점&lt;/a&gt;이 있는지도 학습을 해왔던 상태였다. 그중에서 기존에 서버에서 렌더링할 때는 지원하지 않아 제한적으로 사용되었던 Suspense와 Concurrent mode(Concurrent rendering)를 사용할 수 있다는 변경점이 가장 흥미로웠고 릴리즈 되면 바로 적용하고 싶어졌다. 물론 React 18을 마이그레이션하는 과정에서 릴리즈된 지 얼마 안 된 터라 몇몇 라이브러리가 제대로 지원하지 않아 테스트 및 빌드가 깨지는 이슈가 있었다. 하지만 대부분 사용하고 있는 라이브러리 dependency를 업데이트 및 릴리즈되면서 deprecate 된 부분들을 수정함으로 대부분의 이슈를 해결할 수 있었다. 회사 내부 코드 및 트러블 슈팅한 내용은 공개하지 못하지만 사이드 프로젝트에서도 거의 동일한 이슈를 해결하였는데 궁금하다면 &lt;a href=&quot;https://github.com/jenny-harang/soople/issues/348&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;기존에 Next.js 12임에도 불구하고 babel를 사용하여 &lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;transpile을 하고 있었는데 &lt;a href=&quot;https://swc.rs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SWC&lt;/a&gt; 컴파일러로 변경하는 작업을 했다. SWC는 Rust기반 컴파일러로 5배 빠른 빌드 속도, 3배 빠른 리플레쉬 등 속도 개선에서 너무 많은 이점이 있었기 때문에 무조건 변경해야만 했다. 그래서 기존에 babel을 사용하였던 Jest 환경 및 Next.js를 SWC로 변경하였다. 간단하게 변경함으로써 얻는 이점은 굉장했다. Jest 테스트 속도도 개선과 빌드 시 약 40초 정도 개선되었다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;참고&lt;br /&gt;- &lt;a href=&quot;https://nextjs.org/blog/next-12#faster-builds-and-fast-refresh-with-rust-compiler&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Rust 컴파일러로 더 빠른 빌드 및 빠른 새로 고침&lt;/a&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CI 개선&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 Yarn berry의 PnP 및 zero install 기능을 사용하고 있어서 CI에서 dependency Install 시간은 그렇게 길지는 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, CI 단계에서 lint 검증, unit &amp;amp; e2e test 검증, UI test(storybook)를 검증하고 있었는데 코드량이 많아질수록 CI 단계에서 검증하는 시간이 늘어났다. 그래서 CI에서 Install 및 Build시 캐시를 적용하였고 병렬적으로 각각 검증을 진행하도록 변경하였다. 이렇게 진행한 결과 기존에 Install 시간은 약 40초에서 약 17초로 두 배정도 감소하였고, 각 검증을 병렬적으로 처리하면 그에 배로 감소시키는 효과를 얻을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 검증단계를 하나 추가했는데 CI 단계에서 해당 작업에서 번들 사이즈에 얼마만큼의 영향을 주는지 검증하기 위해서 번들 사이즈 검증을 CI pipeline에 추가하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 CI 단계에서 수정된 코드뿐만 아니라 불필요하게 모든 코드에 대해서 전부 테스트 및 lint check를 하고 있었는데 코드량이 점점 많아질수록 오래 거리게 되었고 main 브랜치와 비교하여 수정된 부분만 테스트 및 Lint를 검증하도록 개선하였다. 이거는 정확하게 비교하기는 힘들지만 간단한 수정이 발생하는 hotfix PR인 경우에는 test는 11초, lint는 7초만에 검증이 완료되게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1972&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blDRzi/btrM6ctnirw/4ZE4mkkYSSZV08CcwxHWE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blDRzi/btrM6ctnirw/4ZE4mkkYSSZV08CcwxHWE0/img.png&quot; data-alt=&quot;CI Pipeline&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blDRzi/btrM6ctnirw/4ZE4mkkYSSZV08CcwxHWE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblDRzi%2FbtrM6ctnirw%2F4ZE4mkkYSSZV08CcwxHWE0%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;1972&quot; height=&quot;558&quot; data-origin-width=&quot;1972&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CI Pipeline&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이렇게 CI를 지속해서 개선하려는 이유는 얻은 피드백을 바탕으로 빠르고 지속해서 반영하여 보여지는 결과를 내고 싶었기 때문이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;commit 컨벤션 적용 및 PR 템플릿 적용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 commit 컨벤션을 정의해놓고 개발하지 않았다. 코드 컨벤션뿐만 아니라 commit에도 컨벤션을 적용하는 게 앞으로 좋을 것 같았기 때문에 &lt;a href=&quot;https://commitlint.js.org/#/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;commit lint&lt;/a&gt;를 사용하여 컨벤션을 적용하였으며 husky를 통해 commit시 검증하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR 템플릿을 적용한 이유는 리뷰어와 리뷰이 모두를 위해서 적용했다. 리뷰이는 본인이 작성한 코드에 대해서 리뷰어의 이해를 돕기 위해서 필요하다는 생각이 들었다. 우아한형제들 기술블로그의 &lt;a href=&quot;https://techblog.woowahan.com/7152/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;코드리뷰&lt;/a&gt; 관련한 좋은 글이 있어서 참고하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-27 오전 2.48.09.png&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;1852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btSXTD/btrM8NZR22Z/udIOs9m1a5Gjlexi9ZX640/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btSXTD/btrM8NZR22Z/udIOs9m1a5Gjlexi9ZX640/img.png&quot; data-alt=&quot;PR Template&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btSXTD/btrM8NZR22Z/udIOs9m1a5Gjlexi9ZX640/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtSXTD%2FbtrM8NZR22Z%2FudIOs9m1a5Gjlexi9ZX640%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;504&quot; height=&quot;552&quot; data-filename=&quot;스크린샷 2022-09-27 오전 2.48.09.png&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;1852&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PR Template&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;Lighthouse로 웹페이지 품질을 측정 후 얻은 피드백 반영&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://web.dev/performance-scoring/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt;를 사용하여 웹사이트 성능을 측정 후 할 수 있는 부분을 수정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Performance 부분은 대부분 이미지와 관련이 있었다. next image를 사용하여 webp로 최적화를 시켜놓았지만, performance가 떨어지게 나오는 이유는 이미지 크기가 보여지는 이미지 크기에 비해 너무 고화질이라 용량이 너무 커 다운로드 받는 속도가 느렸기 때문이다. 이 이슈에 대해서는 백엔드, 디자이너, 프론트, PO까지 같이 미팅을 통해서 이미지 사이즈에 맞춰서 사용할 수 있게끔 최적화를 하는 방법으로 &lt;a href=&quot;https://imagekit.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ImageKit.io&lt;/a&gt;을 사용하기 했다.(적용해주신 백엔드분들 및 홍에게 감사드립니다.  &amp;zwj;♂️)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 Next Image를 걷어내지도 않았고, 이미지 사이즈대로 맞추지도 않았는데도 불구하고 많이 향상된걸 볼 수 있었다.&lt;br /&gt;Accessibility는 기본적으로 웹 접근성에 해당하는 피드백을 반영하였다.&lt;br /&gt;Best Practices 부분도 Next Image와 관련이 있었기에 추후에 이미지 관련 작업을 진행하면 해결될 것이라 생각한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3QFHS/btrM99hhWZi/4hREVZzO6oVxipvoUkiJg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3QFHS/btrM99hhWZi/4hREVZzO6oVxipvoUkiJg0/img.png&quot; data-alt=&quot;개선 전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3QFHS/btrM99hhWZi/4hREVZzO6oVxipvoUkiJg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3QFHS%2FbtrM99hhWZi%2F4hREVZzO6oVxipvoUkiJg0%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;996&quot; height=&quot;310&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개선 전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHAcAs/btrM8kXULsG/FdfOCKnsNnuBkWhsi8gqd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHAcAs/btrM8kXULsG/FdfOCKnsNnuBkWhsi8gqd1/img.png&quot; data-alt=&quot;개선 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHAcAs/btrM8kXULsG/FdfOCKnsNnuBkWhsi8gqd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHAcAs%2FbtrM8kXULsG%2FFdfOCKnsNnuBkWhsi8gqd1%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;1064&quot; height=&quot;314&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;314&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;h2 data-ke-size=&quot;size26&quot;&gt;앞으로 개선하고 싶은 것들은 뭐야?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 개선하고 싶은 것들이 너무 많다. 다음은 개인적으로 개선하고 싶은 것들이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Concurrent mode 적용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재도 적용할 수 있는 부분들을 적용하고 있는데 Suspense로 로딩 상태를 처리하고 Errorboundary를 사용하여 에러를 처리하니 확실히 코드가 선언적이여져서 테스트하기 좋은 코드가 되는 걸 느낄 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직은 많이 적용하지는 못했지만, Concurrent mode를 사용하여 처리할 수 있는 작업을 나누어 우선순위를 부여함으로 우선순위가 높은 부분은 먼저 처리하여 조금 더 나은 사용자 경험을 줄 수 있는 서비스를 만들 수 있을 것 같다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 &lt;a href=&quot;https://www.kasa.co.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;카사&lt;/a&gt; 웹사이트에서 예를 들어보자.&lt;br /&gt;아래 사진은 카사 웹사이트의 사용자가 로그인했을 때 상단에 보이는 화면이다.&lt;br /&gt;빨간색 블록을 확인해보자. 나의 투자 현황과 내가 보유한 배당받을 빌딩, 투자 가능한 빌딩 세 가지 컴포넌트가 렌더링 된다. 우리는 여기서 Concurrent mode를 적용해서 세 가지의 조각으로 나누어서 동시에 작업을 처리하게 할 수 있다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-28 오후 11.58.15.png&quot; data-origin-width=&quot;2248&quot; data-origin-height=&quot;1176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czJdlW/btrNjTTeYsM/CzQnydv4QPxNTahOsV5kDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czJdlW/btrNjTTeYsM/CzQnydv4QPxNTahOsV5kDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czJdlW/btrNjTTeYsM/CzQnydv4QPxNTahOsV5kDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczJdlW%2FbtrNjTTeYsM%2FCzQnydv4QPxNTahOsV5kDK%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;2248&quot; height=&quot;1176&quot; data-filename=&quot;스크린샷 2022-09-28 오후 11.58.15.png&quot; data-origin-width=&quot;2248&quot; data-origin-height=&quot;1176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 &lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#usetransition&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;useTransition&lt;/a&gt;를 사용하여 우선순위에 따른 상태 업데이트를 할 수도 있고 &lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#usedeferredvalue&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;useDeferredValue&lt;/a&gt;도 사용하여 리랜더링도 지연할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;React Query V4 마이그레이션&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 &lt;a href=&quot;https://tanstack.com/query/v4/docs/guides/migrating-to-react-query-4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;react query v4&lt;/a&gt;가 릴리즈되었고 그에 따라서 마이그레이션을 하고 싶어졌다. 어쨌든 메이저 버전이 릴리즈가 되었고 기술 부채를 최대한 줄이기 위해서는 꼭 해야 하는 작업이라고 생각이 들었다. react-query v4가 릴리즈된 주요 이유는 아마도 react의 의존성을 벗어나 다른 vue, 스벨트 등을 지원하기 위해서 그런 것 같았다. 그것 이외에도 메이저 버전업인 만큼 변경점이 많은데 공식 문서를 참고하면서 적용해봐야겠다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공통으로 사용되는 컴포넌트 및 유틸 함수를 모듈화 라이브러리 만들기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 다른 프로젝트에서도 사용할 수 있게 공통으로 구현되어있는 컴포넌트와 유틸 함수를 각각 패키지를 모듈화하여 라이브러리로 만들어 개발 생산성을 최대한으로 향상시키고 싶어졌다.&lt;br /&gt;그리고 스토리북 디자인시스템도 웹 기반으로만 적용되어있지만, 디자이너분들과 같이 디자인 시스템 라이브러리도 만들고 싶다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;E2E 테스트 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 Cypress를 사용하여 E2E 테스트를 작성하고 있는데 많은 부분이 작성되어있지 않다. 기존에 작성하지 못했던 이유는 입사한 지 얼마 되지 않아 도메인에 대한 지식도 부족했고, E2E 테스트는 Github Action으로 사용하지 못하는 VPN 이슈가 있어서 Drone으로 실행되는데 백엔드와 Devops의 Drone CI에서 E2E 테스트가 정상적으로 작동하지 않는 이슈가 있었다. 지금은 해결한 상태라 작성하지 못한 부분들을 차근차근 작성해나갈 예정이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테스트에 MSW 적용하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 테스트에 API 부분이 전부 jest mocking하는 형태로 테스트하고 있다. 기존에 테스트에 중복적으로 API가 jest mocking되어 있는 부분을 &lt;a href=&quot;https://mswjs.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MSW&lt;/a&gt;를 적용하여 &lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;네트워크 요청을 가로채 Service Worker API를 활용하여 동일한 API를 호출하는 것처럼 테스트하도록 변경하려고 한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;기존에 테스트를 위해서 API jest mocking을 하는 방식은 테스트하기 위해 불필요한 사전 작업이 필요하여 개발 생산성을 저하시키는데 MSW를 사용하면 그러한 사전 작업 없이 같은 API를 호출하는 것처럼 테스트할 수 있으므로 가독성 향상 및 생산성 향상에 도움이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이외에도 CI에 &lt;a href=&quot;https://nx.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NX&lt;/a&gt;를 적용하여 CI에서 수행하는 동작들의 결과를 저장하고 이를 캐싱하여 현재의 커밋과 그전의 커밋을 비교하여 변경되지 않으면 캐싱 된 결과물을 사용하도록 적용하는 것과 &lt;a href=&quot;https://tech.kakaopay.com/post/skeleton-ui-idea/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;로딩 스켈레톤 사용자 경험 개선&lt;/a&gt;, 주요 라이브러리 dependency 업데이트, ImageKit을 사용한 사이즈 별로 이미지 최적화 적용 등 적용하고 개선하고 것들이 많다.&lt;br /&gt;배우면 배우고 알면 알수록 배움은 끝이 없는 것 같다는 생각이 자주 드는 요즘이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기나긴 회고를 마무리하며...&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카사에 입사해서 지난 1년여 동안 정말 많은 것들을 해 온 것 같다. 카사 웹 서비스는 초기 세팅부터 맡아서 직접 개발해오다 보니 진짜 애정이 안 생기려야 안 생길 수가 없는 내 자식 같은 느낌이 든다. 내가 만들긴 했지만 회사 소유니까 입양한 자식이 맞을려나...? (퇴사를 하게 되면 자식을 버리는 건가?  )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 다른 서비스도 같이 개발해오고는 있지만 주로 웹 거래소의 서비스를 만들어왔는데 앞으로도 할 신규 개발이 한참 쌓여있다. 입출금, 회원가입, 수익자총회, 공모 등 줄을 서 있다.&lt;br /&gt;기술적인 부분에도 위에서 얘기했듯이 개선 작업들도 넘쳐나고 있다. 그래도 지금까지 잘 해왔다고 생각한다. 현재까지 test suite가 370개, 총 테스트가 1,350개 정도에 달한다. 테스트 커버리지도 현재(2022년 09월 29일 기준)까지도 100%를 유지하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MVlX5/btrNi3WgrJ9/C4rBb2nbVBzldxI7eOXfmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MVlX5/btrNi3WgrJ9/C4rBb2nbVBzldxI7eOXfmk/img.png&quot; data-alt=&quot;codecov test coverage&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MVlX5/btrNi3WgrJ9/C4rBb2nbVBzldxI7eOXfmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMVlX5%2FbtrNi3WgrJ9%2FC4rBb2nbVBzldxI7eOXfmk%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;493&quot; height=&quot;172&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;codecov test coverage&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;br /&gt;그래서 이 글에서 마지막은 같이 웹 거래소를 만든 동료들에게 고마움을 전하고싶다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 언제나 배울 점이 많고 열정 넘치고 나를 카사에 데려오고 지금은 행복 코딩하려  에 간 프론트 쿠쿠, 퇴사하기 전까지 책임감 넘치게 MVP 디자인을 끝내주고 언제나 내 편에서 좋은 말만 해주는 디자이너 케일리, 지금은 퇴사하셨지만 카사에서 가장 능력 있고 제일 많은 것들을 알고 계셨던 백엔드 제이슨, 입사하자마자 도메인 파악하기도 전에 신규 서비스 PO를 맡아 언제 입사했냐는 듯 다 부숴버린 일잘러 PO 지나까지 너무나 좋은 동료들이 있었기에 좋은 서비스를 만들었다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 지금까지 같이 서비스를 만들고 있는 홍, 리아, 칼까지 고마움을 전하고 싶다.  &lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;개인적인 회고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 여태까지 작성했던 회고 글 중에 가장 정성들여 시간을 많이 투자해서 작성했는데 그만큼 얻는 것도 많은 회고였던 것 같다. 이번 회고를 통해 다시 한번 더 회사에서 해왔던 것들을 정리할 기회가 되었던 것 같고, 작년의 나와 비교했을 때 더 나은 내가 된 것 같아서 뿌듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스스로 생각해보았을 때 나는 새로운 것에 대한 습득과 배움에는 빠르지는 않다고 생각한다. 그렇지만 나는 언제나 그렇듯 느리지만 매일 매일 꾸준히 무엇인가를 끄적이고 있다. 나는 어제보다 더 나은 오늘의 내가 되도록 매일 노력하고 있다. 이제는 이렇게 꾸준히 열심히 하다보면 하루 이틀에는 보이지 않는 티끌이 태산이 되는 걸 많이 겪어보다 보니 이제는 꾸준함을 유지하는 게 그렇게 어렵지는 않다. 오히려 그 과정에서 느끼는 성취감이 더 나를 즐겁게 해주는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘은 꾸준함을 유지하는 것이 &lt;span&gt;개발뿐만이 아닌&lt;/span&gt; 건강을 위해서 운동에도 꾸준함을 적용했다. 운동도 6개월 정도 꾸준히 해오다 보니 체력도 확실히 늘어났다는 게 느껴지고 요즘은 재미도 있어서 운동하는 시간도 계속 늘어나고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 모든 일에는 열정과 꾸준함, 끝까지 하고자 하는 끈기만 있으면 안 될게 없는 것 같다. 마지막으로 디자이너 케일리의 추천을 받아 읽은 &lt;a href=&quot;http://www.yes24.com/Product/Goods/32616060&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;그릿&lt;/a&gt;이라는 책을 추천하며 기나긴 회고를 마무리하겠다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;그릿 내용 중&lt;br /&gt;&lt;br /&gt;82p&lt;br /&gt;노력하지 않을 때 당신의 재능은 발휘되지 않는 잠재력일 뿐이다. 재능이 기량으로 발전할 수도 있지만, 노력 없이는 불가능하다. 노력은 재능을 기량으로 발전시켜주는 동시에 기량이 결실로 이어지게 해준다.&lt;br /&gt;&lt;br /&gt;183p&lt;br /&gt;&quot;열심히 하는 거죠. 재미가 없을 때도 해야 할 일은 해야죠. 왜냐하면 결과를 달성하면 엄청 즐거우니까요. &lt;b&gt;마지막에 '아하!' 하는 즐거움&lt;/b&gt;, 그것 때문에 먼 길을 참고 가는 것 입니다.&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;글 내용에 피드백 주신 데브옵스 엔지니어 헐콩, 백엔드 엔지니어 이완, 프로덕트 디자이너 제니 감사드립니다.  &amp;zwj;♂️&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>TDD</category>
      <category>테스트 주도 개발</category>
      <category>프론트엔드 개발자</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/31</guid>
      <comments>https://haranglog.tistory.com/31#entry31comment</comments>
      <pubDate>Thu, 29 Sep 2022 04:55:30 +0900</pubDate>
    </item>
    <item>
      <title>[회고] Github Daily Commit(1일 1커밋) 2년차 그리고..</title>
      <link>https://haranglog.tistory.com/30</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daYBNA/btrCYvobsOu/B4uhk6M3OfPbi9jQonglz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daYBNA/btrCYvobsOu/B4uhk6M3OfPbi9jQonglz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daYBNA/btrCYvobsOu/B4uhk6M3OfPbi9jQonglz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaYBNA%2FbtrCYvobsOu%2FB4uhk6M3OfPbi9jQonglz1%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;454&quot; height=&quot;359&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이전에 &lt;a href=&quot;https://haranglog.tistory.com/26&quot;&gt;daily commit 1년차 회고&lt;/a&gt;를 작성한 적이 있었는데 벌써 2년이 지나서 2년차 회고를 작성합니다.&lt;br&gt;Daily Commit을 한 이유와 해오면서 느꼈던 장단점은 1년차 회고를 참고해주세요. 2년차 회고는 개인적인 회고글입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;벌써 2년이 지났어!  &lt;/h2&gt;
&lt;p&gt;시간이 정말 빠르다…. 정신없이 흘러간 거 같다. 처음 프론트엔드 개발에 흥미를 느껴 공부를 시작하게 된 지 2년이 넘은 것이다. 2020년 4월 26일 이후 지금까지 하루도 빼지 않고 daily Commit을 2년간 넘도록 진행해와 버렸다.&lt;/p&gt;
&lt;p&gt;프론트엔드 개발 공부를 시작하면서 죽어라 해보자 마음을 먹었었고, 그걸 유지하기 위해서 맨 처음 목표로 잡았던 게 이 1일 1커밋이었다. 누구보다 시작이 늦었다고 생각했었고, 그만큼 더 노력해야 한다고 스스로 채찍질을 열심히 해왔던 거 같았다. 하지만, 이 노력이 단순히 목표와 다짐만으로는 지켜지기 힘들다는 건 안다.&lt;/p&gt;
&lt;p&gt;그래도 이렇게 꾸준히 해올 수 있었던 이유는 프론트엔드 개발이 게임? 노는 거? 이런 것보다 훨씬 재밌었고 다른 것에는 없는 성취감이 있었다. 그리고 점점 완성되어가는 결과물을 보는 즐거움이 있었기 때문에 가능했다고 생각한다. 여태까지 인생을 살면서 이렇게까지 열정을 다해 무언가를 열심히 해본 적이 없었다. 이렇듯 어떻게 보면 남들보다 내 길을 찾기 위해서 많이 돌아왔지만, 지금은 정말 내가 좋아하는 것을 직업으로 삼을 수 있게 되어 너무너무 기쁘다.&lt;/p&gt;
&lt;p&gt;좋은 건 좋은 건데 아니, 어떻게 이럴 수가 있을까?&lt;br&gt;벌써 2년이 지났다는 게 이게 맞나? 라는 생각이 든다. 2살이 늙었다니…. 내 인생의 2년이 너무 순식간에 흘러갔다. 2년 동안 개발에 미친것처럼 눈뜨면 개발이고 자기 전까지 개발이었다. 조금은 많이 놀고 싶을 때도 있었지만, 그건 순간일 뿐 결국 개발이었다. 이제는 여유롭게 쉬는 것마저 무료해서 뭐라도 계속하고 있어야 한다.&lt;/p&gt;
&lt;h2&gt;  1년이 지난 후 2년이 될 때까지 어떤 일이 있었어?&lt;/h2&gt;
&lt;p&gt;1년 차 회고 때랑 지금은 많이 달라졌다. 가장 큰 점은 이전에는 SI 회사를 퇴사하고 프론트엔드 개발자로 취업하기 위해 열심히 공부 중이었던 취준생이었다. 그에 반해 지금은 내가 하고 싶은 일을 하는 프론트엔드 엔지니어로 당당히 일하고 있다.&lt;/p&gt;
&lt;p&gt;그리고 또 다른 점은 지난 1년은 이전 1년보다 더 많은 성장을 이룬 것 같다. 물론 개인이 느끼는 상대적인 느낌이지 다른 사람들이 보는 느낌은 아니다. 그래도 꾸준히 무언가를 갈망하며, 노력해오다 보니 하루하루 더 나은 내가 되었다. 또한, 취업 준비를 하고 있었을 때는 자존감 바닥이었던 시절보다 많은 자신감이 생겼다. 지금 회사에 와서도 실력이 뛰어나고 좋은 동료들을 만났고, 그분들을 보며 이전과 또 다른 동기부여를 얻었다.&lt;/p&gt;
&lt;p&gt;그리고 이 과정 동안 회사 업무 이외에도 개인적인 공부는 멈추지 않았고, 지속해서 자기 계발을 해왔다. 기술 서적 읽는 것도 시간상으로는 여유가 많이 없어서 천천히라도 꾸준히 읽어나갔고, 스터디도 지속해서 진행해왔었다. 그리고 사이드 프로젝트도 진행했었고, 지금도 진행 중이다.&lt;/p&gt;
&lt;h2&gt;  1년 동안 어떤 것들을 진행했고 이뤘어?&lt;/h2&gt;
&lt;h3&gt;책읽기&lt;/h3&gt;
&lt;p&gt;1년 전에도 계속 진행해왔던 책 읽기는 여전히 좋은 책들은 찾아서 읽고 있다. (이전엔 내 돈으로 책을 샀다면 지금은 회삿돈!  )&lt;/p&gt;
&lt;p&gt;1년 동안 어떤 책을 읽었는지는 날짜 별로 정리를 해두지 않아서 정확히 기억은 안 나지만, 스터디를 통해 읽거나, 개인적으로 읽고 싶은 책을 읽었다. 1년 동안 한 7~8권 정도? 읽은 것 같다.&lt;br&gt;1달에 한 권 읽기를 목표로 했었지만, 회사 다니면서 읽는다는 게 진짜 쉽지 않다는 것을 깨달았다. 아직도 읽고 싶은 책들이 너무 많은데 급하지 않게 천천히&lt;br&gt;읽어 나가야겠다.&lt;/p&gt;
&lt;p&gt;기술 서적은 정말 정말 계속 꾸준히 읽으려고 한다. 인터넷에 없는 너무 좋은 내용들이 많아서 읽었던 책도 다시 보고 있고, 좋은 내용들 꼭꼭 기억하기 위해 정리하며 되새김질도 하고 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/saseungmin/reading_books_record_repository&quot;&gt;책을 읽고 정리&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Conners&lt;/h3&gt;
&lt;p&gt;이전에 코드숨을 통해서 만들었었던 스터디 모집 사이트인 &lt;a href=&quot;https://github.com/CodeSoom/ConStu&quot;&gt;ConStu&lt;/a&gt;라는 이름의 개인 프로젝트를 진행했었다. 이 프로젝트를 만들게 된 계기가 취업 준비 기간 동안 스터디나 사이드 프로젝트를 개인적으로 찾기가 너무 힘들었었고, 활성화된 사이트도 찾지 못했었다. 그래서 그 당시에 이걸 한번 만들어보자는 생각으로 개인적으로 진행했었다. 하지만 지금 보면 혼자 해보니 역시나 디자인이 너무 자유분방 자기 맘대로였다…. &lt;/p&gt;
&lt;p&gt;그 이후에 회사에 입사 후 친한 디자이너분이 이런저런 서비스를 만들고 싶다는 아이디어는 정말 많았는데 이 아이디어를 디자인만으로는 못하니까 생각만 하고 있다고 얘기를 했었다. 그래서 이전에 했던 이야기를 통해서 &amp;quot;어? 나 이전에 나도 이런 거 해보고 싶었는데 우리 이거 한번 해볼래?&amp;quot;로 시작으로 이렇게 Conners라는 이름으로 시작하게 되었다.&lt;/p&gt;
&lt;p&gt;처음에는 간단할 줄만 알았던 서비스가 생각보다 생각해야 할 것들이 정말 많다는 것을 느꼈고, 하나의 서비스를 만드는 데 필요한 것들이 정말 많다고 다시 한번 느끼게 되었다. 퇴근 후 매주 월요일마다 미팅을 진행하고 새벽까지 이야기하며 개발했었다. 생각보다 매우 힘들었지만 이렇게 계속해서 진행할 수 있었던 이유는 서비스에 대해서 고민하고 커뮤니케이션하는 그 과정이 너무 즐거웠다. (물론 그냥 얘기하는게 즐거워 불필요한 얘기도 참 많았다고 한다..  )&lt;/p&gt;
&lt;p&gt;원래 MVP는 4월 말에 런칭을 하려고 했지만, 지금은 잠시 디자이너분의 개인적인 사정으로 인해 쉬고 있다. 그래도 런칭하자는 생각을 둘 다 가지고 있기에 조만간 다시 시작할 것이다.&lt;/p&gt;
&lt;p&gt;아, 그리고 지금은 Conners라는 이름으로 브랜딩되어 있지만, 추후에는 바꿀 생각이다.  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jennie-harang/Conners&quot;&gt;Conners Github Repository&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://conners.io/&quot;&gt;conners.io&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;그 외&lt;/h3&gt;
&lt;p&gt;생각보다 크게 무엇을 한 건 없었던 것 같다. 하지만, 그 외에도 여러 가지 오픈소스 컨트리뷰트도 했었고 스터디도 진행했으며, 개인적으로 정리하기 위해서 했던 공부들도 꽤 많이 있었다.&lt;/p&gt;
&lt;p&gt;회사 업무로 인해 커밋과 개인적인 공부는 1년 전보다는 못했지만, 꾸준히 해오고 있다는 것으로 만족하고 있다.&lt;/p&gt;
&lt;h2&gt;  앞으로의 Daily Commit의 계획&lt;/h2&gt;
&lt;p&gt;앞으로도 최대한 daily commit을 유지해보려고 한다. 2년 동안 아픈 적이 없거나 무슨 일이 없어서 계속해서 유지할 수 있었다. (설마.. 이걸 멈추면..? 무슨일이..? 생기는게 아닐까?  )&lt;/p&gt;
&lt;p&gt;영원한 건 없듯이 언젠가는 분명히 계속 지속해온 daily commit은 끊일 것이다. 이제는 이렇게 1일 1커밋하는 거에 대해서 뭔가 의무감은 예전보다는 줄었다. 그냥 성격이 뭐라도 끄적여야지 마음이 편해져서 공부는 계속해온 것 같다. 언제까지 할지는 모르겠지만, 그 기간에 그렇게 연연하지 말자. 하루 쉰다고 뭐 달라지나. 그냥 이건 내가 지속해서 성장할 수 있도록 공부하게 유지할 수 있는 단순한 도구였을 뿐이다. 내가 도구를 길들여야지 내가 도구에 길들여지지 말자. 지금 하고 있는 개발도 마찬가지다. 프론트엔드 프레임워크에 길들여지지말자…. 어차피 나중에는 바뀔 것이다. 나중에 바뀌더라도 러닝커브를 줄일 수 있는 기본적인 이론이나 프레임워크에 종속되지 않은 지식을 쌓도록 노력해보자.&lt;/p&gt;
&lt;p&gt;이전보다 회사 업무로 인해 공부량은 줄었지만, 즐길 수 있는 데까지 해보자.&lt;/p&gt;
&lt;p&gt;최근에 회사 업무 외적으로 스터디와 개발자 디자이너 IT 연합 동아리 같은 것도 신청했는데 됐으면 좋겠다. 새로운 건 늘 짜릿해….&lt;/p&gt;
&lt;h2&gt;  마무리하며&lt;/h2&gt;
&lt;p&gt;2년 전 처음 프론트엔드 공부를 시작했을 당시 정했던 목표를 결국 이뤄냈다. 정말 매우 뿌듯하다. 나 자신한테 2년 동안 포기하지 않고 고생했다고 칭찬하고 싶다. 어떻게 보면 운도 좋았지만, 그 목표를 달성하기 위해 노력했기에 지금의 내가 된 것이라고 생각한다.&lt;/p&gt;
&lt;p&gt;이번 회고는 1년 차 회고보단 간단하게 작성하게 되었다. 사실 대부분 얘기는 1년 차 회고 때 많이 얘기했던 거 같아서 정말 그냥 &amp;quot;나 이거 달성했어! 알아봐 줘!&amp;quot;라는 식의 회고를 한 것 같다.&lt;/p&gt;
&lt;p&gt;최근에 회고도 많이 못 했는데, 자주 하려고 노력해야겠다. 그리고 블로그는 거의…. 죽어버렸는데 블로그 좀 써야겠다. 제발…. 쓸 것들은 정말 많은데 블로그 쓰면 정말 반나절이라 막상 마음잡고 쓰기가 힘들다. 그래도 써보도록 노력해보자. 내가 작성한 글이 생각보다 많은 사람에게 동기부여를 주고 도움을 주고 있는 것 같아서 뿌듯하다.&lt;/p&gt;
&lt;p&gt;1일 1커밋은 장단점이 명확하다. 지금 와서 되돌아보면 내 성격상 시작하기 잘한 것 같다. 어쨌든 악용되지 않는 한 지속해서 공부를 시켜주기 때문에 할까 말까 고민하시는 분들은 한 번쯤은 꼭 해보시길 추천해 드린다. 처음에는 짧게 목표를 잡고 한 달…. 두 달…. 점점 천천히 기간을 늘려보면서 지내다 보면 시간은 생각보다 정말 빠르게 흐른다. 운동처럼 꾸준히 해오면 몸에 변화가 생기듯이 개발도 누가 누가 꾸준하게 오래 하나인 것 같다. 티끌 모아 태산이라는 말처럼 어제보다 더 나은 오늘, 오늘보다 더 성숙해진 내일을 바라보면서 하루하루 한층 더 성장하는 나를 보면서 아주 작은 지식을 쌓아가다 보면 어느 순간 쌓여 있는 지식이 태산이 되어있는 것을 몸으로 체감되게 느낄 것이다.&lt;/p&gt;
&lt;p&gt;마지막으로 언제나 지금처럼 개발이 즐거웠으면 좋겠다. 번아웃이 빼꼼하고 오는건가 싶은 느낌도 들지만 안돼! 멈춰! 오지 마!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1578&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnaiwE/btrBvZDIAAE/Q0r2eV3s6iWZUsSKmz2x6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnaiwE/btrBvZDIAAE/Q0r2eV3s6iWZUsSKmz2x6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnaiwE/btrBvZDIAAE/Q0r2eV3s6iWZUsSKmz2x6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnaiwE%2FbtrBvZDIAAE%2FQ0r2eV3s6iWZUsSKmz2x6k%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;1578&quot; height=&quot;476&quot; data-origin-width=&quot;1578&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>회고</category>
      <category>1일 1커밋</category>
      <category>daily commit</category>
      <category>프론트엔드</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/30</guid>
      <comments>https://haranglog.tistory.com/30#entry30comment</comments>
      <pubDate>Wed, 25 May 2022 01:45:58 +0900</pubDate>
    </item>
    <item>
      <title>주니어 프론트엔드 개발자의 2021년 회고</title>
      <link>https://haranglog.tistory.com/29</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XfOpU/btrpwQkMo08/GXqSJMioICoafnz12yrRZ1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XfOpU/btrpwQkMo08/GXqSJMioICoafnz12yrRZ1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XfOpU/btrpwQkMo08/GXqSJMioICoafnz12yrRZ1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/XfOpU/btrpwQkMo08/GXqSJMioICoafnz12yrRZ1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;500&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;작년도 이맘때에 &lt;a href=&quot;https://haranglog.tistory.com/24&quot;&gt;2020년 회고&lt;/a&gt;를 작성했었는데 올해 회고는 작년에 세운 계획이 잘 지켜졌는지와 한 해 어떤 일들이 있었는지에 대해서 회고를 작성하고 2022년도 계획도 크게 세워 볼까 한다.&lt;/p&gt;
&lt;h2&gt;  작년에 세웠던 계획들은 잘 지켰어?&lt;/h2&gt;
&lt;p&gt;일단, 작년에 크게 세워보았던 계획이 얼마나 잘 지켜졌나 봐보자.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Github 1day 1commit 계속 이어나가기&lt;br&gt;작년에 세웠던 계획 중에 이게 제일 어렵다고 생각했었는데 다행히도 daily commit은 하루도 빼먹지 않고 유지 중이다. 처음 프론트엔드 공부를 시작했을 때 최소 2년은 하자고 했던 게 벌써 오늘로써 616일째이다.&lt;br&gt;채워진 잔디를 보면 뿌듯함을 많이 느끼고 있고, 이렇게 꾸준히 해왔기에 개인적으로도 많이 성장했다고 생각한다.&lt;br&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;751&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nxMQi/btrpq6Ww7bE/8UojOmzNcBbwbJV8u8TDlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nxMQi/btrpq6Ww7bE/8UojOmzNcBbwbJV8u8TDlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nxMQi/btrpq6Ww7bE/8UojOmzNcBbwbJV8u8TDlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnxMQi%2Fbtrpq6Ww7bE%2F8UojOmzNcBbwbJV8u8TDlk%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;751&quot; height=&quot;192&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;p&gt;TIL 회고 작성&lt;br&gt;1일 1회고를 목표로 1월 초부터 시작했었고, 지금까지도 매일은 아니지만, 꾸준히 작성 중이다.하지만, 내년도 작성할까에 답은 아직 정하지 못했다. 쓰면 쓸수록 의무감에 작성하는 느낌이 켰기 때문이다. 1일 1회고가 가져다준 장점은 매일 매일 회고를 작성하고 피드백을 얻어 고쳐나가는 역할과 오늘 공부했던 거나 어떤 문제를 해결했을 때를 까먹지 않고 기록하기 위해서 작성했다. 그러나, 충분히 공부하면서 블로그에 기록하거나, 아니면 해당 Github Repository의 Issue란 등에 늘 정리하는 습관이 있어서 회고에 작성할 때 보면 대부분 링크로 대체하곤 했다. 물론 매일 계획을 세우고 그 계획이 잘 지켜졌는지를 확인하는 습관을 들이는 건 좋다고 생각하지만 앞으로 1일 1회고 작성은 좀 더 생각을 해볼 필요가 있어보인다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/saseungmin/TIL&quot;&gt;TIL Github Repository&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;p&gt;퇴사&lt;br&gt;1월 31일에 딱 맞춰서 퇴사했다. 전 회사를 8개월이라는 짧은 시간을 다녔지만, 지금 생각해보면 정말 딱 좋았던 시기에 잘 퇴사했다고 스스로에게 칭찬해주고 싶다. 후회 없는 선택이었다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;취준 및 취업&lt;br&gt;언제나 그랬듯 취업 준비는 힘들고 좋은 회사에 취업하기엔 더더욱 힘들다고 생각한다. 퇴사 후 약 7개월간의 취업 준비 생활을 거쳐서 &lt;a href=&quot;https://www.kasa.co.kr&quot;&gt;카사&lt;/a&gt;라는 회사에 프론트엔드 개발자로 취업했다.&lt;br&gt;딱 지치기 시작할 때쯤에 좋은 회사에 취업하게 된 거 같다. 이제 벌써 5개월 차인데 지금까지도 너무 만족스럽고 즐겁게 다니고 있다. 특히 사람들이 다들 너무 좋다. 그래서 재택을 안 하고 출근을 할 정도…??&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://haranglog.tistory.com/27&quot;&gt;[회고] 프론트엔드 개발자로서의 첫 발걸음을 내딛다!&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S9ceu/btrptgko3GA/9caivGWZm1mJSsG4WkL0b1/img.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;&lt;p&gt;알고리즘 공부&lt;br&gt;알고리즘 공부는 취업 준비할 때는 정말 열심히 했지만, 취업 후 알고리즘 공부는 하나도 안 했다. 지금 당장 우선순위에서 계속 밀리다 보니 어쩔 수 없는 거라고 생각한다. 하지만, 알고리즘은 역시나 꾸준히 하지 않으면 그대로 까먹기에 2022년도에는 1주일에 한 문제씩이라도 풀어야겠다는 생각이 든다.  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/saseungmin/daily_coding_dojo&quot;&gt;알고리즘 문제풀이 Github Repository&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;지치지 말고 꾸준히&lt;br&gt;프론트엔드 공부를 시작했을 때부터 여전히 내 좌우명이다. 다행히 지금까지는 프론트엔드 분야가 너무나 재밌다. 배우면 배울수록 마르지 않는 샘물처럼 정말 배울 게 많아서 매번 새로운 자극을 받는 기분이다.&lt;br&gt;지금도 어떤 것보다 코딩하는 게 제일 재밌고 주어진 문제를 해결해나가며 완성돼가는 서비스를 보는 게 정말 재밌다. 다만 걱정이 되는 건 나도 아직까진 알 수 없는 어떤 계기로 인해 올 번아웃에 조심하면서 나아가야겠다는 생각이 든다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;여행&lt;br&gt;정말…. 여행 가고 싶었는데 결국에 코로나 때문에 아무것도 못 한 2021년도였다. 정말 아픈 2021년도였지만 좀만 더 긍정적으로 생각해보면 그 돈 안 써서 집 보증금을 마련할 수 있었다고 한다…??&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;  올해를 마무리해볼까?&lt;/h2&gt;
&lt;p&gt;2021년도를 그래프로 나타낸다면 &lt;code&gt;V&lt;/code&gt; 모양이 나오지 않을까? 생각이 든다.  &lt;br&gt;프론트엔드 개발자로 취업하기 이전과 이후로 나뉘어서 퇴사 후 취업 준비 기간은 하향곡선이었던 거 같고, 취업 후는 우주를 뚫을 정도까지 나아가는 상승 곡선을 이루어가고 있다고 생각하고 있다.&lt;/p&gt;
&lt;p&gt;그렇기에 올해의 가장 큰 일은 &lt;strong&gt;하고 싶었던 프론트엔드 개발자로서의 취업&lt;/strong&gt;이지 않을까 싶다.&lt;/p&gt;
&lt;p&gt;그리고 건강하게 별 탈 없이 한 해를 마무리했다는 거 자체에 만족스럽다.&lt;/p&gt;
&lt;h4&gt;  2021년 스스로에게 칭찬하고 싶은 점&lt;/h4&gt;
&lt;p&gt;꾸준함을 유지한 것과 프론트엔드 개발자를 선택한 것! 언제나 인생은 선택의 순간이지만, 그 선택을 운이 좋게 잘했다는 생각이 든다.&lt;/p&gt;
&lt;p&gt;커뮤니케이션 능력이 정말 많이 향상된 것을 스스로에게 많이 느낀다. 남들 앞에서 말을 잘하지 못하는 병 같은 게 있었는데, 스터디를 많이 해보면서 기술적인 얘기들도 많이 하고 책도 많이 읽음으로써 자신감이 생기고 그에 따라서 커뮤니케이션 능력이 향상이 따라왔다고 생각한다. 역시나 배움의 부족이었고, 자신감의 부족이었다. 나도 충분히 남들보다 더 잘할 수 있다는 것을 알게 되었다.&lt;/p&gt;
&lt;p&gt;그리고 마지막으로 카사에 입사한 것.  &lt;/p&gt;
&lt;h4&gt;  2021년에 두고 가야 할 것&lt;/h4&gt;
&lt;p&gt;올해 아쉬웠던 점이라면 스스로 성장이라는 틀에서 벗어나지 못하고 여유롭지 못하게 달려왔다는 점이다. 스스로 이렇다는 것을 알고 있음에도 고쳐지지 않았다. 언제나 남들보다 뒤처졌다는 생각을 항상 가지고 있어서 더 여유롭지 못하게 달렸다는 생각이 든다. 이러한 압박감으로 인해 쫓기는듯한 공부는 올해에 두고 가야겠다.&lt;/p&gt;
&lt;p&gt;항상 생각이 너무 많은 거 같다. 조심하는 건 좋지만, 때로는 선택과 집중을 빠르게 해야 할 때도 있다. 너무 남들 눈치 보지 말고 내 뜻대로 가보자.&lt;/p&gt;
&lt;h2&gt;  반가워! 2022&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;안녕! 2022년도야! 코로나가 없어졌다고 말해줘. 제발…??  이제 그만할 때 됐잖아.. 이 자식아.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;작년과 같이 내년의 계획을 크게 한 번 세워보자!&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Github 1 day 1commit 계속 유지하기&lt;br&gt;이제 내년 4월이 되면 2년 동안 유지하게 된 것이다. 처음 목표로 했던 2년은 달성하게 되지만, 그 이후에도 큰일이 있지 않은 한 아마 계속 달려 나갈 거 같다. 물론 중간에 끊기더라도 자책하지 말자. 예전에는 1day 1commit은 꾸준함을 위한 도구였지만, 지금은 아니다. 지금은 그런 꾸준함은 벌써 좋은 습관으로 남아있다. 괜히 1day 1commit을 채우려고 압박감을 가져가며 할 이유는 없다고 생각한다. 아니 확신한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;코딩도 좋지만 건강부터..!&lt;br&gt;2022년은 자기 계발도 좋은데 건강을 먼저 생각해보자. 체력을 키우기 위해 운동을 시작하자.&lt;br&gt;그리고 지금은 취미이자 일이 코딩인데 앞으로 올 번아웃이 오는 걸 막기 위해서 새로운 취미생활을 찾고 더 많은 문화생활을 즐겨야겠다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Conners 서비스 런칭하기&lt;br&gt;2022년도에 Conners 서비스를 런칭하기 만해도 정말 많은 성장과 동기부여가 될 거 같다.&lt;br&gt;작년부터 스터디를 모집해주는 &lt;a href=&quot;https://github.com/CodeSoom/ConStu&quot;&gt;ConStu&lt;/a&gt;라는 사이트를 만들었었는데 여기서 더 나아가서 진짜 제대로 만들고 싶어졌다.&lt;br&gt;그래서 회사 동료 프로덕트 디자이너 제니와 함께 Conners라는 새로운 이름으로 스터디와 프로젝트를 이어주는 서비스를 만들고 있다. 시작한지 이제 1달 정도 되었고 회사 업무 외 시간에 만들기에 시간은 조금 걸릴 수도 있겠지만, 정말 꼭 내년 중에는 런칭하고 서비스를 만들면서 겪었던 기술적인 이슈와 함께 회고를 작성해서 커뮤니티에도 홍보를 할 것이다.&lt;br&gt;같이 Conners를 만들고 있는 언제나 최고인 디자이너 제니에게 정말 고맙다고 전해주고 싶다. 최고!  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jennie-harang/Conners&quot;&gt;Conners Github Repository&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://conners.io/&quot;&gt;https://conners.io/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;p&gt;카사 코리아 웹 거래소 런칭하기&lt;br&gt;주니어 프론트엔드 개발자에게 웹 거래소를 초기 세팅부터 개발할 수 있다는 자체가 엄청난 행운이라고 생각한다. 거기다 웹 거래소 프론트엔드 메인 개발자이다.&lt;br&gt;2022년도에 웹 거래소를 런칭하기로 되어있는데 정말 웹 거래소를 구현하면서 수많은 물음표가 따라올 것이고 부담과 압박감도 심할 거라고 생각한다. 이 기술적인 이슈들을 해결해나가면 엄청나게 성장할 수 있겠다고 확신한다.&lt;br&gt;어려운 거 누구보다 잘 알고 있지만, 그래도 혼자는 아니기에 못할 건 없다는 마인드.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;일본 여행가기&lt;br&gt;유럽이라는 거창한 꿈은 현실적으로 불가능하다. 일본 정도는 노려볼만하잖아? 많은 거 안 바랄게. 솔직히 일본은 정도는 가게 해줘… 금방 갔다올께…&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;보육원 후원&lt;br&gt;내 버킷리스트 중 하나인 &lt;a href=&quot;http://www.swchildren.co.kr/bbs/board.php?bo_table=sw_story&amp;amp;wr_id=310&quot;&gt;보육원 후원&lt;/a&gt;을 2021년 올해 12월에 갔다 왔다. 생각보다 많이 의미 있는 시간이었다. 좀 아쉬웠던 건 코로나로 인해서 아이들을 볼 수 없었다는 점이다. 그래서 2022년도에도 다시 갈 생각이다. 코로나 때문에 지금은 자원봉사는 힘들지만, 기회가 된다면 자원봉사도 하고 싶다. 어찌 됐든 나 혼자만 사는 세상이 아니기에 아이들에게도 긍정적인 에너지를 전달해주고 싶었다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;언제나 즐겁고 긍정적이고 행복하게!&lt;br&gt;지금처럼 좋은 동료들과 재밌고 즐겁고 행복하게 일하고 싶다. 트러블은 너무 싫어. 딱 지금만 같이! 앞으로도 큰 신경 쓸 일 없이, 슬픈 일 없이 좋은 사람들과 오래오래 함께하고 싶다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;마무리하며.. &lt;/h2&gt;
&lt;p&gt;2021년 올해도 이렇게 보낼 때가 왔다! 어찌 보면 코로나로 인해 지우고 싶은 해이긴 했어도 개인적으로는 인생에 있어서 꼽고 싶은 한 해가 아니었나 싶다.&lt;/p&gt;
&lt;p&gt;2021년 마지막 날 급하게 작성하는 회고이지만, 작성하는 동안 올해를 돌아볼 수 있어서 꽤 의미 있던 하루였다. 아마 올해보다 내년이 힘들 수도 있겠지만, 언제나 그랬듯 차근차근 앞으로 나아갈 것이고 이제는 혼자가 아니기에 그 길이 더 수월할 거라 생각한다.&lt;/p&gt;
&lt;p&gt;프론트엔드 개발자로서 좋은 시작을 하게 되어서 너무 만족스러운 한 해였고, 내년은 어찌 보면 주니어 프론트엔드 개발자로서 성장에 큰 터닝포인트가 되지 않을까 기대해보며 2021년 회고를 이만 줄일까 한다.&lt;/p&gt;
&lt;p&gt;글을 읽으시는 모든 분들 새해 복 많이 받으세요!  ‍♂️ 긴 회고 글 읽어주셔서 감사합니다. &lt;/p&gt;</description>
      <category>회고</category>
      <category>2021년 회고</category>
      <category>프론트엔드 개발자</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/29</guid>
      <comments>https://haranglog.tistory.com/29#entry29comment</comments>
      <pubDate>Fri, 31 Dec 2021 22:17:12 +0900</pubDate>
    </item>
    <item>
      <title>Yarn berry에서 React + Jest + Cypress + TypeScript + Github Actions CI/CD를 세팅해보자</title>
      <link>https://haranglog.tistory.com/28</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1NHib/btrhjsGAkU9/TWxoVZQ6Tw4uWEu5sL8diK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1NHib/btrhjsGAkU9/TWxoVZQ6Tw4uWEu5sL8diK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1NHib/btrhjsGAkU9/TWxoVZQ6Tw4uWEu5sL8diK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/d1NHib/btrhjsGAkU9/TWxoVZQ6Tw4uWEu5sL8diK/img.gif&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이 글은 &lt;a href=&quot;https://yarnpkg.com/&quot;&gt;Yarn berry&lt;/a&gt; package manager를 사용하여 &lt;a href=&quot;https://ko.reactjs.org/&quot;&gt;React&lt;/a&gt; 프로젝트에서 초기 세팅하는 방법을 다루고 있으며, 여러 시행착오를 겪어가며 경험한 것들을 토대로 작성한 글입니다.  이 글의 목적은 겪었던 시행착오들을 한 번 더 기억을 되새기기 위해서 작성하였습니다.&lt;br&gt;추가적으로 &lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt;와 프론트엔드 e2e 테스트 프레임워크 &lt;a href=&quot;https://www.cypress.io/&quot;&gt;Cypress&lt;/a&gt;, 프론트엔드 unit 테스트 프레임워크 &lt;a href=&quot;https://jestjs.io/&quot;&gt;Jest&lt;/a&gt; 세팅과 더불어 Yarn berry에서 &lt;a href=&quot;https://github.com/features/actions&quot;&gt;Github Actions&lt;/a&gt;을 사용해 CI/CD 파이프라인을 구축 후 &lt;a href=&quot;https://pages.github.com/&quot;&gt;Github Pages&lt;/a&gt;에 배포하는 것까지 담고 있습니다. 이 내용은 각각의 공식 문서를 기반으로 작성되었습니다.&lt;br&gt;해당 예를 담은 &lt;a href=&quot;https://github.com/saseungmin/yarn-berry-example&quot;&gt;Github Repository&lt;/a&gt;를 참고해주세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  Yarn berry?&lt;/h2&gt;
&lt;p&gt;npm, Yarn v1과 같은 package manager가 이미 존재하는데 Yarn berry는 뭐고 굳이 Yarn berry를 써야하는 이유는 뭘까?&lt;br&gt;우리는 먼저 Yarn berry를 사용하는 이유부터 알아봐야 합니다. 이 답에 대해서는 이미 잘 작성되어있는 아티클과 블로그 글들이 많아 링크만 남기고 생략하겠습니다. 이 글의 목적과 맞지 않다고 판단하기에 설명은 생략합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Yarn 공식 문서 QnA&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://yarnpkg.com/getting-started/qa#why-should-you-upgrade-to-yarn-modern&quot;&gt;Why should you upgrade to Yarn Modern?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;토스 기술블로그&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://toss.tech/article/node-modules-and-yarn-berry&quot;&gt;node_modules로부터 우리를 구원해 줄 Yarn Berry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;원티드 기술블로그&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/wantedjobs/yarn-berry-%EC%A0%81%EC%9A%A9%EA%B8%B0-1-e4347be5987&quot;&gt;yarn berry 적용기(1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/wantedjobs/yarn-berry-%EC%A0%81%EC%9A%A9%EA%B8%B0-2-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%A0%81%EC%9A%A9%EA%B8%B0-45f1ba67c24c&quot;&gt;yarn berry 적용기(2) 프로젝트 적용기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  Install React with Yarn berry&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;이 글 작성 당시 최신 버전의 yarn은 v3.0.2입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yarn berry는 기존에 사용하던 npm, yarn classic과는 다르므로 추가적인 단계가 추가됩니다.&lt;br&gt;일단, Yarn package manager가 설치되어있지 않은 분들은 Yarn을 먼저 &lt;code&gt;global&lt;/code&gt;로 설치 후 설치를 확인하기 위해 yarn 버전을 확인해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install -g yarn
yarn --version&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서는 Webpack, 바벨 설정 등을 제외하기 위해 &lt;a href=&quot;https://create-react-app.dev/&quot;&gt;Create React App&lt;/a&gt;을 사용하여 React를 설치하겠습니다.&lt;/p&gt;
&lt;p&gt;두 가지 방법으로 yarn berry를 사용한 create-react-app을 설치할 수 있습니다. 기본적으로 npm 또는 yarn classic을 사용했을 때와는 다른 방법으로 설치를 해야 합니다.&lt;/p&gt;
&lt;p&gt;저희는 TypeScript를 사용할 것이기 때문에 &lt;code&gt;--template typescript&lt;/code&gt; 옵션을 사용하겠습니다.&lt;/p&gt;
&lt;h3&gt;1. npm 또는 yarn classic으로 create-react-app을 설치 후 마이그레이션하는 방법&lt;/h3&gt;
&lt;p&gt;기존에 Yarn 버전이 &lt;code&gt;v.1.x.x&lt;/code&gt;일 때 또는 npm을 사용해 install할 때 설치 후 마이그레이션 하는 방법입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx create-react-app &amp;lt;설치를 윈하는 폴더&amp;gt; --template typescript
cd &amp;lt;설치를 윈하는 폴더&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 설치했을 경우 &lt;code&gt;node_modules&lt;/code&gt; 의존성 폴더가 존재합니다. 하지만, Yarn berry에서는 &lt;code&gt;node_modules&lt;/code&gt; 의존성 폴더를 사용하지 않고 의존성을 관리합니다. 이에 대한 자세한 내용은 &lt;a href=&quot;https://yarnpkg.com/features/pnp&quot;&gt;공식 문서: yarn berry PnP&lt;/a&gt;를 참고해주세요.&lt;br&gt;그러므로 &lt;code&gt;node_modules&lt;/code&gt; 폴더와 &lt;code&gt;package.lock.json&lt;/code&gt; 파일을 삭제해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rm -rf node_modules
rm -rf package.lock.json&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 우리는 Yarn berry 버전으로 변경해줘야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn set version berry&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;해당 레포지토리에 &lt;code&gt;.yarnrc.yml&lt;/code&gt;과 &lt;code&gt;.yarn/releases&lt;/code&gt; 폴더 아래에 &lt;code&gt;yarn-berry.js&lt;/code&gt; 또는 &lt;code&gt;yarn-3.0.2.cjs&lt;/code&gt; (확장자명은 설정에 따라 다릅니다.) 파일, &lt;code&gt;.pnp.cjs&lt;/code&gt;가 생성됩니다.&lt;br&gt;그 후 Yarn 버전을 확인해보면 3.0.2인 berry로 세팅되어있는 거를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock &quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4xPN3/btrgQAYubOM/D6PD52WnL98XMQfvWD78o1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4xPN3/btrgQAYubOM/D6PD52WnL98XMQfvWD78o1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4xPN3/btrgQAYubOM/D6PD52WnL98XMQfvWD78o1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4xPN3%2FbtrgQAYubOM%2FD6PD52WnL98XMQfvWD78o1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;만약 &lt;code&gt;.yarnrc.yml&lt;/code&gt;파일에 아래와 같이 &lt;code&gt;nodeLinker&lt;/code&gt;가 &lt;code&gt;node-modules&lt;/code&gt;를 가리키고 있다면, Yarn berry의 PnP 방식의 zip 아카이브로 관리되는 것이 아닌 기존의 &lt;code&gt;node_modules&lt;/code&gt; 의존성 폴더 방식으로 관리되게 됩니다. &lt;strong&gt;그러므로 이 속성을 지워주어야 합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yml&quot;&gt;# nodeLinker: node-modules 삭제!

yarnPath: .yarn/releases/yarn-berry.js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그 후 &lt;code&gt;yarn install&lt;/code&gt;을 해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn install&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;자세한 설치 방법은 &lt;a href=&quot;https://yarnpkg.com/getting-started/install&quot;&gt;공식 문서 Install 참고&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2. yarn berry로 create-react-app을 설치하는 방법&lt;/h3&gt;
&lt;p&gt;처음부터 Yarn berry를 사용해 create-react-app을 설치하기 위해서는 Yarn berry가 설치되어 있어야 합니다.&lt;br&gt;그 후 아래와 같이 &lt;a href=&quot;https://yarnpkg.com/cli/dlx&quot;&gt;&lt;code&gt;dlx&lt;/code&gt; 명령어&lt;/a&gt;를 사용해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn dlx create-react-app &amp;lt;설치를 윈하는 폴더&amp;gt; --template typescript
cd &amp;lt;설치를 윈하는 폴더&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;  Setting Zero-Installs&lt;/h3&gt;
&lt;p&gt;Yarn berry에서는 &lt;a href=&quot;https://yarnpkg.com/features/zero-installs&quot;&gt;Zero-Installs&lt;/a&gt;이라는 방법을 사용할 수 있습니다. Zero-Installs을 사용하면 반복적으로 의존성 설치 작업이 이루어지는 CI 단계에서 시간을 단축할 수 있습니다.&lt;/p&gt;
&lt;p&gt;또한, Yarn berry의 &lt;a href=&quot;https://yarnpkg.com/features/pnp&quot;&gt;PnP(Plug &amp;amp; Play)&lt;/a&gt;는 의존성을 압축 파일로 관리하기 때문에 의존성의 용량이 작습니다. 따라서 의존성을 Git으로 관리할 수 있습니다. 즉, &lt;code&gt;node_modules&lt;/code&gt;처럼 &lt;code&gt;.gitignore&lt;/code&gt;파일에 추가시켜 제외할 필요 없이 의존성 관련 파일들을 Github의 Repository에 같이 올려 관리할 수 있게 됩니다. 이렇게 Zero-Installs를 사용해서 저장소를 &lt;code&gt;clone&lt;/code&gt; 했을 때나 브랜치 이동 시에 추가적인 &lt;code&gt;yarn install&lt;/code&gt;이 필요 없게 되는 장점이 있습니다. 이때 Zero-Installs를 사용하고 사용하지 않고는 선택사항입니다.&lt;/p&gt;
&lt;p&gt;두 방법에 따라서 아래와 같이 &lt;code&gt;.gitignore&lt;/code&gt;에 추가되는 설정이 다릅니다. (관련 설정에 대한 정보는 &lt;a href=&quot;https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored&quot;&gt;공식 문서: Which files should be gitignored?&lt;/a&gt;를 참고해주세요.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zero-Installs를 사용할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;Zero-Installs를 사용하지 않을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;여기서는 Zero-Installs를 사용하겠습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;yarn start&lt;/code&gt;를 cmd에 입력하여 정상적으로 시작이 되나 확인합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn start&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;50%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d4WpMD/btrgHD9S4vT/uAkgcZLXyKaPg060jcyTzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d4WpMD/btrgHD9S4vT/uAkgcZLXyKaPg060jcyTzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d4WpMD/btrgHD9S4vT/uAkgcZLXyKaPg060jcyTzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd4WpMD%2FbtrgHD9S4vT%2FuAkgcZLXyKaPg060jcyTzk%2Fimg.png&quot; width=&quot;50%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;  Setting TypeScript, Eslint with Editor SDK&lt;/h2&gt;
&lt;p&gt;Yarn berry의 PnP 기능을 사용할 때, TypeScript가 작동하도록 추가적인 구성이 필요합니다. 안전상의 이유로 VSCode에서는 사용자 지정 TypeScript 설정을 명시적으로 활성화해야 합니다.&lt;/p&gt;
&lt;p&gt;패키지들이 zip 아카이브로 관리되기 때문에 기존의 방식으로는 정상적으로 타입이 불러와지지 않습니다.&lt;/p&gt;
&lt;p&gt;Editor SDK 설정을 하기 전에 먼저 VSCode에서 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs&quot;&gt;zipfs&lt;/a&gt;을 설치해줍니다. 이 extension는 zip 아카이브에서 직접 파일을 읽을 수 있도록 VSCode에 지원을 추가합니다. 예를 들어 이 extension을 추가하지 않으면 import된 React와 같은 설치한 의존성 파일을 열어볼 수 없습니다.&lt;/p&gt;
&lt;p&gt;Editor SDK 설정은 cmd에 다음과 같이 입력 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn dlx @yarnpkg/sdks vscode&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그 후 아래 사진과 같이 현재 프로젝트에 필요한 Editor SDK가 설치가 됩니다. Eslint와 TypeScript SDK가 설치되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock &quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bujnXq/btrgXgyYA9E/hHBVOJ8bZ6k322QCD4BJB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bujnXq/btrgXgyYA9E/hHBVOJ8bZ6k322QCD4BJB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bujnXq/btrgXgyYA9E/hHBVOJ8bZ6k322QCD4BJB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbujnXq%2FbtrgXgyYA9E%2FhHBVOJ8bZ6k322QCD4BJB0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;또한, &lt;code&gt;.vscode&lt;/code&gt;폴더와 &lt;code&gt;.yarn/sdks&lt;/code&gt;에 추가된 SDK를 확인할 수 있습니다. 이때 Zero-Installs을 사용하기 때문에 &lt;code&gt;.vscode&lt;/code&gt;는 &lt;code&gt;.gitignore&lt;/code&gt;에 추가하면 안 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock &quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwtI6f/btrgRUjUCPj/ZkRKpZKnkrjMSZB3CLwub1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwtI6f/btrgRUjUCPj/ZkRKpZKnkrjMSZB3CLwub1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwtI6f/btrgRUjUCPj/ZkRKpZKnkrjMSZB3CLwub1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwtI6f%2FbtrgRUjUCPj%2FZkRKpZKnkrjMSZB3CLwub1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;추가적으로 &lt;a href=&quot;https://github.com/yarnpkg/berry/tree/master/packages/plugin-typescript&quot;&gt;typescript plugin&lt;/a&gt;을 import 시켜줍니다. 이 플러그인은 자체 types를 포함하지 않는 패키지를 추가할 때 &lt;code&gt;@types/&lt;/code&gt; 패키지를 &lt;code&gt;package.json&lt;/code&gt;폴더에 종속성으로 자동으로 추가해줍니다. 이 플러그인 설치는 선택사항이지만 매우 유용하기 때문에 설치하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn plugin import typescript&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;설치 후에는 &lt;code&gt;.yarn/plugins/@yarnpkg&lt;/code&gt; 폴더 아래에 &lt;code&gt;plugin-typescript.cjs&lt;/code&gt; 파일이 생성됩니다. 기타 플러그인 관련해서는 &lt;a href=&quot;https://yarnpkg.com/features/plugins&quot;&gt;여기&lt;/a&gt;를 참고해주세요.&lt;/p&gt;
&lt;p&gt;마지막으로 &lt;code&gt;command + shift + p&lt;/code&gt;키 (맥북 기준)를 눌러 &lt;code&gt;TypeScript 버전 선택..&lt;/code&gt;을 검색해 &lt;code&gt;Use Workspace Version&lt;/code&gt;을 선택해 workspace의 typescript sdk로 변경해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock &quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAhX49/btrgOsVLquu/Om6ObAwKJEl5saBK8A3hh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAhX49/btrgOsVLquu/Om6ObAwKJEl5saBK8A3hh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAhX49/btrgOsVLquu/Om6ObAwKJEl5saBK8A3hh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAhX49%2FbtrgOsVLquu%2FOm6ObAwKJEl5saBK8A3hh0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;자세한 사항은 &lt;a href=&quot;https://yarnpkg.com/getting-started/editor-sdks&quot;&gt;Editor SDK&lt;/a&gt; 참고해주세요.&lt;br&gt;참고로 Eslint 설정과 관련된 작업은 이 글의 목적과 동떨어진 내용이기 때문에 생략하겠습니다. 관련 소스는 예제의 &lt;a href=&quot;https://github.com/saseungmin/yarn-berry-example/blob/main/.eslintrc.js&quot;&gt;&lt;code&gt;.eslintrc.js&lt;/code&gt;&lt;/a&gt;를 참고해주세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  Setting unit test using Jest&lt;/h2&gt;
&lt;p&gt;Jest는 프론트엔드(JavaScript)에서 단위 테스트를 작성하기 위한 테스트 프레임워크로 페이스북에 의해 만들어졌습니다.&lt;br&gt;create-react-app에 jest 설정이 기본적으로 되어 있는 관계로 간단하게 설정만 정리하겠습니다.&lt;/p&gt;
&lt;p&gt;현재 기본적으로 작성되어있는 테스트는 &lt;code&gt;App.tsx&lt;/code&gt;입니다. &lt;code&gt;App.tsx&lt;/code&gt;를 다음과 같이 &lt;code&gt;describe-it&lt;/code&gt;을 사용하여 변경해줍시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { render, screen } from &amp;#39;@testing-library/react&amp;#39;;

import App from &amp;#39;./App&amp;#39;;

describe(&amp;#39;App&amp;#39;, () =&amp;gt; {
  it(&amp;#39;should renders learn react link&amp;#39;, () =&amp;gt; {
    render(&amp;lt;App /&amp;gt;);
    const linkElement = screen.getByText(/learn react/i);

    expect(linkElement).toBeInTheDocument();
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약 해당 파일에 &lt;code&gt;expect(linkElement).toBeInTheDocument();&lt;/code&gt;에 에러가 발생한다면, &lt;code&gt;@testing-library/jest-dom&lt;/code&gt;을 삭제 후 다시 설치해주세요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn remove @testing-library/jest-dom
yarn add -D @testing-library/jest-dom&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CI 파이프라인에서 unit test에 대해 검증을 하기 위해서 &lt;code&gt;package.json&lt;/code&gt;에 &lt;code&gt;test:coverage&lt;/code&gt; 스크립트를 추가시켜줍니다. 또한, jest에 대한 설정을 아래 코드와 같이 해주겠습니다. Jest Config에 대한 설명은 &lt;a href=&quot;https://jestjs.io/docs/configuration&quot;&gt;공식 문서&lt;/a&gt;를 참고해주세요.&lt;/p&gt;
&lt;p&gt;create-react-app의 jest 기본 설정은 &lt;a href=&quot;https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/scripts/utils/createJestConfig.js&quot;&gt;여기&lt;/a&gt;를 참고해주세요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  // ...
  &amp;quot;scripts&amp;quot;: {
    // ...
    &amp;quot;test:coverage&amp;quot;: &amp;quot;yarn test --watchAll=false --coverage&amp;quot;,
  }
  &amp;quot;jest&amp;quot;: {
      // 이 두 파일을 제외해주는 이유는 이 파일들에 대한 테스트를 작성할 이유가 전혀 없기 때문입니다.
    &amp;quot;coveragePathIgnorePatterns&amp;quot;: [
      &amp;quot;src/index.tsx&amp;quot;,
      &amp;quot;src/reportWebVitals.ts&amp;quot;
    ],
    // code coverage가 90프로 이하면 에러를 발생시킵니다.
    &amp;quot;coverageThreshold&amp;quot;: {
      &amp;quot;global&amp;quot;: {
        &amp;quot;branches&amp;quot;: 90,
        &amp;quot;functions&amp;quot;: 90,
        &amp;quot;lines&amp;quot;: 90,
        &amp;quot;statements&amp;quot;: 90
      }
    }
  },
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;작성한 스크립트를 정상적으로 아래와 같이 실행해 하는지 확인해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn run test:coverage&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock &quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AUP6m/btrhdW2qMu0/Y5p3NNlX3bxj6gDWyrd991/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AUP6m/btrhdW2qMu0/Y5p3NNlX3bxj6gDWyrd991/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AUP6m/btrhdW2qMu0/Y5p3NNlX3bxj6gDWyrd991/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAUP6m%2FbtrhdW2qMu0%2FY5p3NNlX3bxj6gDWyrd991%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;  Setting e2e test using Cypress&lt;/h2&gt;
&lt;p&gt;Jest는 단위 테스트를 작성하는 역할을 담당했다면 Cypress는 프론트엔드 e2e 테스트 프레임워크입니다.&lt;/p&gt;
&lt;p&gt;먼저 Cypress를 설치해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn add -D cypress&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 &lt;code&gt;package.json&lt;/code&gt;에 Cypress를 실행하는 &lt;code&gt;cypress:open&lt;/code&gt; 스크립트를 추가시켜줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  // ...
  &amp;quot;scripts&amp;quot;: {
    // ...
    &amp;quot;cypress:open&amp;quot;: &amp;quot;cypress open&amp;quot;,
  }
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 스크립트를 실행하면 &lt;code&gt;cypress.json&lt;/code&gt; 파일과 &lt;code&gt;cypress&lt;/code&gt;폴더가 생성된 걸 확인하실 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn run cypress:open&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;더불어 예제 테스트가 생성된 걸 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock &quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bggpdp/btrhdDu0rnr/iG5wv5Sfyt2MlEJNnlKp21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bggpdp/btrhdDu0rnr/iG5wv5Sfyt2MlEJNnlKp21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bggpdp/btrhdDu0rnr/iG5wv5Sfyt2MlEJNnlKp21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbggpdp%2FbtrhdDu0rnr%2FiG5wv5Sfyt2MlEJNnlKp21%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;  TypeScript를 사용한 프로젝트에서 Jest와 Cypress의 충돌 문제&lt;/h3&gt;
&lt;p&gt;실행한 Cypress는 종료시킨 뒤 Cypress에서 TypeScript를 사용하려면 추가적인 설정을 해줘야 합니다. 안타깝게도 TypeScript를 사용한 &lt;strong&gt;동일한 프로젝트에서 Jest와 Cypress를 모두 사용하는 경우&lt;/strong&gt; 두 테스트 runner가 전역적으로 등록한 TypeScript types가 충돌할 수 있습니다. 이러한 이유는 Jest와 Cypress는 &lt;code&gt;describe&lt;/code&gt; 및 &lt;code&gt;it&lt;/code&gt; 함수에 대해 충돌 types을 제공하기 때문입니다. 또한, Jest와 Cypress 둘 다 &lt;code&gt;expect&lt;/code&gt; assertion 등에 대한 충돌하는 types도 제공하기 때문입니다. 추가적으로 명령어가 비슷한 이유는 Cypress는 &lt;a href=&quot;https://mochajs.org/&quot;&gt;Mocha&lt;/a&gt;라는 테스트 프레임워크를 의존하고 있기 때문입니다. (&lt;a href=&quot;https://docs.cypress.io/guides/references/bundled-tools&quot;&gt;참고&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;TypeScript를 사용한 Jest와 Cypress 충돌에 대한 자세한 내용은 &lt;a href=&quot;https://docs.cypress.io/guides/tooling/typescript-support#Clashing-types-with-Jest&quot;&gt;Cypress 공식 문서: typescript-support&lt;/a&gt;를 참고해주세요.&lt;/p&gt;
&lt;p&gt;Cypress와 Jest의 types가 충돌하는 문제 때문에 추가적으로 &lt;code&gt;cypress&lt;/code&gt; 폴더 안에 &lt;code&gt;tsconfig.json&lt;/code&gt; 파일을 생성해주고 다음과 같이 작성해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;// ./cypress/tsconfig.json
{
  &amp;quot;extends&amp;quot;: &amp;quot;../tsconfig.json&amp;quot;,
  &amp;quot;compilerOptions&amp;quot;: {
    &amp;quot;isolatedModules&amp;quot;: false,
    &amp;quot;noEmit&amp;quot;: true,
    // jest types와 충돌을 피하기 위해 명시적으로 작성해줍니다.
    &amp;quot;types&amp;quot;: [&amp;quot;cypress&amp;quot;]
  },
  &amp;quot;include&amp;quot;: [&amp;quot;./**/*.ts&amp;quot;]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;추가적으로 Eslint도 Jest와 충돌이 발생하기 때문에 &lt;a href=&quot;https://github.com/cypress-io/eslint-plugin-cypress&quot;&gt;eslint-plugin-cypress&lt;/a&gt;도 의존성을 설치해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn add -D eslint-plugin-cypress&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 Cypress 폴더에만 Cypress Eslint 룰을 적용하기 위해서 &lt;code&gt;.eslintrc.js&lt;/code&gt;도 룰을 추가시켜줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;module.exports = {
  // ...
  overrides: [
    {
      extends: [
        &amp;#39;plugin:cypress/recommended&amp;#39;,
      ],
      files: [
        &amp;#39;cypress/**/*.ts&amp;#39;,
      ],
      rules: {
          // 추후 필요한 rule를 추가해주면 됩니다.
      },
    },
  ]
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 기존의 JavaScript 작성된 &lt;code&gt;example&lt;/code&gt; 파일들을 TypeScript 확장자로 변경해보면 정상적으로 적용된 것을 확인할 수 있을 겁니다!&lt;/p&gt;
&lt;h3&gt;  Cypress e2e 테스트 실행하기&lt;/h3&gt;
&lt;p&gt;테스트를 작성하기 전에 &lt;code&gt;cypress/integration&lt;/code&gt; 폴더에 생성된 예제 테스트와 &lt;code&gt;fixtures&lt;/code&gt; 폴더 안에 있는 &lt;code&gt;example.json&lt;/code&gt;는 불필요하므로 삭제해줍니다.&lt;br&gt;그리고 아래 기본으로 생성된 페이지에 대한 간단한 e2e 테스트를 작성해 보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;50%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d4WpMD/btrgHD9S4vT/uAkgcZLXyKaPg060jcyTzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d4WpMD/btrgHD9S4vT/uAkgcZLXyKaPg060jcyTzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d4WpMD/btrgHD9S4vT/uAkgcZLXyKaPg060jcyTzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd4WpMD%2FbtrgHD9S4vT%2FuAkgcZLXyKaPg060jcyTzk%2Fimg.png&quot; width=&quot;50%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;먼저 &lt;code&gt;cypress.json&lt;/code&gt;에 &lt;code&gt;baseUrl&lt;/code&gt;를 설정해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;// cypress.json
{
  &amp;quot;baseUrl&amp;quot;: &amp;quot;http://localhost:3000&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 &lt;code&gt;cypress/integration/sample&lt;/code&gt; 폴더에 &lt;code&gt;sample.spec.ts&lt;/code&gt;에 다음과 같이 작성해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// sample.spec.ts
describe(&amp;#39;sample&amp;#39;, () =&amp;gt; {
  it(&amp;#39;finds the content &amp;quot;Learn React&amp;quot;&amp;#39;, () =&amp;gt; {
    cy.visit(&amp;#39;/&amp;#39;);

    cy.contains(&amp;#39;Learn React&amp;#39;);
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 개발 서버를 실행 후 Cypress e2e 테스트가 정상적으로 통과하는지 확인해봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn start
yarn cypress:open&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;sample.spec.ts&lt;/code&gt;에 대한 테스트가 성공적으로 성공하는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjVTSC/btrhcPbVBOW/wK8BxPujia6z2noqIB5KK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjVTSC/btrhcPbVBOW/wK8BxPujia6z2noqIB5KK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjVTSC/btrhcPbVBOW/wK8BxPujia6z2noqIB5KK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjVTSC%2FbtrhcPbVBOW%2FwK8BxPujia6z2noqIB5KK1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;마지막으로 Github Actions CI 파이프라인에서 Cypress e2e 테스트를 검증하기 위해서 한가지 설정을 추가해주죠!&lt;br&gt;e2e 테스트를 할 때마다 개발 서버를 실행 후 Cypress를 실행해야 합니다. 또한, 개발 서버 실행이 오래걸리면 서버가 실행 전에 Cypress가 실행되어 에러를 유발할 수 있습니다. 그러므로 Cypress의 작업을 한 번에 가능하게 설정해주고, CI 단계에서 HEADLESS된 Cypress 실행을 위해 스크립트를 추가로 작성해줍니다.&lt;/p&gt;
&lt;p&gt;먼저 개발 서버 시작과 실행되기까지 기다린 후 Cypress를 실행해주기 위해 &lt;a href=&quot;https://github.com/bahmutov/start-server-and-test&quot;&gt;start-server-and-test&lt;/a&gt;에 대한 의존성을 설치해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn add -D start-server-and-test&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 다음과 같이 &lt;code&gt;package.json&lt;/code&gt;에 &lt;code&gt;cypress:run&lt;/code&gt;과 &lt;code&gt;test:e2e&lt;/code&gt; 스크립트 두 개를 추가시켜줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  // ...
  &amp;quot;scripts&amp;quot;: {
    // ...
    &amp;quot;cypress:run&amp;quot;: &amp;quot;cypress run&amp;quot;,
    &amp;quot;test:e2e&amp;quot;: &amp;quot;start-server-and-test start http://localhost:3000 cypress:run&amp;quot;,
  }
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 정상적으로 개발 서버가 실행 후 Cypress가 실행되는지 확인합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;yarn run test:e2e&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아래와 사진과 같이 e2e 테스트가 성공적으로 통과하는 걸 확인할 수 있습니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock &quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rQ8Sd/btrhjsZUcsj/xDFp6VU9V8LsykT8WemSKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rQ8Sd/btrhjsZUcsj/xDFp6VU9V8LsykT8WemSKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rQ8Sd/btrhjsZUcsj/xDFp6VU9V8LsykT8WemSKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrQ8Sd%2FbtrhjsZUcsj%2FxDFp6VU9V8LsykT8WemSKK%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;추가로 &lt;code&gt;.gitignore&lt;/code&gt;에 Cypress 관련 파일을 추가시켜줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# cypress
cypress/screenshots
cypress/videos
cypress/log&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;관련한 내용은 &lt;a href=&quot;https://docs.cypress.io/guides/continuous-integration/introduction&quot;&gt;Cypress 공식문서: continuous-integration&lt;/a&gt;를 참고해주세요.&lt;/p&gt;
&lt;h2&gt;  Github Actions CI/CD&lt;/h2&gt;
&lt;p&gt;이제 마지막으로 &lt;a href=&quot;https://github.com/features/actions&quot;&gt;Github Actions&lt;/a&gt;로 &lt;a href=&quot;https://www.redhat.com/ko/topics/devops/what-is-ci-cd&quot;&gt;CI/CD&lt;/a&gt; 파이프라인을 구축해봅시다.&lt;/p&gt;
&lt;p&gt;CI/CD가 무엇이고 왜 해야 하는지에 대한 내용은 역시 주제에 벗어나기 때문에 생략하겠습니다.&lt;/p&gt;
&lt;p&gt;CI 단계에서는 main 브랜치로 pull request 된 상태일 때만 unit 테스트와 e2e 테스트 검증과 lint 체크를 하도록 하겠습니다.&lt;br&gt;CD 단계에서는 main 브랜치로 push될 때 프로젝트 빌드 후 간단하게 &lt;a href=&quot;https://pages.github.com/&quot;&gt;Github Pages&lt;/a&gt;에 배포가 되도록 하겠습니다.&lt;/p&gt;
&lt;h3&gt;  Github Actions CI 파이프라인 설정&lt;/h3&gt;
&lt;p&gt;우선 Eslnit에 대한 lint 체크를 하기 위해 &lt;code&gt;package.json&lt;/code&gt;에 &lt;code&gt;lint&lt;/code&gt; 스크립트를 추가시켜줍시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  // ...
  &amp;quot;scripts&amp;quot;: {
    // ...
    // eslint의 검사 범위는 ts, tsx 파일
    &amp;quot;lint&amp;quot;: &amp;quot;eslint --ext ts,tsx .&amp;quot;,
  }
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Github Actions에 CI 파이프라인을 작성하기 위해서 &lt;code&gt;.github/workflows&lt;/code&gt; 폴더 안에 &lt;code&gt;.yml&lt;/code&gt;(YAML) 확장자로 작성해야 합니다. (&lt;a href=&quot;https://docs.github.com/en/actions/quickstart#creating-your-first-workflow&quot;&gt;참고&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;그러면 &lt;code&gt;.github/workflows&lt;/code&gt; 폴더 안에 &lt;code&gt;ci.yml&lt;/code&gt; 파일을 생성한 뒤 다음과 같이 작성해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yml&quot;&gt;name: CI

on:
  # main 브랜치로 pull request 일 때만 수행한다.
  pull_request:
    branches: [main]

jobs:
  continuous-integration:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 14.x
        uses: actions/setup-node@v1
        with:
          node-version: 14.x

      - name: Install dependencies
        # install 옵션에 대해서는 yarn 공식 문서를 참고해주세요.
        # https://yarnpkg.com/cli/install
        run: yarn install --immutable --immutable-cache

      - name: Check Lint
        # lint 검사
        run: yarn lint

      - name: Check Unit Test
        # unut test 검증
        run: yarn test:coverage&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Cypress Github Actions&lt;/h4&gt;
&lt;p&gt;Cypress는 공식적으로 제공하는 Github Actions가 있습니다. 물론, 굳이 사용할 필요는 없습니다. 다만, 더 편하게 cypress 관련 Cli 명령어를 사용할 수 있습니다. (&lt;a href=&quot;https://github.com/cypress-io/github-action&quot;&gt;Cypress Github Actions&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;안타깝게도 공식적으로 제공하는 Cypress Github Actions는 yarn berry에서 완벽한 호환은 안 됩니다.&lt;/strong&gt; 설치된 Cypress 모듈을 찾지 못해서 생기는 오류로 이 문제에 대해서 여러 테스트를 진행해보았지만 해결되지 않았습니다. 이 문제를 해결하기 위해 직접 Cypress Github Actions의 &lt;a href=&quot;https://github.com/cypress-io/github-action/issues/430&quot;&gt;Github issue 란에 질문&lt;/a&gt;을 올려보았었지만, 역시 제공하는 방법처럼 사용은 불가능하다는 것이었습니다.&lt;/p&gt;
&lt;p&gt;해결 방법으로는 &lt;a href=&quot;https://github.com/cypress-io/github-action#custom-test-command&quot;&gt;custom command&lt;/a&gt;를 입력해야 하는 것이고, 이로 인해 Cypress Github Actions에서 제공하는 command 들은 작성해도 동작하지 않습니다. 예를 들어 아래와 같이 &lt;a href=&quot;https://github.com/cypress-io/github-action#browser&quot;&gt;&lt;code&gt;brower&lt;/code&gt; 옵션&lt;/a&gt;에 &lt;code&gt;chrome&lt;/code&gt; 설정해도 테스트 환경은 chrome으로 변하지 않습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yml&quot;&gt;# ...
- name: Cypress run
  uses: cypress-io/github-action@v2
  with:
    install: false
    command: yarn test:e2e
    # 동작하지 않음!
    brower: chrome&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그래서 이 역시 아래와 같이 &lt;code&gt;command&lt;/code&gt; 옵션에 직접 추가 해주어야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yml&quot;&gt;# ...
- name: Cypress run
  uses: cypress-io/github-action@v2
  with:
    install: false
    # https://docs.cypress.io/guides/guides/command-line#cypress-run-browser-lt-browser-name-or-path-gt
    command: yarn test:e2e --browser chrome&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이러한 문제는 모든 명령어가 동일하게 적용됩니다. 하지만, 괜찮습니다. Cypress Github Actions를 사용하는 거보단 번거롭긴 하겠지만 Cypress에서 제공하는 Cli를 작성하여 모든 명령어를 사용할 수 있습니다. (&lt;a href=&quot;https://docs.cypress.io/guides/guides/command-line&quot;&gt;Cypress Command Line 참고&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;여기서는 Cypress Github Actions를 사용하여 작성하겠습니다.&lt;/p&gt;
&lt;p&gt;아래의 코드는 &lt;code&gt;ci.yml&lt;/code&gt;의 전체 코드입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yml&quot;&gt;name: CI

on:
  pull_request:
    branches: [main]

jobs:
  continuous-integration:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 14.x
        uses: actions/setup-node@v1
        with:
          node-version: 14.x

      - name: Install dependencies
        run: yarn install --immutable --immutable-cache

      - name: Check Lint
        run: yarn lint

      - name: Check Unit Test
        run: yarn test:coverage

      - name: Check e2e Test
        uses: cypress-io/github-action@v2
        with:
          install: false
          command: yarn test:e2e&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 여태까지 작업 된 내용들을 Github Repository의 main 브랜치로 Pull Request를 보내 Github Actions CI 파이프라인이 아래 사진과 같이 정상적으로 동작하는지 확인해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqGQWb/btrhfReZwMz/ODYTalzEY9lqfn4Jm0Uv60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqGQWb/btrhfReZwMz/ODYTalzEY9lqfn4Jm0Uv60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqGQWb/btrhfReZwMz/ODYTalzEY9lqfn4Jm0Uv60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqGQWb%2FbtrhfReZwMz%2FODYTalzEY9lqfn4Jm0Uv60%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;  Github Actions CD 파이프라인 설정 및 Github Pages에 배포&lt;/h3&gt;
&lt;p&gt;이제 마지막으로 Github Pages에 배포해보겠습니다. &lt;a href=&quot;https://github.com/JamesIves/github-pages-deploy-action&quot;&gt;github-pages-deploy-action&lt;/a&gt;를 사용하여 Github Page에 배포하는 건 간단하게 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;다음과 같이 &lt;code&gt;.github/workflows&lt;/code&gt;폴더 안에 &lt;code&gt;cd.yml&lt;/code&gt;을 작성해 줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yml&quot;&gt;name: CD
# main 브랜치로 push될 때만 동작한다.
on:
  push:
    branches:
      - main
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout  ️
        uses: actions/checkout@v2.3.1

      - name: Install and Build  
        run: |
          yarn install --immutable --immutable-cache
          yarn run build
      - name: Deploy  
        uses: JamesIves/github-pages-deploy-action@4.1.5
        with:
          branch: gh-pages
          # 빌드되는 폴더명
          folder: build&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;한가지 주의사항으로 create-react-app을 사용한 React 프로젝트는 기본적으로 루트(&lt;code&gt;/&lt;/code&gt;) URL을 기준으로 프로젝트가 생성됩니다. 하지만 아래 예시와 같이 GitHub Pasges의 URL은 Repository 이름으로 된 path를 기본적으로 가지고 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;// 저장소
https://github.com/&amp;lt;username&amp;gt;/&amp;lt;repository&amp;gt;
https://github.com/saseungmin/yarn-berry-example

// Github Pages
https://&amp;lt;username&amp;gt;.github.io/&amp;lt;repository&amp;gt;
https://saseungmin.github.io/yarn-berry-example&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;따라서 create-react-app을 사용한 React 프로젝트에서 Repository 이름을 가진 path를 사용하도록 수정해줘야 합니다. 그러므로 &lt;code&gt;package.json&lt;/code&gt;에 &lt;code&gt;homepage&lt;/code&gt; 옵션을 작성해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  // ...
  &amp;quot;homepage&amp;quot;: &amp;quot;https://&amp;lt;username&amp;gt;.github.io/&amp;lt;repository&amp;gt;&amp;quot;,
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 main으로 push를 하면 &lt;code&gt;cd.yml&lt;/code&gt; workflow가 트리거 됩니다. 그리고 아래와 같이 Actions 탭에 정상적으로 CD 파이프라인이 동작하는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tiXVS/btrhjtZdZtF/XtjQ6S59rghMJOyyDMMbv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tiXVS/btrhjtZdZtF/XtjQ6S59rghMJOyyDMMbv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tiXVS/btrhjtZdZtF/XtjQ6S59rghMJOyyDMMbv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtiXVS%2FbtrhjtZdZtF%2FXtjQ6S59rghMJOyyDMMbv1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;이제 아래 사진과 같이 Github Repository에 Settings 탭의 Pages 섹션으로 이동해서 Source의 gh-pages를 선택 후 Save 버튼을 눌러 저장합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEbj5D/btrherVRLdB/AcCiwXL0jKW1DIKMobS6P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEbj5D/btrherVRLdB/AcCiwXL0jKW1DIKMobS6P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEbj5D/btrherVRLdB/AcCiwXL0jKW1DIKMobS6P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEbj5D%2FbtrherVRLdB%2FAcCiwXL0jKW1DIKMobS6P1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그러면 이제 Github Pages에 자신이 빌드 후 배포한 사이트에 접속할 수 있게 됩니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OFnYJ/btrhdB5mNpk/PZ7PLBpkRNjQeuWeRAyUAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OFnYJ/btrhdB5mNpk/PZ7PLBpkRNjQeuWeRAyUAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OFnYJ/btrhdB5mNpk/PZ7PLBpkRNjQeuWeRAyUAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOFnYJ%2FbtrhdB5mNpk%2FPZ7PLBpkRNjQeuWeRAyUAk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;  마무리하며&lt;/h2&gt;
&lt;p&gt;우리는 이러한 작업을 통해 빠르게 개발할 수 있게 되었고 빠르게 피드백을 얻어 고쳐나갈 수 있는 프로젝트의 &lt;strong&gt;동작하는 골격&lt;/strong&gt;을 만들었습니다! 이제 오로지 개발에만 신경 쓰고 진행하면 됩니다!&lt;/p&gt;
&lt;p&gt;최근에 회사에서 Yarn berry로 프로젝트를 세팅할 일이 있어서 겪었던 것들을 정리하였습니다. 부족한 부분이 있거나 잘못된 내용이 있으면 댓글로 남겨주시면 감사하겠습니다.   해당 예를 담은 &lt;a href=&quot;https://github.com/saseungmin/yarn-berry-example&quot;&gt;Github Repository&lt;/a&gt;를 참고해주세요.&lt;/p&gt;
&lt;p&gt;쓰다 보니 생각보다 너무 길어졌지만, 한 번쯤 정리해야겠다는 생각을 실천해서 만족스럽습니다. 많은 분께 도움이 되었으면 좋겠고 끝까지 긴 글 읽어주셔서 감사드립니다.  &lt;/p&gt;</description>
      <category>Unit Test &amp;amp; e2e Test &amp;amp; TDD</category>
      <category>CD</category>
      <category>CI</category>
      <category>Cypress</category>
      <category>Github Actions</category>
      <category>GitHub Pages</category>
      <category>jest</category>
      <category>react</category>
      <category>typescript</category>
      <category>yarn berry</category>
      <category>초기 세팅</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/28</guid>
      <comments>https://haranglog.tistory.com/28#entry28comment</comments>
      <pubDate>Mon, 11 Oct 2021 15:55:19 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 프론트엔드 개발자로서의 첫 발걸음을 내딛다!</title>
      <link>https://haranglog.tistory.com/27</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GCIZz/btrgBlIz8mD/DmOkp5dsuXpINbkhAOek00/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GCIZz/btrgBlIz8mD/DmOkp5dsuXpINbkhAOek00/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GCIZz/btrgBlIz8mD/DmOkp5dsuXpINbkhAOek00/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGCIZz%2FbtrgBlIz8mD%2FDmOkp5dsuXpINbkhAOek00%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이 글은 프론트엔드 개발자로서 취업 후 작성하는 회고 글입니다!  &lt;br&gt;저의 간단한 배경은 이전에 SI 개발자로 8개월 정도 일한 경험이 있었고 SI 회사에 입사하기 전에 프론트엔드 개발자로 눈이 떴고, 8개월가량 일과 병행하면서 공부를 한 뒤, 퇴사 후 7개월 동안 공부를 더 하고 취업을 하게 되었습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  프론트엔드 개발자로 취업하기까지 공부해오며 느꼈던 게 있었어?&lt;/h2&gt;
&lt;p&gt;프론트엔드 개발자가 되기로 마음먹었던 게…. 작년 4월쯤이었다. 프론트엔드 개발자가 되기로 마음먹게 된 계기는 이전에 작성한 회고 글을 참고! (&lt;a href=&quot;https://haranglog.tistory.com/17?category=943668&quot;&gt;나는 왜 프론트 앤드를 선택했을까?&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;작년 4월부터이니까 1년 반 정도 만에 드디어 꿈꾸던 프론트엔드 개발자로서 첫발을 내딛게 되었다. 그 과정은 그렇게 순탄하지는 않았던 거 같다. 스스로 생각하기에 프론트엔드 개발자가 되어야겠다고 마음먹었던 게 남들보다 늦다고 판단했었고 그로 인해 더 열심히 해야겠다는 압박감이 있었다. 그 압박감으로 인해 하루라도 공부를 하지 않으면 안 된다는 생각 때문에 스스로를 더 힘들게 하는 병을 가지게 되었다.  이런 부분은 취업을 준비하는 취준생이라면 모두가 느끼고 있을거라 생각한다.&lt;/p&gt;
&lt;p&gt;취업까지의 과정들을 살펴보면 독학도 해보고, 돈을 주고 온라인 부트캠프도 들어봤었고, 온라인 강의도 들어보고, 스터디도 많이 하며 책도 많이 읽었던 거 같다. 어떤 걸 해왔는지는 1일 1커밋 1년 차 회고를 참고! (&lt;a href=&quot;https://haranglog.tistory.com/26?category=943668&quot;&gt;Github Daily Commit(1일 1커밋)을 시작한지 1년이 지났다..&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;프론트엔드 공부를 해오면서 막혔던 순간들이 있었다. 예를 들어보자면 혼자 공부하고 있을 때 뭔가 막히는 이걸 해결해가는 방법이나 내가 공부하는 방식이 이게 맞나? 또는 이렇게 코드를 작성하는 게 맞는 걸까? 모든 게 늘 의문투성이였던 거 같다.&lt;br&gt;그럴 때마다 느꼈던 건 &lt;strong&gt;멘토나 같이 이런 부분에 관해서 얘기할 수 있는 동료가 필요했던 거 같다.&lt;/strong&gt; 그래서 찾았던 방법이 스터디나 커뮤니티를 찾아보자는 것이었다. 역시나 찾기는 매우 힘들었고, 여전히 뜻이 맞는 사람들을 찾는 건 참 힘들다고 느끼고 있다. 하지만, 가만히 있어서는 해결되는 게 없기에 부트캠프를 참여하게 되어 커뮤니티들을 얻을 수 있었고, 스터디도 많을 땐 일주일에 3개까지 하며 관련 분야의 지식과 인사이트를 흡수할 수 있었다.&lt;/p&gt;
&lt;p&gt;스스로 생각하기에는 올바르게 성장해나가고 있다고 생각한다.  올바르게 성장할 수 있었던 원동력은 첫 번째는 &lt;strong&gt;보고 배울 수 있는 멘토분들을 찾았다&lt;/strong&gt;는 것이었고, &lt;strong&gt;커뮤니케이션 능력을 향상시키고 기술적으로 다른 분들과 얘기할 수 있던 스터디&lt;/strong&gt;가 그 두 번째였다. 세 번째는 두 번째와도 연결되는데 &lt;strong&gt;스터디를 통해 책을 꾸준히 읽어나갔다는 점&lt;/strong&gt;이라고 생각한다. 정말 지식을 쌓기에 책만큼 좋은 게 없다고 생각한다. 혼자보다는 역시 함께 자라기를 실천할 수 있는 스터디를 통해 책을 읽는 건 그 효율이 혼자 읽을 때보다 압도적으로 좋다고 느껴졌다.&lt;/p&gt;
&lt;p&gt;마지막으로 느꼈던 건 &lt;strong&gt;열심히 하다 보면 언젠가 기회가 온다&lt;/strong&gt;는 점. 늦게 오든가 일찍 오든가 결국엔 기회가 온다!  다만, 그런 기회가 오기까지 큰 노력이 필요하다는 점이다. 기회는 함부로 오지 않는다. 그 &lt;strong&gt;기회가 왔을 때 잡을 수 있는 능력이 돼야지 그 기회를 잡을 수 있다고 느꼈다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;  지금 회사에 입사하기까지의 과정과 이 회사를 선택한 이유가 뭐야?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;회사 이름은 위 명함에서도 나왔듯이 &lt;a href=&quot;https://www.kasa.co.kr/&quot;&gt;카사&lt;/a&gt;라는 회사입니다. 앞으로 쓰는 회사의 명칭을 카사라고 작성하겠습니다.  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;  입사하기까지의 과정&lt;/h3&gt;
&lt;p&gt;처음 취업을 목표로 했던 회사들은 우리가 흔히 말하는 네카라쿠배였다. 그래서 처음부터 카사에 취업할 생각은 전혀 없었고, 카사라는 회사가 있는지조차 몰랐다.   그리고 너무 취업에 목매달지 않고 전 회사를 퇴사한 지 7개월 정도 지나고 이제 취업을 해야겠다고 마음먹고 회사를 찾아보려고 하고 있었던 시기였다.&lt;/p&gt;
&lt;p&gt;카사를 알게 된 계기는 역시 스터디를 통해서 알게 되었다. 같이 스터디 하시는 분이 카사의 프론트엔드 개발자로 계셨다. 그때 마침 프론트엔드 개발자를 채용 중에 있었고 사내 추천으로 지원을 하게 되었다.&lt;/p&gt;
&lt;p&gt;생각했던 채용과정은 카사가 스타트업이다 보니 어렵지 않겠다고 느꼈지만, 채용 단계가 코딩테스트, 기술 면접, 컬처핏 1차, 2차에 레퍼런스 체크까지 있었다. 나름대로 준비도 열심히 해서 그런지 좋게 봐주셨고, 처우 협의 후 운이 좋게 입사를 할 수 있게 되었다.&lt;/p&gt;
&lt;h3&gt;  카사를 선택한 이유&lt;/h3&gt;
&lt;p&gt;네카라쿠배를 준비하다가 스타트업으로 가게 된 계기가 뭐냐고 생각하시고 물어보시는 분들도 많다. 이 부분에 있어서는 6가지의 이유가 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;네카라쿠배는 신입으로 입사하기에 정말 입사할 수 있는 문이 좁다. 그리고 흔히 말하는 굉장히 빡새다! 뛰어난 능력자들만이 신입으로 입사가 가능해 보였다. 많이 도전해보았지만, 현실적으로 쉽지 않은 문턱이었다.&lt;/li&gt;
&lt;li&gt;회사에 지원하기 전부터 스터디를 같이 하시는 분이랑 기술적인 부분에 관해서 이야기를 해보니까 생각하고 있는 부분들이 비슷한 점들이 많다고 느꼈고, 이야기에 공감되는 부분들이 많았다. 그래서 내가 카사에 입사해도 내가 앞으로 하고 싶은 기술 스택이나 기술적인 부분들을 커뮤니케이션을 통해 좋게 진행할 수 있을 거라 느꼈다.&lt;/li&gt;
&lt;li&gt;취업을 할 때 이런 회사를 가야겠다고 정했던 게 하나 있었다. 다 좋아도 이걸 하지 않는 회사는 안가기로 스스로 정해놨었다. 기술적인 이유였는데, 코드를 작성할 때 테스트 코드를 작성하지 않는 회사는 가고 싶지 않았다. 더 나아가서 TDD를 할 수 있는 회사를 가고 싶었다. 만약에 아직 도입하지 않았어도 앞으로 이런 새로운 기술 도입 제안에 대해서 열려있는 곳을 원했다. 아쉽지만, 아직 카사의 프론트엔드에서는 테스트 코드를 작성하지 않고 있었다. 그래서 면접과 스터디를 통해 이 부분에 대해서 많은 이야기를 유도했었고 물어봤었다. 대답으로는 아직은 테스트 코드를 작성하지 않고 TDD를 도입하지 않고 있지만, 앞으로 할 프로젝트에서는 꼭 테스트 코드를 작성하려고 하고 있다고 했었다. 더 나아가서 주도적으로 TDD를 사용하자고 하실 분이 오시면 너무 좋을 거 같다는 말을 들었다. 그래서 내가 가서도 충분히 도움을 줄 수 있을 거라 생각했었고, 테스트 코드 없는 코드를 작성하지 않아도 될 거 같다는 확신이 들었다. 현재는 새로 진행 중인 프로젝트에서 TDD를 적용하여 개발하고 있다.  &lt;/li&gt;
&lt;li&gt;컬처핏 면접을 봤을 때 꽤 좋은 인상을 받았다. 면접을 진행하신 면접관분들이 어렵지 않은 분위기에 서로 존중해주는 분위기를 받았다. 그래서 좋은 동료들과 같이 일할 수 있겠다고 느꼈다.&lt;/li&gt;
&lt;li&gt;회사를 볼 때 나와 회사가 같이 성장할 수 있을까를 많이 봤다. 카사는 부동산이라는 도메인으로 위험 요소도 있지만, 앞으로 성장할 가능성이 무궁무진한 도메인이라고 생각했다. 그리고 상업용 부동산으로 주식과 비슷하게 아무 때나 사고팔며 지분을 소유할 수 있다는 점에서 주식을 하는 나의 관점에서 확실히 여태까지 없던 신선함을 느꼈다. 개인적인 성장에 대해서는 지금 카사의 홈페이지를 보면 웹사이트에서는 거래할 수 없다. 내가 이제 웹사이트에서 거래할 수 있게 만드는 큰 과제가 있으며, open API 등 앞으로의 과제가 많아 보였다.  만약 대기업이였으면 신입이 이런 과제에 도전할 기회가 많이 있었을까..?&lt;/li&gt;
&lt;li&gt;마지막은 복지에 관한 건데 장비구매비 500만 원 지원에 달에 40만 원 점심 식사 비용 별도 지원, 교육비 전액 지원, 직급이 없는 수평적이고 자유로운 조직 문화, 전 직원 스톡옵션과 만족스러운 연봉 그리고 뛰어난 동료들! 너무 회사 자랑 같지만, 만족스럽게 다니고 있다.  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;823&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kyYqN/btrgC5LEvZh/tOi4PAi7kmsEMcyoSxGipk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kyYqN/btrgC5LEvZh/tOi4PAi7kmsEMcyoSxGipk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kyYqN/btrgC5LEvZh/tOi4PAi7kmsEMcyoSxGipk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkyYqN%2FbtrgC5LEvZh%2FtOi4PAi7kmsEMcyoSxGipk%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;823&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;  앞으로 카사에서 하고 싶은 거나 목표가 있어?&lt;/h2&gt;
&lt;p&gt;면접 때 하고 싶은 두 가지를 말했었다. 하나는 &lt;strong&gt;TDD를 사용해서 개발하는 것&lt;/strong&gt;과 다른 하나는 &lt;strong&gt;카사의 기술 블로그를 만들어보는 것&lt;/strong&gt;이다. 하나는 이미 진행 중이고, 기술 블로그를 만들어보는 건 앞으로의 이뤄나갈 것이다. 하나는 이미 이뤘으니 다른 하나를 추가해볼까 한다. &lt;strong&gt;웹 사이트에서도 거래할 수 있도록 하기와 더 나아가서 궁극적인 목표로 사용자가 원하는 카사 플랫폼 만들기!&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;지금 회사에 입사해서 1달하고 2주 차지만 직접 와서 경험하고 있는 조직 문화와 회사가 가지고 있는 굉장히 큰 비전 등 긍정적인 감정을 느끼고 있다. 앞으로도 지금 카사에 느끼는 이 좋은 감정을 변하지 않고 가져갔으면 좋겠고, 좋은 동료들과 협업하며 어려운 과제들을 차근차근 해결해 나갔으면 하는 바람이고 카사와 함께 성장해가는 함께 자라기를 실천해 나갔으면 좋겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1280&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bn4Yz/btrgC7bESEb/9wAu8rYMvauCHyuOsJaNPK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bn4Yz/btrgC7bESEb/9wAu8rYMvauCHyuOsJaNPK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bn4Yz/btrgC7bESEb/9wAu8rYMvauCHyuOsJaNPK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBn4Yz%2FbtrgC7bESEb%2F9wAu8rYMvauCHyuOsJaNPK%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1280&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;3호 공모가 성공적으로 마무리된 후 건물 액자 뒷면에 사이닝! 더 많아졌지만.. 그 후 사진을 못 찍었다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;  개인적인 목표&lt;/h3&gt;
&lt;p&gt;작년 &lt;a href=&quot;https://haranglog.tistory.com/24?category=943668&quot;&gt;2020년 회고&lt;/a&gt;때 2021년 목표를 했던 것들을 모두 지켜나가고 있다. 그중에 하나가 프론트엔드 개발자로서 취업하는 것이었는데 가장 어려웠던 그 목표를 이루게 되어서 만족스럽다.&lt;/p&gt;
&lt;p&gt;프론트엔드 개발자로 취업을 했다고 끝난 게 아니라는 것을 누구보다 잘 알고 있다. 그러므로 성장에 있어서는 소홀히 하지 않을 것이다. 카사에서도 앞으로 커리어에 대한 경험을 쌓아가며 혼자만의 성장이 아닌 &lt;strong&gt;함께 자라기를 실천&lt;/strong&gt;할 것이다. 또한, 커뮤니케이션하는 법도 많이 배워나갈 것이다.&lt;/p&gt;
&lt;p&gt;추가로 스터디를 찾기 어려워서 혼자서 고민했던 그때를 생각하면서 만들고 있는 &lt;a href=&quot;https://github.com/CodeSoom/ConStu&quot;&gt;스터디를 모집할 수 있는 개인 프로젝트&lt;/a&gt;도 계속 키워나갈 것이다. 그리고 블로그에 너무 소홀히 하고 있었는데, 회사에서 겪은 기술적인 문제들을 해결한 것들을 꾸준히 정리해야겠다.&lt;/p&gt;
&lt;p&gt;이제는 너무 성장에 대한 압박감을 가지며 자기 계발을 하기보단 조금은 마음의 여유를 가지고, 건강을 생각하며 나아갈 생각이다. 나는 프론트엔드 공부를 시작하며 가졌던 &lt;em&gt;지치지 말고 꾸준히&lt;/em&gt; 라는 좌우명을 바탕으로 토끼가 아닌 거북이처럼 지치지 않는 걸 항상 목표로 하고 있다. &lt;strong&gt;남들보다 조금은 느릴 수 있지만, 천천히 조용하게 오늘보다 더 나은 내일을 기대하며 앞으로 나아갈 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;앞으로의 더 많은 계획과 회고에 대해서는 작년과 마찬가지로 연말에 올해가 가기 전에 2021년 회고에 작성해야겠다.&lt;/p&gt;
&lt;p&gt;마무리하면서 모든 취준생 화이팅! 봄은 온다.  &lt;/p&gt;</description>
      <category>회고</category>
      <category>Front-End</category>
      <category>retrospect</category>
      <category>개발자</category>
      <category>이직</category>
      <category>취업</category>
      <category>프론트앤드</category>
      <category>프론트엔드</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/27</guid>
      <comments>https://haranglog.tistory.com/27#entry27comment</comments>
      <pubDate>Mon, 4 Oct 2021 18:31:05 +0900</pubDate>
    </item>
    <item>
      <title>[회고] Github Daily Commit(1일 1커밋)을 시작한지 1년이 지났다..</title>
      <link>https://haranglog.tistory.com/26</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;419&quot; data-origin-height=&quot;346&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/leHNC/btq3UTRbBtr/U1Yvsciy20lwAKu8mk8rU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/leHNC/btq3UTRbBtr/U1Yvsciy20lwAKu8mk8rU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/leHNC/btq3UTRbBtr/U1Yvsciy20lwAKu8mk8rU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FleHNC%2Fbtq3UTRbBtr%2FU1Yvsciy20lwAKu8mk8rU0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;419&quot; data-origin-height=&quot;346&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이 글은 의식의 흐름대로 씁니다.. 참고해주세요.&lt;br&gt;또한, 이 글은 개인적인 회고도 담겨있기 때문에 원하시는 정보가 없을 수도 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  이 글을 쓰는 이유?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;별 이유는 없지만 두 가지 정도가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  첫 번째 이유&lt;/h4&gt;
&lt;p&gt;가장 큰 이유는 1년 동안 무엇을 했고 얼마나 성장했는지에 대해서 자기 회고를 하고 싶어졌다.&lt;br&gt;사실 제대로 된 프론트앤드 개발은 1Day 1Commit을 같이 시작한 1년 전이기 때문에 같이 회고하는 겸 써도 괜찮을 거 같았다.&lt;br&gt;또한, 그로 인해서 자기회고도 할 수 있을 거라 믿고 있다.&lt;/p&gt;
&lt;h4&gt;  두 번째 이유&lt;/h4&gt;
&lt;p&gt;분명히 이 글을 읽으시는 분 중에 Daily Commit(1일 1커밋)을 시도해보았거나 시도하시고 싶은 분들이 분명 있으실 거로 생각한다.&lt;br&gt;그래서 1년간 진행하면서 느낀 점과 장점 또는 단점을 끄적여보려 한다.&lt;br&gt;별 볼 일 없는 프론트앤드 개발 시작한 지 1년 차 밖에는 안됐지만, 시도하고 싶은 분들에게 그 1년간 같이 시작한 Daily Commit의 느꼈던 점을 공유함으로 도움이 되었으면 좋겠다.&lt;/p&gt;
&lt;h2&gt;  1년 동안 무엇을 했을까?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Daily Commit을 1년간 진행해오며 어떤 공부를 해오며 잔디밭을 채워나갔을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  전 아무것도 몰라요&lt;/h3&gt;
&lt;p&gt;Github를 시작한 지도 사실 1년이 전부이고 개발이 재밌어서 눈을 떠 프론트앤드를 공부해야겠다고 마음을 먹은 것도 같은 시점이다. 그러므로 초반에 Github와 Git을 사용할 줄도 몰랐으며 가르쳐줄 사람도 없었다. 또한, 리액트 따위도 이름만 들었지 리액트에 대해서도 무지했다. 그래서 처음에는 혼자 무작정 책을 사서 공부했고, 모르는 건 커뮤니티와 구글 친구와 함께 헤쳐나갔다. 그냥 무작정 공부를 진행해오며 느꼈던 점은 너무 재밌다!! 프론트앤드의 매력에 빠졌다는 표현이 적절할 거 같다.&lt;/p&gt;
&lt;p&gt;그렇게 닥치는 대로 책과 커뮤니티를 참고하며 배우는 것들과 책의 내용을 따라쳐 가며 Github의 잔디밭을 매일매일 채워나갔다.&lt;br&gt;사실 별것 없었다. 코딩테스트를 위한 알고리즘 문제들도 그냥 풀어서 정리하여 올렸고, 책을 읽으며 정리한 내용도 다 MarkDown 형식으로 작성해서 올렸다. 이렇게 한 이유가 뭐냐고 물어본다면 결국 모든 건 보여야 한다고 생각한다. 예를 들어, 내가 자바스크립트 책을 읽었다. 그리고 아무것도 없는 상태에서 &amp;quot;내가 자바스크립트 책을 한 권 읽었어!&amp;quot;라고 얘기하는 거보단 간단한 책을 읽고 회고를 작성하는 등 증거가 있어야 더 보기 좋지 않을까 생각한다. 그리고 정리를 해서 Github에 올린다든가 정리를 해놓으면 필요한 정보를 책에서 찾는 거 보다 훨씬 찾기 수월할 것이다. 현재도 작성되어있는 책 내용을 많이 참고하는 편이다. 물론 주관적인 생각이다. 그리고 나는 다행히 꾸미고 정리하며 채워나가는 방식에 성취감을 얻고 즐거움을 느낀다.&lt;/p&gt;
&lt;h3&gt;  프론트앤드 개발자가 되기 위한 방법?&lt;/h3&gt;
&lt;p&gt;프론트앤드 개발자고 되고 싶다는 목표가 확고해서 블로그와 같은 커뮤니티와 Github는 빠르게 정보를 얻기 위해 매일 같이 봤던 거 같다.&lt;br&gt;이때 &lt;em&gt;그냥 이렇게 공부하면 되는 건가?&lt;/em&gt; 라는 생각이 들었다. 이때, 딱 나한테 좋은 세미나를 찾게 되었다. 지금 생각해보면 어찌 보면 정말 당연한 것들이었지만, 이 시기에 아주 좋은 온라인 세미나였었다. 우아한 형제들에서 주최한 온라인 세미나였는데 주제가 &lt;a href=&quot;https://haranglog.tistory.com/9&quot;&gt;FrontEnd 개발자가 되기 위한 10가지 방법&lt;/a&gt; 이였고 당연히 신청해서 들었다.&lt;br&gt;세미나를 들으면서 느꼈던 점은 그래도 이제 좀 할 줄 안다고 생각했던 나 자신이 한심해졌다는 점과 배워야 할 것들이 산더미구나라는 점이었다.&lt;br&gt;그리고 프론트앤드 로드맵을 보자마자 모르는 것이 너무 많다는 것과 충격 그 자체.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/kamranahmedse/developer-roadmap#frontend-roadmap&quot;&gt;2021 프론트앤드 로드맵 (영어)&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/devJang/developer-roadmap#%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%A1%9C%EB%93%9C%EB%A7%B5&quot;&gt;2020 프론트앤드 로드맵 (한글)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그렇게 세미나를 듣고 중요한 것들을 따라서 공부하려고 했고, 많이 부족했지만, 책을 읽고 배운 것들을 바탕으로 사이드 프로젝트 만들면서 Github 잔디를 채워나갔다. 이때 사실 commit을 어떻게 나눠야 하는지도 몰랐고 commit 컨벤션 같은 것도 몰랐다. 그냥 하루에 공부를 다 하고 전체를 한꺼번에 올렸었다. 또한, 그 사이드 프로젝트도 지금 생각해본 정말 형편없이 구현에만 의존한 개발이었는데 긍정적으로 생각해보면 그만큼 성장한 게 아닌가 싶다.&lt;/p&gt;
&lt;h3&gt;  우아한 테크러닝 3기&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/saseungmin/woowa_tech_learning_react_typescript&quot;&gt;우아한 테크러닝 3기&lt;/a&gt;는 우아한 형제들에서 주최한 교육으로 4주간 진행되었다. 원래라면 오프라인으로 진행되어야 했지만, 코로나로 인해서 운이 좋게 선발되어서 김민태 님의 좋은 강의를 들었다.&lt;br&gt;이 강의는 기본적으로 React와 TypeScript 기반의 웹앱 개발 교육이었지만, 주제를 벗어나 JavaScript의 내부 원리와 기본적인 중요한 내용도 많이 설명해주셨고 질문도 정말 많이 있었는데 그 질문들을 더 일찍 시작해서 답해주셨고, 심지어 시간이 넘어서까지도 모두 다 답해주셨다.&lt;br&gt;그리고 이 강의에서 가장 좋았던 점은 리액트와 리액트의 상태관리 라이브러리인 리덕스를 직접 JavaScript로 구현해보는 것이었다. 여태까지 별 고민 안 하고 단순히 &lt;strong&gt;쓰는 방법&lt;/strong&gt;만 알고 있다고 쓴다고 생각했던 나에게 매우 좋았던 설명이었다. 리액트는 그냥 단순히 개발을 편하게 해주는 라이브러리에 불과할 뿐…. 단순히 사용법만 알면 다 사용할 수 있다는 생각이 들었고 결국 리액트라는 도구가 중요한 게 아니었다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://haranglog.tistory.com/category/%EC%9A%B0%EC%95%84%ED%95%9C%20%ED%85%8C%ED%81%AC%EB%9F%AC%EB%8B%9D%203%EA%B8%B0&quot;&gt;우아한 테크러닝 3기 강의 내용을 정리&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;  코드숨 리액트 2기&lt;/h3&gt;
&lt;p&gt;우아한 테크러닝을 진행하면서 같은 시기에 수강하게 된 &lt;a href=&quot;https://www.codesoom.com/&quot;&gt;코드숨&lt;/a&gt; 리액트 2기를 신청했다. 코드숨은 3개월 동안 진행되는 온라인 코딩부트캠프로 다른 부트캠프와 차별화된 점이 있다면 코드리뷰와 TDD를 배울 수 있다는 것이다. 가격은 부담스러울 수는 있지만 정말 만족스러운 부트캠프였다.&lt;br&gt;신청하기 전까지 TDD의 T자 아니, 테스트의 테자도 몰랐던 나에게 지금은 TDD와 테스트 없이는 개발은 안 하려고 한다. 마치 TDD의 예찬론자가 된 거처럼 만족스러웠고 모두 실력 있는 트레이너분들이라 코드리뷰를 통해 3개월이라는 단 시간 동안 정말 많이 성장했다고 생각한다.&lt;br&gt;성과 아닌 성과라고 한다면 마지막 한 달은 개인 프로젝트로 진행되는데 현재까지 테스트 300개 정도와 Code Coverage 99.6%를 유지하고 있다.&lt;br&gt;원래 부트캠프에 대한 안 좋은 선입견이 있었지만, 정말 만족스러웠고 코드숨을 진행하면서 느꼈던 점은 혼자 공부하는 거보다 배우는 속도가 차원이 다르고 내가 절대 혼자서 알 수 없는 인사이트를 알 수 있다는 점을 몸소 느꼈다.&lt;br&gt;코드숨에 대한 자세한 내용은 아래 링크 참고….&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://haranglog.tistory.com/23?category=943668&quot;&gt;코드숨 리액트 2기 후기&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/CodeSoom/ConStu&quot;&gt;개인 프로젝트&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;  그 외에 잔디 대상자들..&lt;/h3&gt;
&lt;h4&gt;Recoil ToDo 프로젝트&lt;/h4&gt;
&lt;p&gt;리액트의 새로운 상태관리 라이브러리인 Recoil을 배워보고 싶어서 간단하게 진행하려 했다가 조금은 커진? To Do 리스트 프로젝트이다.&lt;br&gt;처음 이 프로젝트에서 Recoil을 사용했을 때 기본적인 사용법은 공식문서가 굉장히 잘 되어있어서 거의 공식문서를 참고했지만, 어떻게 프로젝트를 구조를 가져가야 할지에 대한 참고 글이 너무 없어서 너무 많이 헤맸다. 아직도 여전히 맞는 구조인지는 전혀 모르겠다.&lt;br&gt;최근에 velopert 님의 &lt;a href=&quot;https://github.com/velopert/velofolio&quot;&gt;velofolio&lt;/a&gt;가 공개되었는데 이 프로젝트가 Recoil로 사용되어 있어서 이걸 참고하면 될 거 같다.&lt;/p&gt;
&lt;p&gt;이 프로젝트도 TDD를 적용하며 테스트를 작성했는데 Recoil 테스트는 또 처음이라 제대로 TDD가 적용되었다고 할 수 없었고, 테스트 작성하는데도 비용이 많이 들었다. 또한, Recoil을 사용하여 API 호출도 해보고 싶었기에 백앤드도 테스트를 작성하며 직접 구현하였다. 여기서 테스트 작성하는데 비용이 많이 들면 왜 하냐고 하겠지만, 현재 테스트 작성하는데 들이는 비용은 당연한 훈련이라고 생각한다. 이렇게 계속 TDD의 훈련을 반복하다 보면 테스트를 작성하지 않고 기술부채가 계속 쌓여 지연되는 거보단 더 빠르게 개발할 수 있고 더 좋은 결과물을 얻을 수 있을 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/saseungmin/Recoil_ToDo&quot;&gt;Recoil_ToDo_Frontend&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/saseungmin/Recoil_Todo_Backend&quot;&gt;Recoil_ToDo_Backend&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;책을 읽고 정리..&lt;/h4&gt;
&lt;p&gt;이 Repository는 전공 서적 책들을 읽고 정리하며 스터디를 통해 책을 읽는 것도 포함되어 있다.&lt;br&gt;장점이자 단점이 될 수 있는 방식이라 생각하는데 책을 읽으며 정리하는 건 그냥 읽는 거보다 시간이 오래 걸린다. 장점은 보통 책을 다시 읽기는 힘들고 다시 찾기도 힘들지만 정리해놓으면 지나가면서 계속 보게 되는 장점이 있고 한 권 한 권 읽을 때마다 쌓여가는 성취감도 있다. 하지만 정리하는 게 귀찮고 빠르게 책을 읽고 싶은 분들에겐 추천은 하지 않는다.&lt;br&gt;어쨌든 책을 읽읍시다! 책에는 구글에서 찾고 읽기 힘든 알지 못했던 자세한 내용이 많다는 걸 다시 한 번 깨닫게 되었다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/saseungmin/reading_books_record_repository&quot;&gt;책을 읽고 정리&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;TIL(일일 회고)&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/saseungmin/codesoom_code_review_retrospective&quot;&gt;코드숨에서 회고&lt;/a&gt; 의 장점을 느낀 뒤 올해 다짐 중 하나였던 일일 회고를 작성하게 되었다. 처음 목적은 TIL으로 오늘 배우거나 새롭게 알게 된 것들만을 작성하려고 했지만, 추가로 일일 회고까지 작성하게 되었다. 올해 1월 초부터 시작하여 5월 초인 현재까지 지속하여오고 있다. 중간에 한 7번 정도 빼먹긴 했지만 나름 준수하게 잘 지켜나가고 있다고 생각한다.&lt;br&gt;현재 작성하고 있는 형식은 간단하게 어제 정해놓은 계획들을 오늘 잘 실천했는가를 체크하고 배운 점과 알게 된 점을 작성하고 하루 회고를 작성한 뒤 내일 계획을 작성하는 순으로 작성하고 있다.&lt;/p&gt;
&lt;p&gt;솔직히 매일 회고 작성하는 것이 어렸을 때 일기 작성하는 것처럼 쉽지만은 않다. 하지만 일일 회고를 작성하면서 하루를 다시 돌아볼 수 있는 계기가 되고 자기반성을 할 수 있으며, 그 반성을 통해 고쳐나갈 수 있는 지름길이라고 생각한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/saseungmin/TIL&quot;&gt;TIL(일일 회고)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  1년 간 진행해오며 느낀 1Day 1Commit의 장점과 단점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;1년 간 진행해오면서 느낀 &lt;strong&gt;주관적인&lt;/strong&gt; 생각입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  장점&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;꾸준함을 유지할 수 있다&lt;/strong&gt;.&lt;br&gt;개발자로서 그 꾸준함을 나타낼 수 있는 지표는 Github의 commit 상태뿐이라고 생각이 든다.&lt;br&gt;보이지 않고서야 누가 알아줄까? 보이는 지표로는 최고라고 생각한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;포트폴리오가 자연스럽게 만들어진다.&lt;/strong&gt;&lt;br&gt;개발자로 취업과 이직을 위해서 Github Link는 선택 사항이라고는 하지만 사실상 이제는 이력서에 거의 필수 사항이 되었다.&lt;br&gt;그만큼 Github 관리는 매우 중요하다고 생각이 든다. Daily Commit을 하며 하나하나 쌓아나가면 자연스럽게 포트폴리오가 완성될 수 있으며 좋은 인상을 심어줄 수 있다고 생각한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;성취감을 얻을 수 있다.&lt;/strong&gt;&lt;br&gt;자신과의 약속을 지킨다는 다짐으로 시작하면 하루 이틀 계속 시간이 지날수록 초록색으로 변할 잔디를 보면서 &lt;em&gt;내가 이렇게 열심히 하고 있구나!&lt;/em&gt; 라는 성취감을 얻을 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;  단점&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;커밋을 해야 한다는 압박감에 시달릴 수 있다.&lt;/strong&gt;&lt;br&gt;이게 제일 큰 단점이라고 생각이 든다. 지금은 당연하듯 해야 할 공부가 많기도 하고 배우는 게 즐거워 신경도 안 쓰고 하고 있지만, 처음에 시작할 때 하루에 커밋을 해야 한다는 압박감을 느낄 수밖에 없었다.&lt;br&gt;예를 들어서 오늘 커밋을 아직 안 한 상태에서 23:50분이 되었다. &lt;em&gt;오늘 00:00 시가 지나가면 잔디가 잘려버려!&lt;/em&gt; 라는 생각을 가질 수밖에 없게 되고 그로 인해 의미 없는 커밋을 할 수밖에 없어진다. 이 말이 즉, 단점 2번에 해당하는 말이다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;커밋이 전부가 아니다.&lt;/strong&gt;&lt;br&gt;아무거나 커밋을 한다고 그 커밋이 제대로 된 커밋일까? 예를 들어 하나의 Repository에 Daily Commit이 잘리기를 보기 싫어서 띄어쓰기 한 칸만 하고 커밋하여 유지하는 게 과연 당당하게 오늘 공부해서 만든 성취감 있는 진실한 커밋이라 할 수 있을까?&lt;br&gt;결국, 2번 단점도 단점 1번에 의해 생긴 단점이다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;악용될 우려가 존재한다&lt;/strong&gt;&lt;br&gt;사실 Github의 커밋 상태는 조작할 수 있다. Github의 커밋 상태라기보단 커밋의 시간을 조작할 수 있다는 게 맞겠다.&lt;br&gt;커밋의 시간을 조작하여 이전 시간의 커밋으로 변경해 잔디를 채울 수 있다. 다행히도 아직 조작해서 커밋을 채운 적은 없다. 다만, 편법은 쓴 적은 있다. 예를 들어 다음날에는 못할 거 같다고 생각하면 그 전날에 00시까지 넘어서 공부하고 커밋을 하는 방법을 사용하였다. 어떻게 보면 00시가 지났으니 당연할 수 있지만, 양심에는 찔린다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;  마무리하며..&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;400&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/co8gj4/btq3S0qtbrC/Aeu4l3oXHub9f6yw197Gtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/co8gj4/btq3S0qtbrC/Aeu4l3oXHub9f6yw197Gtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/co8gj4/btq3S0qtbrC/Aeu4l3oXHub9f6yw197Gtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fco8gj4%2Fbtq3S0qtbrC%2FAeu4l3oXHub9f6yw197Gtk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;400&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;1년간 진행해오면서 회고를 써야겠다 다짐만 하다가 쓰는데 이 글을 읽고 조금이라도 도움이 되셨으면 좋겠다.&lt;br&gt;Daily Commit을 진행해오면서 다행히도 1년 동안 아프거나 큰일은 없었다. 그래서 이렇게 유지할 수 있던 게 아니었나 싶다. 앞으로도 2년, 3년 상황이 가능하고 몸이 가능할 때까지 계속해서 유지해볼 생각이다. 이제는 그냥 일상이고 습관이라고 생각하며 하니까 별로 어렵지도 않고 오히려 기분이 좋아진다.&lt;br&gt;그리고 Daily Commit을 할 때 한가지 주의할 점은 &lt;strong&gt;필요 없는 repository라도 삭제&lt;/strong&gt;하면 큰일 난다. repository를 삭제하면 commit history도 없어지기 때문에 잔디가 사라지게 된다.&lt;br&gt;Daily Commit에 대해 궁금하다면 자세한 내용은 이미 유명하신 개발자분들이 작성해놓은 블로그들이 많이 있다. 아래에 첨부를 꼭! 참고 바란다.&lt;/p&gt;
&lt;p&gt;마지막으로 Daily Commit을 할지 고민하고 계신 분들에게 하고 싶은 말은 일단 주저하지 말고 해보시는 것을 추천해 드린다. 눈에 보이는 지표를 보면서 많은 성취감을 얻을 수 있을 것이고 덩달아 성장하고 있다는 걸 느낄 것이다. 물론, 매일 한다는 게 중요하진 않다. 꾸준히 의미 있는 무언가를 했다는 게 더 중요하다고 생각한다. 그렇기에 그 꾸준함을 유지할 수 있는 도구라 생각한다. 리액트도 단순히 프론트앤드 개발을 하기 위한 도구처럼 말이다. 그러므로 매일 해야 한다는 압박감을 가지고 시작하기보단 끊겨도 좋으니까 꾸준함을 유지한다는 생각으로 하면 좋을 거 같다. 또한, 진행하면서 계속 유지할 수 있게 만들어주는 건 자신과의 약속이라고 생각하면 좋다. 늘 그랬듯이 남과의 약속이 아닌 나 자신과 약속을 하고 자신이 정한 목표를 이루고 쌓아간다는 생각으로 하면 충분히 누구나 잘할 수 있다고 생각한다.&lt;/p&gt;
&lt;p&gt;저도 했는데 누군 못하겠습니까?&lt;br&gt;끝! 긴 글 읽어주셔서 감사합니다!  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://jojoldu.tistory.com/402&quot;&gt;이동욱님의 일일 커밋의 효용성 블로그 글&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.outsider.ne.kr/1141&quot;&gt;Outsider님의 일일 커밋 블로그 글&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://milooy.wordpress.com/2016/07/02/daily-commit-1-year/&quot;&gt;진유림님의 일일 커밋 회고 블로그 글&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>회고</category>
      <category>1Day 1Commit</category>
      <category>1일 1commit</category>
      <category>GitHub</category>
      <category>retrospect</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/26</guid>
      <comments>https://haranglog.tistory.com/26#entry26comment</comments>
      <pubDate>Mon, 3 May 2021 18:29:59 +0900</pubDate>
    </item>
    <item>
      <title>  Firebase API Key를 공개하는 것이 안전합니까?</title>
      <link>https://haranglog.tistory.com/25</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsK2ma/btqSXi18sr4/mcOY5p9X1kjoXrO6farQM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsK2ma/btqSXi18sr4/mcOY5p9X1kjoXrO6farQM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsK2ma/btqSXi18sr4/mcOY5p9X1kjoXrO6farQM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsK2ma%2FbtqSXi18sr4%2FmcOY5p9X1kjoXrO6farQM0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;  고민하게 된 계기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;스터디 모집 &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin&quot;&gt;개인 프로젝트&lt;/a&gt;를 진행하면서 백앤드로 &lt;code&gt;firebase&lt;/code&gt;를 사용하게 되었습니다.&lt;/li&gt;
&lt;li&gt;사용하면서 API Key이니까 당연히 &lt;code&gt;.env&lt;/code&gt; 파일로 관리해야지라고 생각한 뒤 관리를 하게 되었고, 그 후 Ci/Cd를 위해 Github-Action 사용하고 있었고 CI를 진행하면서 &lt;code&gt;.env&lt;/code&gt;파일로 인해 테스트가 깨지는 현상이 발생하게 되었습니다.&lt;br&gt;테스트를 할 때 apikey를 사용하는 부분을 mocking해서 할려고 했었고 고민을 해보았지만 쉽게 진행되지 않았습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;FIREBASE_API_KEY= &amp;quot;your api-key&amp;quot;
FIREBASE_AUTH_DOMAIN = &amp;quot;your auth-domain&amp;quot;
FIREBASE_DATA_BASEURL = &amp;quot;your data-baseurl&amp;quot;
FIREBASE_PROJECT_ID = &amp;quot;your project-id&amp;quot;
FIREBASE_STORAGE_BUCKET = &amp;quot;your storage-bucket&amp;quot;
FIREBASE_MESSAGING_SENDER_ID = &amp;quot;your messaging-sender-id&amp;quot;
FIREBASE_APP_ID = &amp;quot;your app-id&amp;quot;
FIREBASE_MEASUREMENT_ID = &amp;quot;your measurement-id&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;그렇다가 우연치않게 &lt;a href=&quot;https://stackoverflow.com/questions/37482366/is-it-safe-to-expose-firebase-apikey-to-the-public&quot;&gt;Is it safe to expose Firebase apiKey to the public?&lt;/a&gt;라는 제목의 주제로 api 키를 공개하는 것이 안전한지에 대한 스택 오버플로우의 질문이 눈에 들어오게 되었습니다.&lt;br&gt;그리고 이 부분에 대해서 계속 고민하게 되었고, 답은 저 글에 명확히 나와있지만 API Key라는 점이 걸려 제가 알게된 사실을 적어 공유해볼까 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  Is it safe to expose Firebase apiKey to the public?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;고민의 발단이 된 &lt;a href=&quot;https://stackoverflow.com/questions/37482366/is-it-safe-to-expose-firebase-apikey-to-the-public&quot;&gt;스택오버플로우의 질문&lt;/a&gt;는 제목 그대로 firebase apikey를 공개를 하는 것이 안전한지에 대해서 물어본 것이였습니다.&lt;br&gt;그 질문의 답변은 Google의 Firebase 엔지니어분이 달아주셨는데 그 답은 다음과 같았습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;apiKey는 Google 서버에서 Firebase 프로젝트만 식별합니다. 누군가가 그것을 아는 것은 보안 위험이 아닙니다. 사실, 그들이 당신의 Firebase 프로젝트와 상호 작용하기 위해서는 그들이 그것을 알아야합니다. 이 동일한 구성 데이터는 Firebase를 백엔드로 사용하는 모든 iOS 및 Android 앱에도 포함됩니다.&lt;/li&gt;
&lt;li&gt;Firebase 백엔드 서비스에 대한 모든 데이터 액세스를 승인하는 방법을 알아 보려면 Firebase 보안 규칙에 대한 문서를 읽어 보세요. 이러한 규칙은 파일 저장소 및 데이터베이스 액세스에 대한 액세스를 제어하며 Firebase 서버에 적용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;이러한 답변으로 결론은 &lt;strong&gt;Firebase의 API-Key는 Google 서버에서 firebase 프로젝트만 식별하기 때문에 보안에 문제가 되지 않는다&lt;/strong&gt;는 명확한 답변이였습니다. 하지만, 그 질문의 글은 무려 5년 전 질문이었고 API-key에 대한 의심은 있어서 더 찾아보게 되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  Firebase 공식 문서&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Firebase의 공식 문서에서 &lt;a href=&quot;https://firebase.google.com/docs/projects/api-keys#apply-restrictions&quot;&gt;Firebase 용 API 키 사용 및 관리에 대해 알아보기&lt;/a&gt;라는 제목으로 Api key의 관리 방법이 자세히 설명되어있습니다.&lt;/li&gt;
&lt;li&gt;여기서도 아래와 같이 Firebase용 API키는 일반적인 API키와 다르기 때문에 &lt;strong&gt;API키가 일반적으로 사용되는 방식과 달리 Firebase 서비스용 API키는 백엔드 리소스에 대한 액세스를 제어하는 데 사용 되지 않습니다.&lt;/strong&gt; 라는 내용이 적혀있었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;343&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TNB5f/btqS1WDM2BC/mUwVoG4mtDz067sct35q1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TNB5f/btqS1WDM2BC/mUwVoG4mtDz067sct35q1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TNB5f/btqS1WDM2BC/mUwVoG4mtDz067sct35q1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTNB5f%2FbtqS1WDM2BC%2FmUwVoG4mtDz067sct35q1K%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;343&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;하지만 제가 진행하고 있던 개인 프로젝트는 &lt;strong&gt;이메일/비밀번호 로그인 방법으로 Firebase 인증을 사용하는 경우&lt;/strong&gt;에 해당하였기 때문에 API키에 제한을 적용해야하는 구체적인 경우에 해당하게 되었습니다.&lt;/li&gt;
&lt;li&gt;또한, 다른 구체적인 경우는 &lt;a href=&quot;https://firebase.google.com/docs/projects/api-keys&quot;&gt;공식 문서&lt;/a&gt; 참고하시길 바랍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  Firebase 보안 규칙&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;공식 문서의 내용에 따라 &lt;a href=&quot;https://firebase.google.com/docs/projects/api-keys#apply-restrictions-password-based-auth&quot;&gt;암호 기반 인증&lt;/a&gt;을 사용할 때 보호하는 방법인 &lt;a href=&quot;https://firebase.google.com/docs/rules&quot;&gt;firebase 보안 규칙&lt;/a&gt;을 사용할 수 있습니다.&lt;br&gt;보안 규칙을 잘 적용하면 &lt;strong&gt;보안 규칙에 의해 보호되는 한 프로젝트의 데이터베이스 또는 저장소 데이터게 엑세스 할 수 없게 됩니다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;공식 문서의 보안 규칙에 굉장히 자세히 설명이 되어있으므로 참고하시기 바랍니다. 특히 &lt;a href=&quot;https://firebase.google.com/docs/rules/insecure-rules&quot;&gt;안전하지 않은 규칙 차단&lt;/a&gt;을 참고하시면 좋을 거 같습니다.&lt;/li&gt;
&lt;li&gt;만약 &lt;code&gt;firestore&lt;/code&gt; 의 다음과 같은 규칙을 작성 후 적용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;rules_version = &amp;#39;2&amp;#39;;
service cloud.firestore {
  match /databases/{database}/documents {
    match /groups/{group} {
      allow read: if true // 읽기 모두 허용
      // 쓰기: 콘텐츠 소유자 전용
      allow write: if request.auth != null &amp;amp;&amp;amp; request.auth.uid == request.resource.data.author_uid 
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;본인이 작성한 것이 아닌 다른 사람이 작성한 것을 수정할려고 할 경우 다음과 같이 권한이 없어 거부되는 에러를 발생하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H0aPj/btqSPFqC3wq/eWNFhKsWCUb80QgX6trKGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H0aPj/btqSPFqC3wq/eWNFhKsWCUb80QgX6trKGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H0aPj/btqSPFqC3wq/eWNFhKsWCUb80QgX6trKGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH0aPj%2FbtqSPFqC3wq%2FeWNFhKsWCUb80QgX6trKGk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;다음은 인증된 사용자만 쓰기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;rules_version = &amp;#39;2&amp;#39;;
service cloud.firestore {
  match /databases/{database}/documents {
    match /groups/{group} {
      allow read: if true; // 읽기 모두 가능
      allow write: if request.auth.uid != null; // 쓰기 인증된 사용자만
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;추가적인 규칙은 공식 문서 참고 하시길 바랍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  다른 웹 사이트에서 Firebase 프로젝트에 생성, 수정 등을 못하게 하는 방법&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;firebase를 사용하여 배포를 하게되고 내 프로젝트의 공개된 API-key를 사용해서 수정할 수 있는 여지가 생기게 됩니다.&lt;/li&gt;
&lt;li&gt;예를 들어서 Github에 공개된 API-key를 사용해서 fork 후 로컬에서 프로젝트를 사용할 수 있게 됩니다. (localhost) 그렇게 되면 해당 구성 파일을 사용하여 새 사용자를 만들 수 있게 되고, 로컬에서 글을 작성, 수정 등을 할 수게 있게 됩니다.&lt;/li&gt;
&lt;li&gt;이에 대한 해결 방법은 다음 &lt;a href=&quot;https://medium.com/@devesu/how-to-secure-your-firebase-project-even-when-your-api-key-is-publicly-available-a462a2a58843&quot;&gt;블로그&lt;/a&gt;에 자세히 나와있고, 이 부분은 firebase 공식 문서의 &lt;a href=&quot;https://firebase.google.com/support/guides/launch-checklist&quot;&gt;출시 체크리스트&lt;/a&gt; 중 하나의 &lt;strong&gt;도메인 허용 목록을 추가하여 무단 사용을 방지합니다.&lt;/strong&gt; 라고 소개되어있습니다. 또한, 구글의 firebase 엔지니어 Frank van Puffelen 께서 피싱 공격에 대해 이 방법을 언급하셨습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://console.cloud.google.com/apis&quot;&gt;Google Cloud Platform API 및 서비스&lt;/a&gt;에 접속하여 해당 프로젝트를 선택 후 사용자 인증 정보 탭으로 이동합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P9ccm/btqS1V565Is/Y4O5HQ5KjpRaWkMDdH2DSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P9ccm/btqS1V565Is/Y4O5HQ5KjpRaWkMDdH2DSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P9ccm/btqS1V565Is/Y4O5HQ5KjpRaWkMDdH2DSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP9ccm%2FbtqS1V565Is%2FY4O5HQ5KjpRaWkMDdH2DSK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;사용자 인증 정보 탭의 API 키 부분의 &lt;strong&gt;Browser Key (auto created by Firebase)&lt;/strong&gt; 를 선택합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKX0BP/btqSScO71qw/fKxh8T8WOuuiNcBIKAoB7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKX0BP/btqSScO71qw/fKxh8T8WOuuiNcBIKAoB7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKX0BP/btqSScO71qw/fKxh8T8WOuuiNcBIKAoB7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKX0BP%2FbtqSScO71qw%2FfKxh8T8WOuuiNcBIKAoB7k%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;선택 후 &lt;strong&gt;API키 제한 및 이름 변경&lt;/strong&gt; 페이지로 이동하게 되는데 그 부분에서 애플리케이션 제한 사항을 &lt;strong&gt;HTTP 리퍼러(웹사이트)&lt;/strong&gt; 를 선택해줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CyWwM/btqS1VLPGuu/PFqMEFS6R7KA9zD03rEGD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CyWwM/btqS1VLPGuu/PFqMEFS6R7KA9zD03rEGD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CyWwM/btqS1VLPGuu/PFqMEFS6R7KA9zD03rEGD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCyWwM%2FbtqS1VLPGuu%2FPFqMEFS6R7KA9zD03rEGD1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;선택하면 아래에 웹사이트 제한사항 항목이 나타나게 되는데 그 항목에 배포되어 있는 해당 프로젝트 URL를 입력 후 저장을 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz54fK/btqS1UTF9ot/MKljUx4f8MTSmFP2SOopaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz54fK/btqS1UTF9ot/MKljUx4f8MTSmFP2SOopaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz54fK/btqS1UTF9ot/MKljUx4f8MTSmFP2SOopaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz54fK%2FbtqS1UTF9ot%2FMKljUx4f8MTSmFP2SOopaK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이렇게 모든 작업을 완료하면 다른 사이트에서 API를 사용할 수 없게 됩니다. 하지만 이렇게 하면 &lt;strong&gt;로컬에서 사용할 수 없게 됩니다.&lt;/strong&gt;&lt;br&gt;로컬에서 해당 API를 가지고 로그인을 시도해보았을 때 다음과 같이 403 에러가 나타나게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JWMJM/btqSV9xxu31/ekELhcIwcaKJP3Kqkf7im1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JWMJM/btqSV9xxu31/ekELhcIwcaKJP3Kqkf7im1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JWMJM/btqSV9xxu31/ekELhcIwcaKJP3Kqkf7im1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJWMJM%2FbtqSV9xxu31%2FekELhcIwcaKJP3Kqkf7im1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;로컬에서 테스트 및 개발을 못하기 때문에 이를 해결하기 위해서는 새로운 &lt;strong&gt;firebase 프로젝트를 사용&lt;/strong&gt;해야 합니다. 개발 및 테스트만을 위한 firebase 프로젝트를 생성하고 새롭게 만든 firebase 프로젝트의 환경 변수를 사용해서 테스트를 진행할 수 있습니다.&lt;br&gt;아래와 같이 개발 환경에 따라 환경 변수를 설정해줄 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import firebase from &amp;#39;firebase/app&amp;#39;;

import &amp;#39;firebase/auth&amp;#39;;
import &amp;#39;firebase/firestore&amp;#39;;

import devConfig from &amp;#39;../../config/dev&amp;#39;; // 개발 환경 설정
import prodConfig from &amp;#39;../../config/prod&amp;#39;; // 배포 환경 설정

const config = process.env.NODE_ENV === &amp;#39;production&amp;#39;
  ? prodConfig
  : devConfig;

firebase.initializeApp(config);

const fireStore = firebase.firestore;

export default fireStore;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;  하지만, 이렇게 설정하는거에 대해서 &lt;a href=&quot;https://medium.com/@skreweverything/you-do-realize-that-http-referrers-can-be-spoofed-very-easily-right-d1ca79a5bc04&quot;&gt;반박&lt;/a&gt;도 존재했습니다.&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;HTTP 리퍼러가 매우 쉽게 스푸핑 될 수 있다는 점&lt;/strong&gt;으로 결국 이렇게 하는 건 소용이 없다는 점이였고, api key를 노출하거나 노출하지 않는 것은 중요하지 않고 이 방법은 실제로 이러한 구성 정보는 공개 된 것으로 간주된다는 말이였습니다.&lt;/p&gt;
&lt;h2&gt;  결론&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이런 저런 내용을 찾아보았지만 결국 결론은 &lt;strong&gt;firebase api키는 공개가 되도 상관이 없다는 점&lt;/strong&gt;입니다. 하지만, 권한을 주어 더 안전하게 관리할 수 있다는 점입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.env&lt;/code&gt;로 api-key를 숨기더라도 위와 같이 권한이 거부될 때도 결국 api키의 정보가 노출됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JWMJM/btqSV9xxu31/ekELhcIwcaKJP3Kqkf7im1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JWMJM/btqSV9xxu31/ekELhcIwcaKJP3Kqkf7im1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JWMJM/btqSV9xxu31/ekELhcIwcaKJP3Kqkf7im1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJWMJM%2FbtqSV9xxu31%2FekELhcIwcaKJP3Kqkf7im1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API는 Firebals/Google 서비스와 상호 작용할 때 Firebase 프로젝트를 식별하는 데 사용합니다. &lt;a href=&quot;https://firebase.google.com/docs/projects/api-keys#use-api-keys&quot;&gt;API 키 사용&lt;/a&gt;에 대한 공식 문서를 참고해주시면 됩니다.&lt;/li&gt;
&lt;li&gt;Google의 Firebase JS 모바일 개발자인 Jorge Vergara분도 메일로 &lt;a href=&quot;https://jsmobiledev.com/article/hide-firebase-api&quot;&gt;FIrebase API 키를 숨겨야하느냐&lt;/a&gt;에 대한 메일이 한달에 3 ~ 4번이 온다고 합니다. 이 글에서 핵심은 결국 &lt;strong&gt;공개되도 문제가 되지 않는다&lt;/strong&gt;는 점이였고, 아래와 같이 쓰신 글을 보고 확신을 가질 수 있게되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;apiKey를 안다는 것은 사람들이 당신의 모든 데이터를 즉시 해킹 할 수 있다는 것을 의미하는 것이 아니라 그들이 당신의 앱/프로젝트에 연결할 수 있다는 것을 의미합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;이 답변을 듣고 명확하게 이해할 수 있었습니다. 그렇기 때문에 연결에 대한 부분을 위와 같이 해결해주면 문제는 없다는 결론을 내렸습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  참고 링크&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/issues/133&quot;&gt;개인 프로젝트의 Firebase에 대한 참고 사항 이슈&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@devesu/how-to-secure-your-firebase-project-even-when-your-api-key-is-publicly-available-a462a2a58843&quot;&gt;How to secure your Firebase project even when your API key is publicly available&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jsmobiledev.com/article/hide-firebase-api&quot;&gt;Do you need to hide your Firebase API keys for Ionic apps?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://howtofirebase.com/firebase-security-rules-88d94606ce4a&quot;&gt;Firebase Security &amp;amp; Rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://firebase.google.com/docs/projects/api-keys&quot;&gt;Firebase 용 API 키 사용 및 관리에 대해 알아보기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/35418143/how-to-restrict-firebase-data-modification&quot;&gt;How to restrict Firebase data modification?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/37482366/is-it-safe-to-expose-firebase-apikey-to-the-public&quot;&gt;Is it safe to expose Firebase apiKey to the public?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://firebase.google.com/docs/rules&quot;&gt;Firebase 보안 규칙&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@skreweverything/you-do-realize-that-http-referrers-can-be-spoofed-very-easily-right-d1ca79a5bc04&quot;&gt;You do realize that HTTP referrers can be spoofed very easily right?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Firebase</category>
      <category>api-key</category>
      <category>Firebase</category>
      <category>firebase apikey</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/25</guid>
      <comments>https://haranglog.tistory.com/25#entry25comment</comments>
      <pubDate>Fri, 8 Jan 2021 15:33:14 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 2020년을 되돌아보며..  </title>
      <link>https://haranglog.tistory.com/24</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHFA7X/btqRXIBuWMe/iIrYoFDp5qjmBMXrO4NN9k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHFA7X/btqRXIBuWMe/iIrYoFDp5qjmBMXrO4NN9k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHFA7X/btqRXIBuWMe/iIrYoFDp5qjmBMXrO4NN9k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bHFA7X/btqRXIBuWMe/iIrYoFDp5qjmBMXrO4NN9k/img.gif&quot; width=&quot;100%&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;  2020년 1월 ~ 4월&lt;/h2&gt;
&lt;h3&gt;  키워드&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&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;/blockquote&gt;
&lt;p&gt;올해 학교를 졸업했나 싶을 정도로 오래전 일인 거 같지만 졸업했다. 코로나로 인하여 인생의 마지막인 졸업식이 취소됐다.  &lt;br&gt;학교에 다니며 6년 간(2년 군대) 다니면서 추억도 많았고, 평생 갈 만한 좋은 사람들도 정말 많이 사귀었다.&lt;/p&gt;
&lt;p&gt;컴퓨터 공학과를 졸업했고, 공부도 꽤 했지만, 코딩에 코자도 모를 정도였다. 이론적인 수업들과 컴퓨터 개론, 운영체제부터 시작하여 그래픽스, IOT까지 넓은 범위를 얕게 배우다 보니 뭘 해야 할지도 모르겠고 흥미도 전혀 없었다. 하지만 학교의 그런 수업 방식에 불평 안 하고 공부했다는 점에 그래도 지금 와서 보니 꽤 도움이 되는 부분이 알게 모르게 있는 거 같다.&lt;/p&gt;
&lt;p&gt;학교 다니기 마지막 학기의 중간고사 이후부터 출석인정을 받고 JAVA Spring 국비 교육 신청하여 들었다. 이때 신청하게 된 계기는 딱히 없었고, 취업하기에 급급한 그저 그런 졸업을 앞둔 학생 중 한 명이었다. 사실 코딩하는 게 재밌다고 느꼈던 때는 이때가 처음이었다.&lt;br&gt;재밌다는 생각은 들었지만, 여전히 이게 진짜 내가 평생직장으로 할 수 있을까? 이게 맞는 길일까? 라는 의문점이 있었다.&lt;br&gt;지금 생각하면 웃기지만 어느 정도 뚝딱뚝딱 만들 수 있는 정도가 되었었는데 그게 개발의 전부라고 생각했다. 너무 안일하고 멍청했던 생각이었다.&lt;/p&gt;
&lt;h2&gt;  2020년 5월 ~ 8월&lt;/h2&gt;
&lt;h3&gt;  키워드&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&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;/blockquote&gt;
&lt;p&gt;국비 교육이 끝나고 취업까지 1달의 백수 생활이 있었다. 이 기간이 내 인생의 두 번째 터닝 포인트가 되었다.&lt;br&gt;&lt;strong&gt;하고 싶은 게 생겼다. 내 인생에 있어서 목표가 생겼다. 이 기간은 아마 평생 기억 남을 것이다.&lt;/strong&gt; 기간 동안 어떻게 공부해야 할지와 뭘 해야 할까 등등 커뮤니티와 블로그 등 온종일 컴퓨터를 붙잡고 찾았던 거 같다. 이때 잘하시는 분들과 열정이 넘치는 분들을 보면서 대단하다는 생각과 절망감 또한 같이 들었다. 그렇게 다짐을 하기 시작했다. &lt;strong&gt;프론트 앤드 개발자가 되기로..&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://haranglog.tistory.com/17?category=943668&quot;&gt;프론트 앤드를 선택한 이유&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;다짐한 후 처음 시작한 일은 GitHub에 &lt;strong&gt;1day 1 commit&lt;/strong&gt;을 하는 것이었다. 그냥 공부하는 모든 것들을 기록하기 시작했다. 다만, &lt;strong&gt;절대 하루라도 빼먹지 않기로 마음을 먹었다.&lt;/strong&gt;&lt;br&gt;그리고 블로그를 같이 시작했다. 블로그를 시작하면서 계획을 세웠었는데 계획과는 좀 달라진 부분이 있다. 이래서 계획과 회고를 적는가보다 느꼈다.&lt;/p&gt;
&lt;p&gt;그 후 6월 1일 자로 취업을 했다. 가면 뭔가 배우는 게 있겠지 하고 갔지만, 생각했던 거랑 너무 많이 달랐다. 이 부분은 말을 아끼겠다. 시작하면 너무 길어질 거 같다. 취업한 지 얼마 안 됐지만 &lt;strong&gt;1월 31일 자로 퇴사 예정&lt;/strong&gt;이다. 당연히 하는 게 맞고 지금 당장이라도 하고 싶지만, 마무리는 짓고 나와야 하기 때문에.. 더 기다려야 한다.&lt;/p&gt;
&lt;h2&gt;  2020년 9월 ~ 12월&lt;/h2&gt;
&lt;h3&gt;  키워드&lt;/h3&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;우아한 테크러닝 3기&lt;/li&gt;
&lt;li&gt;코드숨&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;회사에 다니면서 퇴근 후 주말마다 공부를 계속 이어간다는 건 굉장히 힘든 일이었다. 하지만 해냈다고 자부할 수 있다! 이렇게 할 수 있었던 이유는 아마 그래도 개발이 재밌어서 아닐까?  &lt;/p&gt;
&lt;p&gt;9월은 매주 화목으로 우아한 형제들의 김민태 기술이사님이 진행하시는 우아한 테크러닝을 들었다.&lt;br&gt;원래 오프라인으로 진행됐다면 못 들었을 거 같지만, 코로나로 인해서 온라인으로 진행해서 운 좋게 들을 수 있던 거 같다.&lt;br&gt;강의를 들으며 다시 동기부여를 너무 많이 얻었다. 신청하신 분들과 김민태 님의 이야기를 들으며 앞으로 더 많은 노력을 해야겠다는 자극을 많이 받을 수 있었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/saseungmin/woowa_tech_learning_react_typescript&quot;&gt;우아한 테크러닝 강의 내용 정리&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;기본적인 Javascript부터 시작해서 리액트, 리덕스를 직접 만들어보기, 미들웨어 직접 만들어보기 등.. 얻은 것이 너무 많았다. 그냥 다 사용하니까 사용하는 거지라고 생각했던 부분을 직접 만들어보고 진행되는 과정을 보니 이래서 이렇게 되는구나! 라는 깨달음을 얻었고 소름도 같이 돋았고 이 강의 이후로 김민태 님의 열렬한 팬이 되었다는..&lt;/p&gt;
&lt;p&gt;올해의 베스트 어워드! &lt;strong&gt;코드숨&lt;/strong&gt;이다. 개발의 바른길로 이끌어주셨고 이끌어주시고 계신 코드숨 트레이너분들 감사합니다!&lt;br&gt;코드숨에 대한 자세한 내용은 후기에  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://haranglog.tistory.com/23&quot;&gt;코드숨 2기 후기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;코드숨을 다 이야기하려면 끝도 없을 거 같고, 확실한 건 &lt;strong&gt;어떻게&lt;/strong&gt;라는 부분은 확실히 알았다.&lt;br&gt;지금은 많이 부족하고 실력은 형편없지만, 열정 넘치고 아끼지 않으시는 트레이너분들의 길을 따라가다 보면 분명히 좋은 개발자가 될 거 같다는 믿음이 생겼다.&lt;br&gt;또한, 같은 목적을 가지고 달려가고 있는 코드숨 동기분들도 같이 있으니 더 확실해졌다.   너무 다 열심히 하신다..&lt;/p&gt;
&lt;h2&gt;  올해의 마무리&lt;/h2&gt;
&lt;p&gt;올해는 그 어느 때보다 열심히 달렸다. 내가 어쩌다 이렇게 개발에 빠지게 되어 공부를 하고 있다니 불과 작년에만 생각해도 말도 안 되는 일이다.&lt;br&gt;그리고 올해는 어느 때보다 만족하는 건 다 필요 없고 &lt;strong&gt;내가 하고 싶은 걸 찾았다는 것이다.&lt;/strong&gt; 하고 싶을 게 있으니 목표가 생기고 그에 따라 삶에 사는 이유가 생긴 거나 다름이 없는 것이다. 주변 사람들은 하고 싶은 게 있다는 거에 굉장히 부러워한다. 하고 싶은 게 있는 만큼 포기하지 말고 달려나가야겠다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub 1일 1커밋&lt;br&gt;8개월가량 진행했고 1일 1커밋은 여전히 진행 중이다. 내년에도 하루도 빼먹을 계획이 전혀 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;60%&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;416&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ban0jA/btqR1Fj7NKC/TKDa3K0wUTdmmwucjAmx6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ban0jA/btqR1Fj7NKC/TKDa3K0wUTdmmwucjAmx6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ban0jA/btqR1Fj7NKC/TKDa3K0wUTdmmwucjAmx6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fban0jA%2FbtqR1Fj7NKC%2FTKDa3K0wUTdmmwucjAmx6k%2Fimg.png&quot; width=&quot;60%&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;416&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;412&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nWPSw/btqRXHJibO2/xsM1HrMdEASlKnXytV7Ofk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nWPSw/btqRXHJibO2/xsM1HrMdEASlKnXytV7Ofk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nWPSw/btqRXHJibO2/xsM1HrMdEASlKnXytV7Ofk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnWPSw%2FbtqRXHJibO2%2FxsM1HrMdEASlKnXytV7Ofk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;412&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;프론트 앤드 공부를 시작한 지 벌써 8개월이나 됐다. 정말 처음과 비교했을 때 너무 많이 성장한 거 같다. 내년은 올해보다 더 성장하자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/saseungmin/algorithm_study&quot;&gt;알고리즘 공부&lt;/a&gt;&lt;br&gt;알고리즘 공부는 꾸준히 했을까??&lt;br&gt;이건 50%정도 성공인 거 같다.. 좀 아쉽다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;블로그&lt;br&gt;이 부분도 50%정도이다. 그 대신 깃허브에 작성을 많이 하여서 꾸준하게 블로그를 작성하지 않았다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  반가워! 2021!&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;내년의 계획을 크게 세워보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Github 1day 1commit 계속 이어나가기&lt;br&gt;2021년 12월 31일이 되는 그날까지 깃허브 잔디에 빈 칸은 없다!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;회고&lt;br&gt;이번 연도는 회고 작성을 많이 하지 않았다.&lt;br&gt;윤석 님의 말에 따라 회고를 더 많이 작성해볼까 한다.&lt;br&gt;1일 1회고를 목표로 하자.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;퇴사&lt;br&gt;1월 31일 퇴사&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;취준 및 취업&lt;br&gt;새로운 목표를 위해 취준은 필요한 법...&lt;br&gt;취준 기간을 거쳐 새로운 직장의 프론트앤드 개발자로 취업하는게 목표이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;알고리즘 공부&lt;br&gt;이게 사실 취업할 때 제일 중요하다..&lt;br&gt;알고리즘 너무 어렵지만, 해야 한다. 코딩테스트를 통과하려면 무조건 해야 한다.&lt;br&gt;온라인 강의도 신청해서 다시 처음부터 시작해봐야겠다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;지치지말고 꾸준히&lt;br&gt;공부하면서 제일중요!!&lt;br&gt;이게 안 되면 아무것도 할 수 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;마지막 바람..&lt;br&gt;여행가고 싶다.&lt;br&gt;유럽 여행을 한 달 가기위해서 주식으로 열심히 벌어놨더니만..&lt;br&gt;이 놈의 코로나 제발 빨리 없어져라!!  &lt;br&gt;사실 여행을 갈 수 있을지도 모르겠다..  &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;  마지막으로 나에게 하고 싶은 말.&lt;/h2&gt;
&lt;p&gt;2020년도 고생했다. 내년은 더 힘들 거야 ㅋㅋㅋ&lt;br&gt;올해는 참 열심히 달렸어 칭찬해&lt;br&gt;내년은 더 달려서 한층 더 발전하고 성장하자. 승민아&lt;/p&gt;</description>
      <category>회고</category>
      <category>2020년 회고</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/24</guid>
      <comments>https://haranglog.tistory.com/24#entry24comment</comments>
      <pubDate>Fri, 1 Jan 2021 18:33:49 +0900</pubDate>
    </item>
    <item>
      <title>코드숨 2기 그 3달간의 여정!</title>
      <link>https://haranglog.tistory.com/23</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;200&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRFe5G/btqQZw8FPB4/PuSBQpEEn02ONKEif2jbK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRFe5G/btqQZw8FPB4/PuSBQpEEn02ONKEif2jbK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRFe5G/btqQZw8FPB4/PuSBQpEEn02ONKEif2jbK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRFe5G%2FbtqQZw8FPB4%2FPuSBQpEEn02ONKEif2jbK1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;200&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;  어쩌다가 신청하게 됐어?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;이직을 계획하고 있었다.&lt;/strong&gt;&lt;br&gt;1월 말에 퇴사하겠다고 말했고, 다시 처음으로 돌아간다는 생각으로 프론트엔드 개발자가 되는 데 필요한 결정이었다.&lt;br&gt;나에게 있어서 공부하는 즐거움을 주고 즐기며 일할 수 있겠다고 확신을 준 프론트엔드 녀석…. 그 녀석으로 인해서 더는 지체할 수 없었고, 이직을 위해서 회사에 다니는 와중에 퇴근 후 공부하기를 매일 반복하였다. 회사보다 퇴근 후가 나의 하루 시작이라고 생각했을 만큼 즐기며 공부하는 와중에 회사의 과장님 추천으로 &lt;a href=&quot;https://www.codesoom.com/&quot;&gt;코드숨&lt;/a&gt;을 알게 되었다.&lt;br&gt;혼자 프론트엔드를 공부하면서 &lt;em&gt;이렇게 하는 게 맞는 걸까?&lt;/em&gt; 라는 의문이 들었고, 그 의문을 해결해주기 위해 &lt;strong&gt;누군가의 가이드가 필요하다 생각&lt;/strong&gt;했고, &lt;strong&gt;회사에 다니면서 할 수 있는 점&lt;/strong&gt;이 가장 매리트를 많이 느꼈던 부분이었다. 그에 부합하는 것이 코드숨이였지만, 가격적으로 부담이 안 됐다고는 말 못하겠다. 나에게는 굉장히 큰돈이였고, 신중히 결정할 문제었다.&lt;br&gt;시간을 돈으로 살 수 없다는 말이 있지 않았나? 돈으로 시간을 살 수는 없는 게 분명했지만, 시간을 아낄 수 있다는 점은 명확했기에 충분히 투자할 가치가 있다고 생각했다.&lt;br&gt;그런 확신을 가지고 열심히 해보자고 다짐하며 신청하게 됐다.&lt;/p&gt;
&lt;p&gt;서론이 매우 길었다.&lt;br&gt;이제 그 3달 동안의 험난하고 꽤나 고통스러웠던 여행 을 기록해보며 다시 나를 돌아보고 스스로를 다독이며 동기부여를 얻고 앞으로의 방향성을 제시해보려 한다.&lt;/p&gt;
&lt;h2&gt; ‍♂️ 어리버리.. 그렇게 흘러간 2주&lt;/h2&gt;
&lt;p&gt;원래 처음은 다 어렵듯이 해본 적 없고, 얕게 알고 있던 지식으로 알아가 보려 하니 역시 처음 2주는 쉽게 진행되지 않았다.&lt;br&gt;Github에 PR(Pull Request) 제대로 해본 적 없었고, 커밋 메시지에 대해서도 역시 고민해본 적 없었다.&lt;/p&gt;
&lt;p&gt;그렇게 2주 동안 간단한 과제(&lt;a href=&quot;https://www.codesoom.com/courses/react&quot;&gt;커리큘럼 참고&lt;/a&gt;)를 수행하면서 엄청나게 많은 코드리뷰를 받았다. 받으면서 &lt;em&gt;부족한 게 정말 많구나..&lt;/em&gt; 라는 생각과 코드를 작성할 때 리팩토링에 대한 고민을 해본 적 없었던 나에게는 굉장히 신선하면서 하나씩 알아가는 재미를 느꼈다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1637&quot; data-origin-height=&quot;1432&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1RDDO/btqQ0QFVu7O/WwyzdUNkIGkl58VtYK5UT0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1RDDO/btqQ0QFVu7O/WwyzdUNkIGkl58VtYK5UT0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1RDDO/btqQ0QFVu7O/WwyzdUNkIGkl58VtYK5UT0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1RDDO%2FbtqQ0QFVu7O%2FWwyzdUNkIGkl58VtYK5UT0%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;1637&quot; data-origin-height=&quot;1432&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;  2주간의 과제 PR &lt;a href=&quot;https://github.com/CodeSoom/week1-assignment-1/pull/44&quot;&gt;1주차-1&lt;/a&gt;, &lt;a href=&quot;https://github.com/CodeSoom/week1-assignment-2/pull/36&quot;&gt;1주차-2&lt;/a&gt;, &lt;a href=&quot;https://github.com/CodeSoom/week2-assignment-1/pull/33&quot;&gt;2주차-1&lt;/a&gt;, &lt;a href=&quot;https://github.com/CodeSoom/week2-assignment-2/pull/38&quot;&gt;2주차-2&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;과제를 수행하고 리뷰를 받고 받을 리뷰에 대해서 수정하고 다시 커밋하고를 반복하며 처음에 짰던 코드보다 훨씬 더 보기 좋아진 코드를 볼 수 있었다.&lt;br&gt;수정 후 코드 리뷰 받는 사이클을 반복하며 1주일 2주일이 지났을 때 이제 좀 코드숨의 과제 진행 형태를 적응해 갈 때 즘이였다.&lt;br&gt;가장 고통스러웠던 그 녀석이 찾아왔다.. &lt;/p&gt;
&lt;h2&gt;  모든 고통의 근원 TDD(테스트 주도 개발)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;사실 코드숨 과정을 알기 전, TDD(Test-Driven Development) 의 T자도 몰랐다.&lt;br&gt;그래서 테스트 케이스를 짜기 위해 시간을 더 많이 투자했어야 했고, 머리를 쥐어짜며 고통의 연속이 될 수밖에 없었다.&lt;br&gt;2주차 때 만들었던 간단한 TO-DO 리스트에 대한 테스트를 작성해보라는 것이었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;775&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfY7UZ/btqQZwvd2WD/DkgfFbgKy7PZvaQ0dyU8w1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfY7UZ/btqQZwvd2WD/DkgfFbgKy7PZvaQ0dyU8w1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfY7UZ/btqQZwvd2WD/DkgfFbgKy7PZvaQ0dyU8w1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfY7UZ%2FbtqQZwvd2WD%2FDkgfFbgKy7PZvaQ0dyU8w1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;775&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;3주차는 TDD 사이클 적용하기 전 테스트에 친해져 보기 위해서 아마 TDD를 제외한 과제였나 싶었다.&lt;br&gt;아샬님의 강의 영상도 보았지만, 생각보다 쉽게 친해지지 못했다. &lt;br&gt;특히 &lt;code&gt;describe-context-it&lt;/code&gt;이 구조와 &lt;code&gt;mocking&lt;/code&gt; 함수를 만드는 점..등&lt;br&gt;하지만 나에게는 누가 있던가. 친절히 답해주시는 기현님.. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;880&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJPqt6/btqQXALjmEA/m72IQp7TZUNVX7ot8eTIo1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJPqt6/btqQXALjmEA/m72IQp7TZUNVX7ot8eTIo1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJPqt6/btqQXALjmEA/m72IQp7TZUNVX7ot8eTIo1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJPqt6%2FbtqQXALjmEA%2Fm72IQp7TZUNVX7ot8eTIo1%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;880&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;  &lt;a href=&quot;https://github.com/CodeSoom/week3-assignment-1/pull/32&quot;&gt;3주차 PR&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;그렇게 테스트 케이스를 작성하는 것은 이런 건가? 라는 느낌이 들 때쯤 4주차부터 시작된 TDD 사이클을 적용하라는 과제였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;690&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xLAHh/btqRau99JGv/4CMJaVlxj8niITEKft0kP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xLAHh/btqRau99JGv/4CMJaVlxj8niITEKft0kP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xLAHh/btqRau99JGv/4CMJaVlxj8niITEKft0kP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxLAHh%2FbtqRau99JGv%2F4CMJaVlxj8niITEKft0kP1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;690&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;아무 생각 없이 개발했던 나에겐 &lt;em&gt;TDD를 왜 해야 할까?&lt;/em&gt; 라는 의문이 들 수밖에 없었다.&lt;br&gt;&lt;strong&gt;테스트를 먼저 구현하고(red), 실제 기능을 구현(green) 후, 리팩토링(refactoring)을 하라는 점&lt;/strong&gt;은 익숙지 않았다.&lt;br&gt;&lt;em&gt;아니 뭐라고?  테스트를 구현하여 테스트가 깨지게 한 다음, 그에 맞춰서 기능을 구현한다니..&lt;/em&gt; 처음은 설명을 들어도 이해가 가지 않았지만, 그래도 역시 부딛혀보고 깨닫는 게 아니겠는가..! 일단 해보자!  &lt;/p&gt;
&lt;p&gt;그렇게 진행된 &lt;strong&gt;TDD 사이클에 맞춰 그에 맞는 커밋 메시지를 적고 과제를 수행&lt;/strong&gt;하였다.&lt;br&gt;그로 인해 확실히 하나의 커밋당 작업을 잘게 나눠서 하다 보니 4주차 커밋이 69개나 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;700&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/usNVg/btqQZwhH99s/qWrGvAdlOs1IZ7PEZa6kH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/usNVg/btqQZwhH99s/qWrGvAdlOs1IZ7PEZa6kH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/usNVg/btqQZwhH99s/qWrGvAdlOs1IZ7PEZa6kH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FusNVg%2FbtqQZwhH99s%2FqWrGvAdlOs1IZ7PEZa6kH0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;700&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이때부터는 느껴졌던 건 TDD는 고통의 연속, 과제는 TDD를 위한 것이라 해도 과언이 아니었다는 점..! &lt;/p&gt;
&lt;p&gt;사실 TDD로 과제를 진행하면서도 &lt;em&gt;이렇게 하는 게 맞는 건가..?&lt;/em&gt; 라는 생각이 계속 들었다. 하면서도 솔직히 확신은 없었고, 그냥 하라니까 일단 해봐야지라는 생각이 없다고 한다면 그건 나쁜 거짓말이다.&lt;br&gt;그렇기에 TDD의 의문점은 여전히 가득하다 못해 흘러넘쳤다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/CodeSoom/week4-assignment-1/pull/34&quot;&gt;4주차-1 PR&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/CodeSoom/week4-assignment-2/pull/28&quot;&gt;4주차-2 PR&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;4주차부터는 무조건 &lt;strong&gt;TDD로 기능을 구현해야 했고, 또한 테스트 커버리지 역시 100%를 달성&lt;/strong&gt;해야 했다.&lt;br&gt;사실 리액트는 어느 정도 다룰 줄 알았지만, 그 리액트는 단순히 &lt;strong&gt;&amp;quot;도구&amp;quot;&lt;/strong&gt; 였을 뿐 중요하지 않았다. 기능을 구현할 줄 알면 뭐하니.. 그건 중요하지 않는데 TDD라는 큰 산이 나를 막고 있는데!  &lt;/p&gt;
&lt;p&gt;그렇게 4주차가 지나고 5주차...8주차까지 TDD와 익숙해지기 위해 매일 꾸준히 공부하고 친해지기 위해 많은 고통을 느끼며 노력했던 거 같다.&lt;br&gt;TDD 사이클을 지키기 위해서 커밋은 각 PR당 100개 정도의 커밋이 존재하게 되었고, 그만큼 받는 코드리뷰가 많을 수 있던 거 같다.&lt;/p&gt;
&lt;p&gt;이 과정에서 점점 TDD 사이클에 익숙해졌고, 어느 정도 테스트 케이스를 짤 수 있게 되었다. 또한, 처음에 들었던 왜 TDD를 할까? 라는 생각은 점점 사라져 가고 있었다. 코드를 리팩토링할 때 &lt;em&gt;아..리팩토링하다가 기존의 기능이 안 되면 어떡하지?&lt;/em&gt; 라는 생각을 했지만 테스트를 작성하니 수정했을 때 조마조마한 생각 없이 &lt;strong&gt;안심하고 코드를 수정하는 있는 힘을 가질 수 있게 되었다.&lt;/strong&gt; 또한, 수정하면 당연히 테스트가 깨지겠고, 깨지는 부분을 바로바로 확인하며 수정할 수 있으니 기존보다 &lt;strong&gt;더 빠르게 오류를 찾아 수정할 수 있었다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/CodeSoom/week5-assignment-1/pull/27&quot;&gt;5주차 PR&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/CodeSoom/week6-assignment-1/pull/26&quot;&gt;6주차 PR&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/CodeSoom/week7-assignment-1/pull/26&quot;&gt;7주차 PR&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/CodeSoom/week8-assignment-1/pull/22&quot;&gt;8주차 PR&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  NO! 회고! NO! Merge!&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;매주 과제를 끝내기 위해서는 PR을 Merge를 해야 한다.&lt;/strong&gt;&lt;br&gt;하지만 과제를 다 수행했다고 해서 끝이 아니다. &lt;strong&gt;회고 없이는 절대 merge를 할 수 없다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;207&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BY0ie/btqRoRrMeU4/77lgdtV6HN0QIUaf1j7eI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BY0ie/btqRoRrMeU4/77lgdtV6HN0QIUaf1j7eI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BY0ie/btqRoRrMeU4/77lgdtV6HN0QIUaf1j7eI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBY0ie%2FbtqRoRrMeU4%2F77lgdtV6HN0QIUaf1j7eI1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;207&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그래도 회고 작성에 대한 부분에 대해서는 꽤 긍정적인 생각이었다.&lt;br&gt;&lt;strong&gt;글은 매우 못 쓰지만&lt;/strong&gt;, 깃허브에 기록을 남기고 블로그 쓰는 것을 좋아했기 때문에 반가운 회고 작성이었다.&lt;/p&gt;
&lt;p&gt;회고를 작성함으로써 매주 과제를 수행하고 회고를 작성할 때마다 &lt;strong&gt;한 주를 되돌아보는 계기&lt;/strong&gt;가 된다. 한 주 동안 어떤 점이 아쉬웠고, 어떤 부분은 칭찬해줄 만한 일인지를 다시금 일깨워주고 마무리할 수 있게 해준다. 또한, 매주 새로 알게 된 점과 아쉬운 점에 대해서는 다음 주에 어떻게 할지를 계획도 세우게 해주는 큰 역할을 해준다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;  &lt;a href=&quot;https://github.com/saseungmin/codesoom_code_review_retrospective&quot;&gt;3달 동안의 회고 모음&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;항상 느끼는 거지만 &lt;strong&gt;어떤 것을 했는지 꾸준히 기록을 남기고, 자기 생각을 남기는 게&lt;/strong&gt; 나중에 돼서도 &lt;em&gt;아 이때는 이랬구나!&lt;/em&gt; 하면서 얼마나 발전했는지 또는 아쉬운 부분을 찾아 다시금 동기부여가 될 수 있게 해주는 거 같다.&lt;/p&gt;
&lt;h2&gt;  마지막을 멋지게 장식할 4주간의 나만의 프로젝트&lt;/h2&gt;
&lt;p&gt;2달 동안 배웠던 모든 것들을 총동원하여 나만의 개인 프로젝트를 할 수 있는 날이 왔다. 이 순간을 위해 2달 동안 열심히 달려온 것 아닌가! &lt;br&gt;사실 개인 프로젝트에 대해서 주제를 미리 정하고 어떻게 구상할지를 생각이라도 하고 있어야 했지만, 고민조차 안 하고 있었다.. &lt;br&gt;그렇게 시작 전 한 주라는 시간 동안 어떤 주제를 할지 생각을 해보았는데, 자신이 원하는 주제로 스터디를 모집하고 참여할 수 있는 그런 스터디 모집 사이트를 만들까 생각을 하게 되었다. 하지만 회사와 병행하고 있는 나로서는 생각을 하면 할수록 기능이 너무 많아질 거 같아서 4주라는 시간동안 제대로 마무리를 지을지에 대해서 확신이 없었다. 그래서 다른 프로젝트를 할까 더 해보았지만, 마땅히 떠오르는 주제가 생각나지 않았다. 그때 수형 님이 slack에서 쓰레드로 해주신 말이 생각이 났다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;목숨 걸고 만들으라는 말씀.. 최선을 다해 도와드리겠다..&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;269&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBmHUd/btqRqbDkNGO/EecfIAKir619q3YRh7tYpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBmHUd/btqRqbDkNGO/EecfIAKir619q3YRh7tYpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBmHUd/btqRqbDkNGO/EecfIAKir619q3YRh7tYpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBmHUd%2FbtqRqbDkNGO%2FEecfIAKir619q3YRh7tYpk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;269&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그렇게 수형 님의 말씀이 생각나서 될 때로 돼보자 하는 심보, 그리고 2달 동안 쉬지 않고 달려왔지만, &lt;em&gt;잠을 줄이지 않았어. 잠을 딱 한 시간만 더 줄여보자. 한 달 금방가.&lt;/em&gt; 라는 생각으로 스스로 동기부여를 얻고 개인 프로젝트 주제를 결정하였다.&lt;/p&gt;
&lt;p&gt;결정 후 이젠 프로젝트를 진행해야지~! 하는 순간 &lt;em&gt;아 맞다..여긴 코드숨이지..? 그냥 프로젝트를 진행할 수는 없지!&lt;/em&gt; 라는 생각을 다시 한 번 일깨워준 규칙? 들을 말씀해주셨다.&lt;br&gt;첫 번째는 매주 구현하기에 앞서 github 개인 프로젝트 repository의 &lt;strong&gt;issue에 계획을 작성해야 한다.&lt;/strong&gt;&lt;br&gt;그리고 &lt;strong&gt;한 주가 끝날 때마다 회고를 작성해야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;  &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/issues/1&quot;&gt;프로젝트 1주차 계획&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;  &lt;a href=&quot;https://github.com/saseungmin/codesoom_code_review_retrospective/tree/master/week_9&quot;&gt;프로젝트 1주차 회고&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;918&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFDN4q/btqRrQej5Kk/kHwRkHMc7qzuElP0bPqUM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFDN4q/btqRrQej5Kk/kHwRkHMc7qzuElP0bPqUM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFDN4q/btqRrQej5Kk/kHwRkHMc7qzuElP0bPqUM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFDN4q%2FbtqRrQej5Kk%2FkHwRkHMc7qzuElP0bPqUM0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;918&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;두 번째는 프로젝트를 매주 &lt;strong&gt;구현한 결과를 보여줄 수 있도록 하라는 점.&lt;/strong&gt; 배포(웬만해서는 배포로..)를 하던, 동영상, 스크린 샷 등을 사용해서 말이다.&lt;br&gt;처음 들었을 땐 &lt;em&gt;오호?  왜 그래야 할까? 흠.. 그렇군.. &lt;/em&gt; 생각이 들었지만, 이 역시 매주 진행하면서 몸으로 느낄 수 있었다.&lt;br&gt;일단, 보여줄 수 있는 결과물이 있어야지 트레이너분들이 보고 &lt;strong&gt;리뷰와 피드백을 빠르게 볼 수 있는 매개체 역할을 할 수 있는 점&lt;/strong&gt;이 가장 큰 이유이다.&lt;br&gt;프로젝트가 끝난 후 &lt;a href=&quot;https://github.com/saseungmin/reading_books_record_repository/tree/master/The%20Nature%20of%20Software%20Development&quot;&gt;The Nature of Software Development&lt;/a&gt; 책과 &lt;a href=&quot;https://github.com/saseungmin/reading_books_record_repository/tree/master/%ED%81%B4%EB%A6%B0%20%EC%95%A0%EC%9E%90%EC%9D%BC&quot;&gt;Clean Agile&lt;/a&gt;을 읽고 스터디를 하고 나서 더 확실히 그 이유를 알게 되었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;  배포된 매주 결과물을 보고 &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/issues?q=is%3Aissue+author%3Ahannut91+&quot;&gt;issue&lt;/a&gt;를 달아주셨던 윤석 님&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;644&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjec46/btqRom6Hgzx/Ch2JBYPoyAGglr7CUKEcGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjec46/btqRom6Hgzx/Ch2JBYPoyAGglr7CUKEcGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjec46/btqRom6Hgzx/Ch2JBYPoyAGglr7CUKEcGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjec46%2FbtqRom6Hgzx%2FCh2JBYPoyAGglr7CUKEcGk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;644&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그렇게 매주 계획을 세우고 회고를 작성하기를 반복하고, TDD 사이클에 맞춰 기능을 구현한 4주 동안 스스로한테 열심히 했다고 칭찬해 줄 수 있을 만큼 열심히 했던 거 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;  &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/issues?q=label%3A%22weekly+plan%22+&quot;&gt;프로젝트 4주간의 계획&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;  고된 4주간의 프로젝트 기간이 끝나고, 결과물을 되돌아 보았다.&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;4주 동안 240개의 커밋, 97개의 PR이 존재했다.&lt;/li&gt;
&lt;li&gt;기능으로는 로그인, 회원가입, 스터디 소개글 작성, 수정, 삭제와 스터디 신청에 따른 사용자 상태 등을 구현했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;2864&quot; data-origin-height=&quot;2815&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfQ8PL/btqRptddkPv/vXpcWrKy0C32GVxZE5bxD1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfQ8PL/btqRptddkPv/vXpcWrKy0C32GVxZE5bxD1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfQ8PL/btqRptddkPv/vXpcWrKy0C32GVxZE5bxD1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfQ8PL%2FbtqRptddkPv%2FvXpcWrKy0C32GVxZE5bxD1%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;2864&quot; data-origin-height=&quot;2815&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;비록 테스트 커버리지는 100%를 만족하지 못했지만, 테스트 케이스는 총 229개를 작성하게 되었다.&lt;br&gt;테스트 커버리지는 100%를 만족시키지 못했던 가장 큰 이유는 첫 번째는 글 작성할 때 외부 라이브러리 에디터를 사용하여 테스트를 작성하는 데 한계가 있었다는 점, 두 번째는 firebase를 통한 api 통신에 대한 테스트를 작성하는데 mock 함수를 만드는 부분에 대한 이해의 부족함.. (추후 고통받으며 테스트 케이스를 작성해 볼 예정.)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;164&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dk9PcT/btqRptddaO0/RZxY5djdGmkJyrRKhOLB0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dk9PcT/btqRptddaO0/RZxY5djdGmkJyrRKhOLB0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dk9PcT/btqRptddaO0/RZxY5djdGmkJyrRKhOLB0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdk9PcT%2FbtqRptddaO0%2FRZxY5djdGmkJyrRKhOLB0K%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;164&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;다른 사람들은 어떻게 볼지 몰라도 스스로에게 충분히 만족스러운 결과물이라고 생각한다.✨ &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://sweet-1cfff.web.app/&quot;&gt;개인 프로젝트 주소&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin&quot;&gt;개인 프로젝트 repository&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  코드숨 과정 외에서 벌어지는 일들!?&lt;/h2&gt;
&lt;p&gt;코드숨에서 소통과 커뮤니티 역할을 하는 건 슬랙을 사용하고 있다.&lt;br&gt;슬랙을 통해 각자 &lt;strong&gt;회고&lt;/strong&gt;를 공유하고, 필요한 정보들과 모르는 부분을 질문할 수도 있다.&lt;br&gt;그중에서도 윤석 님이 주최하여 매주 진행하고 있는 스터디가 있다. 이 스터디는 하고 싶은 사람에 한해서 &lt;strong&gt;관련 기술 서적을 읽어와 매주 수요일 Google meet을 통해 주어진 범위에 대해서 의논하고, 같이 얘기할 수 있는 스터디이다.&lt;/strong&gt;&lt;br&gt;코드숨 과정이 끝난 지금까지도 계속 이어져 오고 있고, 현재까지 진행했던 책으로는 &lt;a href=&quot;https://github.com/CodeSoom/functional-javascript&quot;&gt;함수형 자바스크립트&lt;/a&gt;, &lt;a href=&quot;https://github.com/CodeSoom/The-Nature-of-Software-Development&quot;&gt;The Nature of Software Development&lt;/a&gt;, &lt;a href=&quot;https://github.com/saseungmin/reading_books_record_repository/tree/master/%ED%81%B4%EB%A6%B0%20%EC%95%A0%EC%9E%90%EC%9D%BC&quot;&gt;클린 애자일&lt;/a&gt;이고 앞으로도 줄 서 있는 책들이 가득하다.&lt;/p&gt;
&lt;p&gt;스터디를 통해서 얻은 장점은 이해를 못 해서 넘어갔던 부분을 다시 다 같이 이야기하며 이해할 수 있게 되었고, 스터디를 하니 책을 더 집중해서 읽게 되었다. 또한, 다른 분들의 의견과 생각을 들을 수 있었고 가끔 현업에 관련된 이야기도 들을 수 있어서 재밌었다.&lt;br&gt;궁극적으로는 더 열심히 해야겠다는 동기부여를 매주 얻고 가서 너무 알찬 스터디라고 생각하고, 스터디를 매주 진행해주셔서 너무 감사하다.  &lt;/p&gt;
&lt;h2&gt;  3달간의 여정을 마치고 하고 싶은 말이 있다면&lt;/h2&gt;
&lt;h3&gt;  신청하실 생각이 단 1%라도 있으신 분들에게&lt;/h3&gt;
&lt;p&gt;해주고 싶은 말은 시작 전 마음 단단히 먹고 시작하는 것이 좋을 것이다. 과정이 꽤 고통스럽다.&lt;br&gt;트레이너분들이 꾸준히 압박을 넣어주긴 하지만 이 역시 스스로 열심히 할 마음을 먹지 않으면 쉽지 않다 생각한다. 자신이 가진 마음가짐이 어떤지를 확인하고 또 확인한 뒤 확신이 있으면 그때 신청했으면 한다. 그렇지 않으면 돈만 버리게 될 뿐이니..&lt;br&gt;직장이 있으신 분들이거나 이직을 원하시는 분들은 무조건 추천한다. 이처럼 온라인으로 시간 제약을 받지 않고 할 수 있는 과정이 어딨을까?&lt;br&gt;일단 신청하여 열심히 한다면 후회는 없고 돈 따위 전혀 아깝다고 생각이 들지 않을 것이다.&lt;br&gt;TDD를 배우고 싶은 분들에게는? 말해야 알까?  &lt;/p&gt;
&lt;h3&gt;  트레이너분들과 동기분들에게&lt;/h3&gt;
&lt;p&gt;코드숨 시작 전과 시작 후를 비교해 봤을 때 너무 많이 성장하게 도와준 모든 트레이너분에게 너무 감사드립니다. &lt;br&gt;좋은 강의를 해주시고 늘 어디선가 지켜봐 주시는 &lt;a href=&quot;https://github.com/ahastudio&quot;&gt;아샬님&lt;/a&gt;, 누구보다 열정 가득하고 늘 자세한 코드리뷰와 스터디를 진행해주신 &lt;a href=&quot;https://github.com/hannut91&quot;&gt;윤석님&lt;/a&gt;, 당근과 채찍을 같이 주시는 멋진 &lt;a href=&quot;https://github.com/wholemann&quot;&gt;수형님&lt;/a&gt;, 항상 친절하게 답해주시는 &lt;a href=&quot;https://github.com/Kihyun92&quot;&gt;기현님&lt;/a&gt; 까지 다시 한 번 감사드립니다!  &lt;br&gt;3달간 여정을 함께한 동기분들과 1기 여러분들도 감사드립니다.  항상 슬랙에서 많은 이야기와 회고를 보면서 동기부여를 얻었고, 3달 동안 지치는 순간도 있었지만, 함께 달렸기에 끝까지 할 수 있던 거 같습니다. 모두 감사드립니다!  &lt;/p&gt;
&lt;h2&gt;  앞으로의 계획이 뭐야?&lt;/h2&gt;
&lt;p&gt;길다면 길고 짧다면 짧은 3달간의 여정이 끝났다. 끝났더라도 진행했던 프로젝트에 아직 기능이 만족스럽지 못하기에 기능을 덧붙여 나갈 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;  &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/issues/115&quot;&gt;개인 프로젝트에 대한 앞으로의 계획&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;하지만 &amp;quot;끝은 언제나 새로운 시작&amp;quot;이라는 말이 있듯이 이젠 코드숨에서 배운 인사이트를 가지고 새로운 시작을 준비해야 한다.  &lt;br&gt;지금 회사를 퇴사하고 프론트엔드 개발자라는 새로운 시작을 위해 더 노력해야 할 것이다. 지금보다 앞으로가 더 힘들게 눈에 훤히 보인다. 하지만 고통이 있을수록 성취감과 얻는 것이 많은 걸 알기에 돌아서 가는 길보단 부딪쳐 나갈 것이다.&lt;/p&gt;
&lt;p&gt;처음 프론트엔드 공부를 시작했을 당시의 목표와 목적을 잊지 말고 &lt;strong&gt;지치지 말고 꾸준히&lt;/strong&gt;라는 말을 머릿속에 항상 가지고 앞으로 나아가야 한다. 안 그러면 내가 나한테 지쳐 스트레스로 버티지 못할 것이 분명하기 때문이다. 그래도 오래달리기는 잘하는 편이니  괜찮을 것이다. 지금 가지고 있는 절실함과 열정으로 꾸준히 한다면 언젠가는 이 고통이 딱딱한 굳은살이 아닌 순화되어 말끔한 새살을 돋게 해줄 수 있지 않을까? 그렇게 믿어본다. &lt;br&gt;배워야 할 것들이 너무 많고 끊임없이 변화하는 트렌드 속에서 어떻게 공부해야 하는 것이 좋은 방법인가는 여전히 모르겠다. 방법이 뭐 있을까? 잔머리를 굴려 돌아갈 방법은 찾기보다 그 시간에 꾀부리지 말고 꾸준히 하는 방법뿐..&lt;/p&gt;
&lt;p&gt;올해처럼 열심히 살았다고 자신 있게 말할 수 있는 해는 살면서 여태껏 없던 거 같다. 2020년도는 전 세계가 지옥 그 자체였지만, 그 지옥에서 어느 때보다 열심히 달려온 나. 올해가 가기 전에 올해를 되돌아보는 회고를 작성해야겠고 내년의 계획도 같이 작성해봐야겠다.&lt;/p&gt;
&lt;p&gt;내년엔 새로운 직장에서 당당하게 프론트엔드 개발자라는 타이틀로 개발하고 있는 내가 되어있으면 좋겠다.  &lt;/p&gt;</description>
      <category>회고</category>
      <category>react</category>
      <category>TDD</category>
      <category>코드숨</category>
      <category>코드숨 2기</category>
      <category>회고</category>
      <category>후기</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/23</guid>
      <comments>https://haranglog.tistory.com/23#entry23comment</comments>
      <pubDate>Sat, 26 Dec 2020 15:13:22 +0900</pubDate>
    </item>
    <item>
      <title>✔ 우아한 테크러닝 3기: React &amp;amp; TypeScript 8회차(마지막 회차)</title>
      <link>https://haranglog.tistory.com/22</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1671&quot; data-origin-height=&quot;1546&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F9Zwh/btqJxqOQ3k3/NPgpkFhNKWl5syoEeFM5OK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F9Zwh/btqJxqOQ3k3/NPgpkFhNKWl5syoEeFM5OK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F9Zwh/btqJxqOQ3k3/NPgpkFhNKWl5syoEeFM5OK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF9Zwh%2FbtqJxqOQ3k3%2FNPgpkFhNKWl5syoEeFM5OK%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;1671&quot; data-origin-height=&quot;1546&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;✌ 9월24일 (목) 우아한 테크러닝 3기 8회차 강의 정리&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  8회차 강의 목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://codesandbox.io/s/navigation08live-8op00?file=/src/index.tsx&quot;&gt;마지막 예제 살펴보기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mobx.js.org/README.html&quot;&gt;Mobx&lt;/a&gt; 살펴보기&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  예제 살펴보기&lt;/h2&gt;
&lt;h3&gt;  React-Router&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이 예제는 1년전 예제라 &lt;a href=&quot;https://reactrouter.com/&quot;&gt;react-router&lt;/a&gt; 공식 문서를 참고하며 보기를 바란다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;router&lt;/code&gt;의 진행 동작은 URL이 바뀌는 것을 내부의 &lt;code&gt;location&lt;/code&gt;의 &lt;code&gt;path&lt;/code&gt;를 감지해서 해당 &lt;code&gt;location&lt;/code&gt;의 주소에 맞는 컴포넌트와 연결시켜주는 간단한 동작이다.&lt;br&gt;그렇기 때문에 서버 사이드에서 말하는 router와 다르다. 서버 사이드에서는 요청 URI end point path에 따라서 다른 비지니스 로직을 실행하는 컴포넌트를 연결시키는 것이고 여기서의 router는 URL의 변경 사항과 그것과 mapping되는 페이지 컴포넌트들을 연결시켜주는 역할을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import * as React from &amp;quot;react&amp;quot;;
import { BrowserRouter, Switch, Route } from &amp;quot;react-router-dom&amp;quot;;
import {
  DefaultLayout,
  FullSizeLayout,
  NotificationContainer
} from &amp;quot;../containers&amp;quot;;
import * as Pages from &amp;quot;../pages&amp;quot;;
import PrivateRoute from &amp;quot;./PrivateRoute&amp;quot;;

interface IProps {
  children?: React.ReactNode;
}

const Router: React.FC&amp;lt;IProps&amp;gt; = () =&amp;gt; {
  return (
    &amp;lt;BrowserRouter&amp;gt;
      &amp;lt;NotificationContainer /&amp;gt;
      &amp;lt;Switch&amp;gt;
        // react-router-dom이 제공하는 컴포넌트
        &amp;lt;Route exact path=&amp;quot;/login&amp;quot;&amp;gt;
          &amp;lt;FullSizeLayout&amp;gt;
            &amp;lt;Pages.Login /&amp;gt;
          &amp;lt;/FullSizeLayout&amp;gt;
        &amp;lt;/Route&amp;gt;
        &amp;lt;DefaultLayout&amp;gt;
          &amp;lt;Switch&amp;gt;
            // 인증정보들을 체크해서 안되어있을 때 다른 동작을 취하게 만드는 PrivateRoute
            // 로그인안하면 못들어가는 url 같은 것
            &amp;lt;PrivateRoute exact path=&amp;quot;/&amp;quot; page={Pages.Dashboard} /&amp;gt;
            &amp;lt;PrivateRoute exact path=&amp;quot;/orders&amp;quot; page={Pages.Order} /&amp;gt;
            &amp;lt;PrivateRoute exact path=&amp;quot;/shops&amp;quot; page={Pages.Shops} /&amp;gt;
            &amp;lt;Route component={Pages.PageNotFound} /&amp;gt;
          &amp;lt;/Switch&amp;gt;
        &amp;lt;/DefaultLayout&amp;gt;
      &amp;lt;/Switch&amp;gt;
    &amp;lt;/BrowserRouter&amp;gt;
  );
};

export default Router;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;아래는 &lt;code&gt;PrivateRoute&lt;/code&gt; 컴포넌트로 로그인이 필요한 컴포넌트로 이동할 때 로그인 되지 않았았을 때 &lt;code&gt;redirect&lt;/code&gt; 시켜주는 로직이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const PrivateRouter: React.FC&amp;lt;IProps &amp;amp; IStateToProps &amp;amp; RouteProps&amp;gt; = props =&amp;gt; {
  const Page: RoutePageComponent = props.page;
  const { authentication } = props;

  return (
    &amp;lt;Route
      {...props}
      render={props =&amp;gt; {
        if (authentication) {
          return &amp;lt;Page {...props} /&amp;gt;;
        } else {
          return (
            &amp;lt;Redirect
              to={{
                pathname: &amp;quot;/login&amp;quot;,
                state: { from: props.location }
              }}
            /&amp;gt;
          );
        }
      }}
    &amp;gt;
      {props.children}
    &amp;lt;/Route&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PrivateRouter&lt;/code&gt;에서 &lt;code&gt;Route&lt;/code&gt; 컴포넌트를 보면 아래와 같은 형태를 &lt;strong&gt;&lt;code&gt;render&lt;/code&gt; 패턴&lt;/strong&gt;이라고 부른다.&lt;br&gt;컴포넌트에 &lt;code&gt;children&lt;/code&gt;을 사용하는 것이 아니라 &lt;code&gt;render&lt;/code&gt;라고 하는 &lt;code&gt;props&lt;/code&gt;를 제공해주고 만약에 &lt;code&gt;rendering&lt;/code&gt;관련한 기본동작을 변경할려면 &lt;code&gt;render props&lt;/code&gt;에다가 함수를 제공하는데 이 함수는 결국 &lt;code&gt;react&lt;/code&gt; 컴포넌트이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;authentication&lt;/code&gt; 존재하면 그냥 받은 props(페이지)를 렌더링해주고 만약 없으면 login 페이지로 redirect 해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;&amp;lt;Route
  {...props}
  render={props =&amp;gt; {
    if (authentication) {
      return &amp;lt;Page {...props} /&amp;gt;;
    } else {
      return (
        &amp;lt;Redirect
          to={{
            pathname: &amp;quot;/login&amp;quot;,
            state: { from: props.location }
          }}
        /&amp;gt;
      );
    }
  }}
&amp;gt;
  {props.children}
&amp;lt;/Route&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;  컴포넌트의 아키텍처&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;각 컴포넌트별 파일 이름을 잘 지어 주어야 한다.&lt;br&gt;아래 사진과 같이 &lt;code&gt;page&lt;/code&gt; 컴포넌트들의 이름을 &lt;code&gt;Order.tsx&lt;/code&gt; 이런 식 보다는 &lt;code&gt;OrderPage.tsx&lt;/code&gt;를 붙여서 명확하게 나타내는 방법이 더 좋은 방법이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 &lt;code&gt;Order.tsx&lt;/code&gt;를 살펴보면 &lt;code&gt;PageHeader&lt;/code&gt; 컴포넌트는 &lt;code&gt;label&lt;/code&gt;만 &lt;code&gt;props&lt;/code&gt; 로 전달해주고 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export default class Order extends React.PureComponent&amp;lt;IProps&amp;gt; {
  render() {
    return (
      &amp;lt;React.Fragment&amp;gt;
        &amp;lt;AuthContainer&amp;gt;
          &amp;lt;PageHeader label=&amp;quot;주문&amp;quot; /&amp;gt;
        &amp;lt;/AuthContainer&amp;gt;
        &amp;lt;OrderListContainer /&amp;gt;
      &amp;lt;/React.Fragment&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;하지만 &lt;code&gt;PageHeader&lt;/code&gt; 컴포넌트를 확인해보면 &lt;code&gt;label&lt;/code&gt;이외에 &lt;code&gt;authentication&lt;/code&gt;, &lt;code&gt;requestLogout&lt;/code&gt;, &lt;code&gt;openNotificationCenter&lt;/code&gt;를 &lt;code&gt;props&lt;/code&gt;로 받고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export const PageHeader: React.FC&amp;lt;IProps&amp;gt; = ({
  label,
  authentication,
  requestLogout,
  openNotificationCenter
}) =&amp;gt; {
  // ... 생략

  return (
    // ... 생략
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;결국 직접적으로 &lt;code&gt;props&lt;/code&gt;를 전달해주는 건 &lt;code&gt;AuthContainer&lt;/code&gt;가 대신해준다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthContainer.tsx&lt;/code&gt;&lt;br&gt;&lt;code&gt;map&lt;/code&gt;을 하면서 받은 &lt;code&gt;props&lt;/code&gt;를 &lt;code&gt;cloneElement&lt;/code&gt;를 이용해서 주입을 시켜준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const AuthWrapper: React.FC = props =&amp;gt; {
  const children = React.Children.map(
    props.children,
    (child: React.ReactElement, index: number) =&amp;gt; {
      return React.cloneElement(child, { ...props });
    }
  );

  return &amp;lt;React.Fragment&amp;gt;{children}&amp;lt;/React.Fragment&amp;gt;;
};

export const AuthContainer = connect(
  // ... 생략
)(AuthWrapper);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이런 패턴의 장점은 &lt;code&gt;PageHeader&lt;/code&gt;에서 반복되서 똑같이 내려가는 작업들을 숨김으로써 코드를 절약할 수 있는 장점이 생긴다.&lt;/li&gt;
&lt;li&gt;단점은 명시적으로 &lt;code&gt;props&lt;/code&gt;가 받는게 보이지 않아서 직접 들어가서 확인해야 한다.&lt;/li&gt;
&lt;li&gt;비지니스 로직이 없고 단순 데이터만 주입시키는 형태에서 사용할 때 편리하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export default class Order extends React.PureComponent&amp;lt;IProps&amp;gt; {
  render() {
    return (
      &amp;lt;React.Fragment&amp;gt;
        &amp;lt;AuthContainer&amp;gt;
          &amp;lt;PageHeader label=&amp;quot;주문&amp;quot; /&amp;gt;
        &amp;lt;/AuthContainer&amp;gt;
        &amp;lt;OrderListContainer /&amp;gt;
      &amp;lt;/React.Fragment&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 와 같은 형태에서 아래와 같이 변경이 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export default class Order extends React.PureComponent&amp;lt;IProps&amp;gt; {
  render() {
    return (
      &amp;lt;&amp;gt;
        &amp;lt;AuthContainer&amp;gt;
          &amp;lt;PageHeader label=&amp;quot;주문&amp;quot; /&amp;gt;
        &amp;lt;/AuthContainer&amp;gt;
        &amp;lt;OrderListContainer /&amp;gt;
      &amp;lt;/&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;또한 이 방법도 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export default class Order extends React.PureComponent&amp;lt;IProps&amp;gt; {
  render() {
    return &amp;lt;&amp;gt;
        &amp;lt;AuthContainer&amp;gt;
          &amp;lt;PageHeader label=&amp;quot;주문&amp;quot; /&amp;gt;
        &amp;lt;/AuthContainer&amp;gt;
        &amp;lt;OrderListContainer /&amp;gt;
      &amp;lt;/&amp;gt;;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;아래는 store의 상태의 디자인이다.&lt;br&gt;기본적으로 depth가 깊어지면 어려워지기 때문에 아래와 같이 &lt;code&gt;depth&lt;/code&gt;를 유지하는 것이 편리하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export const initializeState: IStoreState = {
  authentication: null,
  monitoring: false,
  shopList: [],
  openNotificationCenter: false,
  showTimeline: false,
  duration: 200,
  asyncTasks: [],
  notifications: [],
  success: 0,
  failure: 0,
  successTimeline: [],
  failureTimeline: []
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;예제의 자세한 내용과 코드는 &lt;a href=&quot;https://codesandbox.io/s/navigation08live-8op00?file=/src/index.tsx&quot;&gt;김민태님의 CodeSandBox 참고&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  Mobx 살펴보기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;증가하는 &lt;code&gt;counter&lt;/code&gt; 예제로 살펴보자.&lt;/li&gt;
&lt;li&gt;Mobx를 알아보기 전에 우리는 &lt;code&gt;setInterval&lt;/code&gt;로 값을 증가시킬 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// App.tsx
import * as React from &amp;quot;react&amp;quot;;
import &amp;quot;./styles.css&amp;quot;;

interface AppProps {
  data: number;
}

export default function App(props: AppProps) {
  return (
    &amp;lt;div className=&amp;quot;App&amp;quot;&amp;gt;
      &amp;lt;h1&amp;gt;외부 데이터: {props.data}&amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;index.tsx&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import * as React from &amp;quot;react&amp;quot;;
import { render } from &amp;quot;react-dom&amp;quot;;

import App from &amp;quot;./App&amp;quot;;

const store = {
  data: 1
};

const rootElement = document.getElementById(&amp;quot;root&amp;quot;);

setInterval(() =&amp;gt; {
  store.data++;
  render(&amp;lt;App data={store.data} /&amp;gt;, rootElement);
}, 1000);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;하지만 위와 같이 이렇게 할 수는 없다.&lt;br&gt;위와 같은 예제는 데이터가 바꼈다라는 통지를 우리가 직접 &lt;code&gt;setInterval&lt;/code&gt;사용하여 해준 것이다.&lt;/li&gt;
&lt;li&gt;Mobx는 &lt;code&gt;observable&lt;/code&gt;이라는 타입을 제공하고 &lt;code&gt;observable&lt;/code&gt;은 함수이다. 원하는 데이터를 &lt;code&gt;observable&lt;/code&gt;로 감싸준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { observable } from &amp;quot;mobx&amp;quot;;
// ...
const cart = observable({
  data: 1
});
// ...&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Mobx가 제공하는 redux의 subscribe와 같은 &lt;code&gt;autorun&lt;/code&gt;을 사용한다.&lt;br&gt;&lt;code&gt;autorun&lt;/code&gt;는 &lt;code&gt;observable&lt;/code&gt;로 만든 객체가 &lt;strong&gt;자동으로 추적해서&lt;/strong&gt; 변경이 되면 &lt;code&gt;autorun&lt;/code&gt;에 등록된 함수를 실행시켜준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import * as React from &amp;quot;react&amp;quot;;
import { observable, autorun } from &amp;quot;mobx&amp;quot;;
import { render } from &amp;quot;react-dom&amp;quot;;
import App from &amp;quot;./App&amp;quot;;

const cart = observable({
  data: 1
});

const rootElement = document.getElementById(&amp;quot;root&amp;quot;);

autorun(() =&amp;gt; {
  render(&amp;lt;App data={cart.data} /&amp;gt;, rootElement);
});

setInterval(() =&amp;gt; {
  cart.data++;
},1000)&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;counter&lt;/code&gt; 상태를 추가시킨후 &lt;code&gt;counter&lt;/code&gt;는 2씩 증가시켜보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// index.tsx
const cart = observable({
  data: 1,
  counter: 1
});

const rootElement = document.getElementById(&amp;quot;root&amp;quot;);

autorun(() =&amp;gt; {
  render(&amp;lt;App data={cart.data} counter={cart.counter} /&amp;gt;, rootElement);
});

setInterval(() =&amp;gt; {
  cart.data++;
  cart.counter+=2;
}, 1000);

// App.tsx
interface AppProps {
  data: number;
  counter: number;
}

export default function App(props: AppProps) {
  return (
    &amp;lt;div className=&amp;quot;App&amp;quot;&amp;gt;
      &amp;lt;h1&amp;gt;
        외부 데이터: {props.data} vs. {props.counter}
      &amp;lt;/h1&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;만약 상태가 객체 형태가 아니라 &lt;code&gt;Primitive type&lt;/code&gt; 일 때는 어떻게 해야할까?&lt;br&gt;&lt;code&gt;Mobx&lt;/code&gt;의 &lt;code&gt;observable&lt;/code&gt;은 기본값이 객체이기 때문에 기본적으로 &lt;code&gt;Primitive type&lt;/code&gt;을 지원하지 않는다.&lt;br&gt;그렇기 때문에 &lt;code&gt;observable.box&lt;/code&gt;함수를 사용하면 &lt;code&gt;Primitive type&lt;/code&gt;을 사용할 수 있다.&lt;br&gt;하지만 &lt;code&gt;Primitive type&lt;/code&gt;을 사용하는 경우는 별로 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const weight = observable.box(63);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;새로운 값을 넣을려면 &lt;code&gt;set&lt;/code&gt;을 사용하고 값을 가져올려면 &lt;code&gt;get&lt;/code&gt;을 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// 생략..
autorun(() =&amp;gt; {
  console.log(weight.get());
  render(&amp;lt;App data={cart.data} counter={cart.counter} /&amp;gt;, rootElement);
});

setInterval(() =&amp;gt; {
  cart.data++;
  cart.counter += 2;
  weight.set(weight.get() - 1);
}, 1000);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;여기서 &lt;code&gt;console.log&lt;/code&gt;를 찍어 확인해보면 이상한 점을 발견할 수 있다.&lt;br&gt;아래와 같이 동일한 값이 3번씩 찍히는 것을 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span class=&quot;imageblock&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0iN44/btqJBdnO9AC/07qJSpNw7MXUg8dTWRP5KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0iN44/btqJBdnO9AC/07qJSpNw7MXUg8dTWRP5KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0iN44/btqJBdnO9AC/07qJSpNw7MXUg8dTWRP5KK/img.png&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b0iN44/btqJBdnO9AC/07qJSpNw7MXUg8dTWRP5KK/img.png&quot;/&gt;&lt;/span&gt;&lt;/span&gt;&amp;lt;/p|data-origin-width=&amp;quot;201&amp;quot; data-origin-height=&amp;quot;208&amp;quot; data-ke-mobilestyle=&amp;quot;widthOrigin&amp;quot;|alignCenter||_##]&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이러한 이유는 &lt;code&gt;observable&lt;/code&gt;된 값들 각각 마다 변경이 될 때 각각 하나씩 여러번 불리게 된다. 지금은 &lt;code&gt;cart.data&lt;/code&gt;, &lt;code&gt;cart.counter&lt;/code&gt;, &lt;code&gt;weight&lt;/code&gt; 3개의 값이 변경이 되기 때문에 &lt;code&gt;autorun&lt;/code&gt; 3번씩 불러지게 된다.&lt;/li&gt;
&lt;li&gt;그렇기 때문에 이 변경 단위를 &lt;code&gt;Mobx&lt;/code&gt;의 &lt;code&gt;action&lt;/code&gt;을 사용하여 묶어줄 수 있다.&lt;br&gt;&lt;code&gt;action&lt;/code&gt;은 논리적인 작업 단계를 묶어주는 것이다. (&lt;code&gt;redux&lt;/code&gt;의 &lt;code&gt;action&lt;/code&gt;이랑 &lt;code&gt;dispatch&lt;/code&gt;하는 것과 비슷하다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { observable, autorun, action } from &amp;quot;mobx&amp;quot;;

// 생략..
const myAction = action(() =&amp;gt; {
  cart.data++;
  cart.counter += 2;
  weight.set(weight.get() - 1);
});

// 생략..
setInterval(() =&amp;gt; {
  myAction();
}, 1000);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 예제는 &lt;code&gt;action&lt;/code&gt; 따로 사용하는데 &lt;code&gt;cart&lt;/code&gt; 객체에 &lt;code&gt;class&lt;/code&gt;로 변경하여 &lt;code&gt;action&lt;/code&gt;을 같이 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;class Cart {
  data: number = 1;
  counter: number = 1;

  myAction = action(() =&amp;gt; {
    this.data++;
    this.counter += 2;
  });
}

// 인스턴스 객체를 생성한다.
const cart = new Cart();

// 생략...
setInterval(() =&amp;gt; {
  cart.myAction();
}, 1000);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;어노테이션&lt;/strong&gt;을 사용하여 &lt;code&gt;observable&lt;/code&gt;을 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;class Cart {
  @observable data: number = 1;
  @observable counter: number = 1;

  myAction = action(() =&amp;gt; {
    this.data++;
    this.counter += 2;
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;하지만 어노테이션은 typescript에서도 실험적인 기능이기 때문에 &lt;code&gt;tsconfig.json&lt;/code&gt;에 &lt;code&gt;experimentalDecorators&lt;/code&gt;를 &lt;code&gt;true&lt;/code&gt;로 추가 시켜주면 정상적으로 동작하는 것을 확인 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &amp;quot;include&amp;quot;: [
        &amp;quot;./src/*&amp;quot;
    ],
    &amp;quot;compilerOptions&amp;quot;: {
        &amp;quot;strict&amp;quot;: true,
        &amp;quot;esModuleInterop&amp;quot;: true,
        &amp;quot;experimentalDecorators&amp;quot;: true,
        &amp;quot;lib&amp;quot;: [
            &amp;quot;dom&amp;quot;,
            &amp;quot;es2015&amp;quot;
        ],
        &amp;quot;jsx&amp;quot;: &amp;quot;react&amp;quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;action&lt;/code&gt; 도 어노테이션을 사용하여 바꾸어주자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;class Cart {
  @observable data: number = 1;
  @observable counter: number = 1;

  @action
  myAction = () =&amp;gt; {
    this.data++;
    this.counter += 2;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;autorun&lt;/code&gt; 뿐만 아니라 조건에 맞게 사용 가능한 &lt;a href=&quot;https://mobx.js.org/refguide/when.html&quot;&gt;&lt;code&gt;when&lt;/code&gt;&lt;/a&gt;도 존재한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;class MyResource {
    constructor() {
        when(
            // once...
            () =&amp;gt; !this.isVisible,
            // ... then
            () =&amp;gt; this.dispose()
        )
    }

    @computed get isVisible() {
        // indicate whether this item is visible
    }

    dispose() {
        // dispose
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;react Hooks&lt;/code&gt;에 &lt;code&gt;useEffect&lt;/code&gt;와 비슷한 &lt;a href=&quot;https://mobx.js.org/refguide/reaction.html&quot;&gt;&lt;code&gt;reaction&lt;/code&gt;&lt;/a&gt;을 사용한다.&lt;br&gt;&lt;code&gt;reaction&lt;/code&gt;은 두 개의 함수를 받는데 앞에 함수의 return 값이 두 번째 함수 리턴으로 들어가서 두 번째 함수는 앞에 값이 변경되었을 때만 동작하도록 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// correct use of reaction: reacts to length and title changes
const reaction2 = reaction(
    () =&amp;gt; todos.map((todo) =&amp;gt; todo.title),
    (titles) =&amp;gt; console.log(&amp;quot;reaction 2:&amp;quot;, titles.join(&amp;quot;, &amp;quot;))
)&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;  Mobx-react&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;계속 &lt;code&gt;autorun&lt;/code&gt;을 사용하는 것은 안되기 때문에 &lt;code&gt;observable&lt;/code&gt;을 &lt;code&gt;subscribe&lt;/code&gt;하는 &lt;a href=&quot;https://mobx-react.js.org/&quot;&gt;Mobx-react&lt;/a&gt;의 &lt;code&gt;obsever&lt;/code&gt;를 구성할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subscribe&lt;/code&gt;를 하기 때문에 &lt;code&gt;App&lt;/code&gt;컴포넌트의 &lt;code&gt;props&lt;/code&gt;를 제거해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// index.tsx
autorun(() =&amp;gt; {
  render(&amp;lt;App /&amp;gt;, rootElement);
});&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Mobx-react가 제공하는 것 중에 &lt;code&gt;inject&lt;/code&gt;와 &lt;code&gt;observer&lt;/code&gt;가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// App.tsx
import { inject, observer } from &amp;quot;mobx-react&amp;quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;observer&lt;/code&gt;는 &lt;code&gt;Primitive Type&lt;/code&gt; 과 &lt;code&gt;function&lt;/code&gt;은 지원하지 않기 때문에 &lt;code&gt;App&lt;/code&gt; 컴포넌트를 class 컴포넌트로 변경해주어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// 생략..
@observer
export default class App extends React.Component&amp;lt;AppProps&amp;gt; {
  render() {
    return (
      &amp;lt;div className=&amp;quot;App&amp;quot;&amp;gt;
        &amp;lt;h1&amp;gt;
          외부 데이터: {this.props.data} vs. {this.props.counter}
        &amp;lt;/h1&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;그 후 실제로 데이터를 받아올 때는 &lt;code&gt;inject&lt;/code&gt; 어노테이션을 사용해서 주입할 이름을 적어주면 &lt;code&gt;this.props&lt;/code&gt;로 자동으로 주입된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;@inject(&amp;quot;cart&amp;quot;)
@observer
export default class App extends React.Component&amp;lt;AppProps&amp;gt; {
  render() {
    return (
      &amp;lt;div className=&amp;quot;App&amp;quot;&amp;gt;
        &amp;lt;h1&amp;gt;
          외부 데이터: {this.props.data} vs. {this.props.counter}
        &amp;lt;/h1&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;하지만 &lt;code&gt;inject&lt;/code&gt; 어노테이션을 typescript에서 사용하면 단점은 필수가 아니라 &lt;code&gt;optional&lt;/code&gt;로 해줘야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;interface AppProps {
  data?: number;
  counter?: number;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;  마지막 주차(8회차) 후기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한달 간의 우아한 테크러닝 3기를 끝났다.  &lt;br&gt;이번 테크러닝을 통해서 많이 배웠고 어떤 것을 공부해야할지 더 알게된 계기가 되었다.&lt;br&gt;그리고 끝날 때마다 블로깅하는 것도 조금은 귀찮았지만 블로깅하면서 한번 더 복습하는 계기가 되어서 역시 힘들지만 엄청 보람차다.  &lt;br&gt;다음 번에 또 기회가 있어서 꼭 다시 듣고 싶다!&lt;br&gt;한달 동안 좋은 강의 해주신 김민태님 감사합니다!!  &lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>우아한 테크러닝 3기</category>
      <category>react</category>
      <category>typescript</category>
      <category>우아한 테크러닝 3기</category>
      <category>우아한 테크러닝 8회차</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/22</guid>
      <comments>https://haranglog.tistory.com/22#entry22comment</comments>
      <pubDate>Sat, 26 Sep 2020 02:07:58 +0900</pubDate>
    </item>
    <item>
      <title>✔ 우아한 테크러닝 3기: React &amp;amp; TypeScript 7회차</title>
      <link>https://haranglog.tistory.com/21</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZDDkm/btqJphSanjE/fuUHJlkBvi2BVXVQaQpR91/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZDDkm/btqJphSanjE/fuUHJlkBvi2BVXVQaQpR91/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZDDkm/btqJphSanjE/fuUHJlkBvi2BVXVQaQpR91/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZDDkm%2FbtqJphSanjE%2FfuUHJlkBvi2BVXVQaQpR91%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;✌ 9월22일 (화) 우아한 테크러닝 3기 7회차 강의 정리&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  7회차 강의 목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;React 에서 컴포넌트 분리&lt;/li&gt;
&lt;li&gt;React &amp;amp; TypeScript &lt;a href=&quot;https://codesandbox.io/s/ordermonitor08-1ttxf?file=/src/index.tsx&quot;&gt;예제&lt;/a&gt; 훑어보기&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  컴포넌트는 어떻게 분할할까?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;컴포넌트가 &lt;b&gt;외부 상태에 의존적인 것과 외부 상태와 의존적인 컴포넌트를 분리하는 하는 것&lt;/b&gt;은 react의 원칙적인 것이다.&lt;/li&gt;
&lt;li&gt;여기서 말하는 외부 상태에 의존적인 것은 &lt;b&gt;비지니스 로직을 의미&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;비지니스 로직은 상태를 변경하면서 어떤 상태를 어디로부터 어떤것을 가지고 올지와 관련된 코드들을 말한다.&lt;br /&gt;그렇기 때문에 비지니스 로직을 가지고 있는 컴포넌트들은 여러가지 단계로 분리하는데 개별 컴포넌트들한테 데이터를 전달하는 코드들은 &lt;code&gt;container&lt;/code&gt; 컴포넌트라고 불리고 이것들을 아래와 같이 &lt;code&gt;containers&lt;/code&gt; 폴더에 따로 분리해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span class=&quot;imageblock&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brisd0/btqJoL0yRM2/t7jO1dv1Nw2Vsym8C0BCl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brisd0/btqJoL0yRM2/t7jO1dv1Nw2Vsym8C0BCl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brisd0/btqJoL0yRM2/t7jO1dv1Nw2Vsym8C0BCl1/img.png&quot; srcset=&quot;https://blog.kakaocdn.net/dn/brisd0/btqJoL0yRM2/t7jO1dv1Nw2Vsym8C0BCl1/img.png&quot;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;props만 받아와 사용하는 컴포넌트들은 비지니스 로직이 없는 컴포넌트로 상태가 존재하지 않다.&lt;br /&gt;그런 컴포넌트들은 위와 같이 &lt;code&gt;components&lt;/code&gt;폴더에 분리하여 작성해준다.&lt;/li&gt;
&lt;li&gt;이 컴포넌트들은 자체적으로 가지고 있는 상태가 있을 수가 있다. (예를 들어 &lt;code&gt;Hooks&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;하지만 중요한 것은 내부적으로 상태를 가지고 있건 말건 그것은 독립적인지 아니면 외부와 커뮤니케이션이 필요한지에 따라서 달라질 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;  그렇다면 어느 시점에 분리를 할까??&lt;br /&gt;코드를 쓰다보면 고민되는 시점이 있다. 그때가 분리해야 하는 시점이다?!&lt;br /&gt;너무 처음부터 자기가 알고있는 작은 단위의 패턴으로 컴포넌트를 분리하면 그 크기가 안됬을 때는 굉장히 피곤하고, 그 볼륨보다는 아키텍처에만 집중하게 되는 상황이 나오게 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  React &amp;amp; TS Boilerplate 예제&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;App&lt;/code&gt; 컴포넌트에서는 아래와 같이 3개의 &lt;code&gt;Container&lt;/code&gt; 컴포넌트를 가지고 있다.
&lt;p&gt;&lt;code&gt;NotificationContainer&lt;/code&gt;, &lt;code&gt;OrderStatusContiner&lt;/code&gt;, &lt;code&gt;MonitorControllerContainer&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import * as React from &quot;react&quot;;

import {
  NotificationContainer,
  OrderStatusContiner,
  MonitorControllerContainer
} from &quot;./containers&quot;;
import { Typography } from &quot;antd&quot;;

import &quot;antd/dist/antd.css&quot;;
import &quot;./sass/main.scss&quot;;

export default class App extends React.PureComponent {
  render() {
    return (
      &amp;lt;div&amp;gt;
        &amp;lt;NotificationContainer /&amp;gt;
        &amp;lt;header&amp;gt;
          &amp;lt;Typography.Title&amp;gt;React &amp;amp; TS Boilerplate&amp;lt;/Typography.Title&amp;gt;
        &amp;lt;/header&amp;gt;
        &amp;lt;main&amp;gt;
          &amp;lt;OrderStatusContiner /&amp;gt;
          &amp;lt;MonitorControllerContainer /&amp;gt;
        &amp;lt;/main&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;그리고 &lt;code&gt;containers&lt;/code&gt;에는 &lt;code&gt;index.ts&lt;/code&gt;파일을 가지고 있고, &lt;code&gt;components&lt;/code&gt; 폴더에도 &lt;code&gt;index.ts&lt;/code&gt; 파일을 가지고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;// ../containers/index.ts
export * from &quot;./OrderStatus&quot;;
export * from &quot;./MonitorController&quot;;
export * from &quot;./Notification&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위와 같이 index.ts를 구성하여 사용하면 바깥에서 사용할 때 사용할 컴포넌트를 간단하게 사용할 수 있는 장점을 가진다.&lt;br /&gt;또한, 내부의 정보를 외부에서 감출 수가 있다. (캡슐화)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;autohotkey&quot;&gt;&lt;code&gt;import {
  NotificationContainer,
  OrderStatusContiner,
  MonitorControllerContainer
} from &quot;./containers&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;  React에서 TypeScript&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;컴포넌트의 props의 타입을 지정하는 경우에는 아래와 같은 형식으로 많이 작성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;// OrderStatus.tsx
export interface OrderStatusProps {
    showTimeline: boolean;
    success: number;
    failure: number;
    successTimeline: ITimelineItem[];
    failureTimeline: ITimelineItem[];
}

class OrderStatus extends React.Component&amp;lt;OrderStatusProps&amp;gt; {
// ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위에서 &lt;code&gt;interface&lt;/code&gt;에서 논쟁이 있다.
&lt;p&gt;&lt;code&gt;interface&lt;/code&gt;와 &lt;code&gt;typealias&lt;/code&gt;중에서 무엇을 사용할까??  &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  Interface와 Type Alias&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TypeScript&lt;/code&gt;에서 &lt;code&gt;type&lt;/code&gt;을 기술하는 두 가지 방법으로 &lt;code&gt;Interface&lt;/code&gt;와 &lt;code&gt;Type Alias&lt;/code&gt;로 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;type Person = {
    name: string;
    age: number;
    job?: [];
};

interface Human {
    name: string;
    age: number;
    job?: [];
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이 둘은 &lt;b&gt;컴파일 타임&lt;/b&gt;에 적용되는 스펙이다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/play/&quot;&gt;typescript playground&lt;/a&gt;에서 트랜스 파일링한 결과는 아무것도 나오지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgifm4/btqJlr9CI8u/KEzSXWUe2o9vyoXFLry7bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgifm4/btqJlr9CI8u/KEzSXWUe2o9vyoXFLry7bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgifm4/btqJlr9CI8u/KEzSXWUe2o9vyoXFLry7bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgifm4%2FbtqJlr9CI8u%2FKEzSXWUe2o9vyoXFLry7bk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;아래의 코드는 사용자가 어떤 값을 입력할지 예측할 수 없다.&lt;br /&gt;또한, 이 타입(&lt;code&gt;Person&lt;/code&gt;)이라고 하는 것은 결국 JavaScript로 변환할 때 작동하는 메커니즘이기 때문에 &lt;b&gt;런타임에 타입을 검사하는 것은 존재하지 않는다.&lt;/b&gt;&lt;br /&gt;그렇기 때문에 &lt;b&gt;TypeScript로 작성했다고 해서 모든 타입에러를 잡는 것은 아니고&lt;/b&gt;, 어떤 것이 런타임인지 컴파일 타임인지를 명확하게 알고 있지 않으면 어떤 상황이 버그로 예측하기 힘들다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type Person = {
    name: string;
    age: number;
    job?: [];
};

const p: Person = JSON.parse(prompt(&quot;객체를 입력해주세요&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;그래서 위 코드는 오류 처리가 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Interface와 Type Alias의 두개는 거의 기능적으로는 유사하다.&lt;/li&gt;
&lt;li&gt;Interface는 실제로 &lt;b&gt;상속을 지원&lt;/b&gt;하지만 유니온 타입은 지원하지 않는다.&lt;/li&gt;
&lt;li&gt;Type Alias는 &lt;b&gt;유니온 타입&lt;/b&gt;을 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;type box = number | string;
let b: box = 1;
b = &quot;box&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;현재 대세적으로는 type을 많이 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  React Component의 class OOP(객체 지향 프로그래밍)을 따를까?&lt;/h4&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class OrderStatus extends React.Component&amp;lt;OrderStatusProps&amp;gt; {
  // 생략..
  render() {
    // 생략..
  }
}

export const OrderStatusContiner = connect(mapStateToProps)(OrderStatus);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;react&lt;/code&gt; 컴포넌트의 &lt;code&gt;class&lt;/code&gt;는 &lt;code&gt;OOP&lt;/code&gt;적인 성격은 가지고 있지 않다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;react class&lt;/code&gt;는 &lt;code&gt;OOP&lt;/code&gt;를 구성하기 위해서 만든 것이 아니다.&lt;/li&gt;
&lt;li&gt;컴포넌트 &lt;code&gt;Life Cycle&lt;/code&gt;를 만들기 위한 도구에 지나지 않는다.&lt;br /&gt;그래서 &lt;code&gt;react&lt;/code&gt;로 프로그래밍을 할 때 컴포넌트를 만들기 위한 &lt;code&gt;class&lt;/code&gt;는 단 한번도 &lt;code&gt;new&lt;/code&gt;를 하지않는다. (인스턴스를 만들지 않는다.)&lt;br /&gt;&lt;code&gt;react&lt;/code&gt;가 다 알아서 하기 때문에 &lt;code&gt;class&lt;/code&gt;안에 있는 것들은 전부다 &lt;code&gt;public method&lt;/code&gt;일 수 밖에 없다.(밖으로 다 들어나야되기 때문)&lt;/li&gt;
&lt;li&gt;심지어 요즘에는 함수형 컴포넌트를 지원하기 때문에 더욱 더 &lt;code&gt;react&lt;/code&gt;로 만든 앱에 &lt;code&gt;oop&lt;/code&gt;성격은 굉장히 약화되어있다.&lt;br /&gt;그런 측면에서 typescript 생각해보면 그렇게 많이 기능을 쓸것은 없다. 특히 &lt;code&gt;redux&lt;/code&gt;와 &lt;code&gt;saga&lt;/code&gt;의 조합이면 더 더욱 쓸 것은 줄어든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  TypeScript에서 제네릭이란?&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;제네릭 &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/generics.html&quot;&gt;공식문서 참고&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;아래와 같이 &lt;code&gt;any&lt;/code&gt;로 모든 타입을 지정할 수 있다.(좋지 않다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function identity(arg: any): any {
  return arg;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;아래의 &lt;code&gt;T&lt;/code&gt;라고 하는 것을 규정을 하고 &lt;code&gt;arg&lt;/code&gt;가 &lt;code&gt;T&lt;/code&gt;라고 하는 것을 호출되는 시점에 결정이 된다.&lt;br /&gt;만약에 &lt;code&gt;T&lt;/code&gt;가 &lt;code&gt;String&lt;/code&gt; 타입으로 규정하면 &lt;code&gt;arg&lt;/code&gt;는 &lt;code&gt;String&lt;/code&gt;으로 넘겨주고 &lt;code&gt;String&lt;/code&gt;으로 리턴되게 된다.&lt;br /&gt;이러한 &lt;b&gt;동적타입&lt;/b&gt;을 제네릭이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;function identity&amp;lt;T&amp;gt;(arg: T): T {
  return arg;
}

let output = identity&amp;lt;string&amp;gt;(&quot;myString&quot;);
//       ^ = let output: string&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Typescript의 제네릭은 &lt;b&gt;컴파일 타임&lt;/b&gt;에 동작하는 코드이다.&lt;br /&gt;TypeScript playground에서 확인하면 아무것도 결과에 나오지가 않는다. (자바스크립트관점에서)&lt;/li&gt;
&lt;li&gt;리액트 컴포넌트의 제네릭은 아래와 같이 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class OrderStatus extends React.Component&amp;lt;OrderStatusProps&amp;gt; {
  // 생략..
  render() {
    // 생략..
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;P&lt;/code&gt;는 컴포넌트의 &lt;code&gt;props&lt;/code&gt;, &lt;code&gt;S&lt;/code&gt;는 컴포넌트 내부의 &lt;code&gt;state&lt;/code&gt;를 규정하고 &lt;code&gt;SS&lt;/code&gt;는 &lt;code&gt;snapshot&lt;/code&gt;(잘 사용하지 않는다.)을 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;class React.Component&amp;lt;P = {}, S = {}, SS = any&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;  react-redux를 사용하여 redux와 컴포넌트 연결하기&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;옛날 버전의 &lt;code&gt;react-redux&lt;/code&gt;는 컴포넌트와 &lt;code&gt;redux&lt;/code&gt;를 연결하기 위해 &lt;code&gt;connect&lt;/code&gt; 헬퍼 함수를 사용했다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;connect&lt;/code&gt; 함수는 &lt;a href=&quot;https://ko.reactjs.org/docs/higher-order-components.html&quot;&gt;HoC(High order Component)&lt;/a&gt;(함수를 받고 함수를 리턴) 형태로 작성되어 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;connect&lt;/code&gt;는 호출을 두 번 한다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;&lt;code&gt;connect&lt;/code&gt;가 &lt;code&gt;return&lt;/code&gt;하는 함수*&lt;/i&gt;는 &lt;code&gt;connect&lt;/code&gt;가 최초 호출할 때 받는 &lt;code&gt;mapStateToProps&lt;/code&gt; 객체가 하는 일을 클로저로 가지고 있다가 &lt;code&gt;connect&lt;/code&gt;의 두번째 &lt;code&gt;OrderStatus&lt;/code&gt; 함수한테 &lt;code&gt;property&lt;/code&gt;로 주입시키는 역할을 한다.&lt;br /&gt;&lt;code&gt;OrderStatus&lt;/code&gt;라는 함수는 &lt;code&gt;store&lt;/code&gt;에 구독받는 형식으로 바뀌게 되고 그 바뀐 형태의 컴포넌트를 &lt;code&gt;export&lt;/code&gt; 해주는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;// ...
import { connect } from &quot;react-redux&quot;;
// ...

// StoreState에서 필요한것만 뽑아서 connect함수로 주면 OrderStatus로 주입을 해준다.
const mapStateToProps = (state: StoreState) =&amp;gt; {
  return {
    showTimeline: state.showTimeline,
    success: state.success,
    failure: state.failure,
    successTimeline: state.successTimeline,
    failureTimeline: state.failureTimeline
  };
};

class OrderStatus extends React.Component&amp;lt;OrderStatusProps&amp;gt; {
 // ... 생략
}

export const OrderStatusContiner = connect(mapStateToProps)(OrderStatus);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;지금은 react-redux에서도 함수형 컴포넌트를 사용하기 위해 &lt;a href=&quot;https://react-redux.js.org/api/hooks&quot;&gt;&lt;code&gt;Hooks&lt;/code&gt;&lt;/a&gt; 지원해서 쉽게 사용이 가능하다.&lt;/li&gt;
&lt;li&gt;아래와 같이 &lt;code&gt;useSelector&lt;/code&gt;를 사용하면 &lt;code&gt;store&lt;/code&gt;에 있는 데이터를 가져올 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React from 'react'
import { useSelector } from 'react-redux'

export const CounterComponent = () =&amp;gt; {
  // 함수를 전달
  const counter = useSelector(state =&amp;gt; state.counter)
  return &amp;lt;div&amp;gt;{counter}&amp;lt;/div&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;useDispatch&lt;/code&gt; 사용하면 언제든지 디스패치를 꺼내서 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React from 'react'
import { useDispatch } from 'react-redux'

export const CounterComponent = ({ value }) =&amp;gt; {
  const dispatch = useDispatch()

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;span&amp;gt;{value}&amp;lt;/span&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; dispatch({ type: 'increment-counter' })}&amp;gt;
        Increment counter
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;useSelector&lt;/code&gt;와 &lt;code&gt;useDispatch&lt;/code&gt;를 사용하므로써 간단하게 &lt;code&gt;connect&lt;/code&gt;함수를 대신하여 &lt;code&gt;store&lt;/code&gt;에 있는 데이터를 가져올 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  axios를 사용한 API 통신&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;API 메시지 &lt;code&gt;response&lt;/code&gt;에 대한 규격을 &lt;code&gt;interface&lt;/code&gt;를 하고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import axios, { AxiosResponse, AxiosError } from &quot;axios&quot;;
import endpoint from &quot;./endpoint.config&quot;;

// 성공
interface IApiSuccessMessage {
  status: string;
}

// 에러
interface IApiError {
  status: string;
  statusCode: number;
  errorMessage: string;
}

export class ApiError implements IApiError {
  status: string = &quot;&quot;;
  statusCode: number = 0;
  errorMessage: string = &quot;&quot;;

  constructor(err: AxiosError) {
    this.status = err.response.data.status;
    this.statusCode = err.response.status;
    this.errorMessage = err.response.data.errorMessage;
  }
}

interface INumberOfSuccessfulOrderResponse extends IApiSuccessMessage {
  result: {
    success: number;
  };
}

interface INumberOfFailedOrderResponse extends IApiSuccessMessage {
  result: {
    failure: number;
  };
}

interface IOrderTimelineResponse extends IApiSuccessMessage {
  results: {
    successTimeline: [];
    failureTimeline: [];
  };
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;실제로 각각 사용하기에는 불편하기 때문에 아래와 같이 함수로 Promis 형태로 작성했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export function fetchNumberOfSuccessfulOrder(): Promise&amp;lt;
  INumberOfSuccessfulOrderResponse
&amp;gt; {
  return new Promise((resolve, reject) =&amp;gt; {
    // axios 랩핑한 것
    axios
      .get(endpoint.orders.request.success({ error: true }))
      .then((resp: AxiosResponse) =&amp;gt; resolve(resp.data))
      .catch((err: AxiosError) =&amp;gt; reject(new ApiError(err)));
  });
}

export function fetchNumberOfFailedOrder(): Promise&amp;lt;
  INumberOfFailedOrderResponse
&amp;gt; {
  return new Promise((resolve, reject) =&amp;gt; {
    // axios 랩핑한 것
    axios
      .get(endpoint.orders.request.failure())
      .then((resp: AxiosResponse) =&amp;gt; resolve(resp.data))
      .catch((err: AxiosError) =&amp;gt; reject(new ApiError(err)));
  });
}

export function fetchOrderTimeline(
  date: string
): Promise&amp;lt;IOrderTimelineResponse&amp;gt; {
  return new Promise((resolve, reject) =&amp;gt; {
    axios
      .get(endpoint.orders.request.timeline(date))
      .then((resp: AxiosResponse) =&amp;gt; resolve(resp.data))
      .catch((err: AxiosError) =&amp;gt; reject(new ApiError(err)));
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;아래는 실제로 &lt;code&gt;axios.get&lt;/code&gt;을 하면 &lt;code&gt;endpoint&lt;/code&gt;(URI)를 빌드하는 것이다.&lt;/li&gt;
&lt;li&gt;API가 많아지면 여기에 생기게 된다.&lt;/li&gt;
&lt;li&gt;반복적으로 처리하지 않기 위해 따로 분리하여 아래와 같이 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;interface Config {
  orders: {
    request: {
      success(options: { error?: boolean }): string;
      failure(): string;
      timeline(date: string): string;
    };
  };
}

// process.env.production 분기!!
const config: Config = {
  orders: {
    request: {
      success: ({ error = false }) =&amp;gt;
        `${SERVER}/${API_PREFIX}/orders/request/success${
          error ? &quot;?error=random&quot; : &quot;&quot;
        }`,
      failure: () =&amp;gt; `${SERVER}/${API_PREFIX}/orders/request/failure`,
      timeline: date =&amp;gt; `${SERVER}/${API_PREFIX}/orders/request/all/${date}`
    }
  }
};

export default config;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;  7회차 후기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;이번시간은 자세히는 아니지만 typescript를 사용하여 react를 작성하는 것을 배웠다.&lt;br /&gt;typescript와 더 친해지고 싶다... 공부가 많이 필요할 듯하다.&lt;br /&gt;이제 1회차면 1달간 진행했던 우아한 테크러닝 3기도 마무리가 된다.  &lt;br /&gt;마지막까지 잘듣고 블로깅해야겠다.  &lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>우아한 테크러닝 3기</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/21</guid>
      <comments>https://haranglog.tistory.com/21#entry21comment</comments>
      <pubDate>Wed, 23 Sep 2020 22:42:50 +0900</pubDate>
    </item>
    <item>
      <title>✔ 우아한 테크러닝 3기: React &amp;amp; TypeScript 6회차</title>
      <link>https://haranglog.tistory.com/20</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2.jpg&quot; data-origin-width=&quot;1671&quot; data-origin-height=&quot;1546&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkl3RE/btqI58Bi4H0/4Khqm0LM2PgqCsd0z6iyt1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkl3RE/btqI58Bi4H0/4Khqm0LM2PgqCsd0z6iyt1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkl3RE/btqI58Bi4H0/4Khqm0LM2PgqCsd0z6iyt1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbkl3RE%2FbtqI58Bi4H0%2F4Khqm0LM2PgqCsd0z6iyt1%2Fimg.jpg&quot; data-filename=&quot;2.jpg&quot; data-origin-width=&quot;1671&quot; data-origin-height=&quot;1546&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;✌ 9월17일 (목) 우아한 테크러닝 3기 6회차 강의 정리&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  6회차 강의 목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;webpack&lt;/code&gt;에 대한 흐름 이해하기&lt;/li&gt;
&lt;li&gt;React &amp;amp; Typescript 살펴보기&lt;/li&gt;
&lt;li&gt;redux-saga 사용하기&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  Webpack 흐름 이해하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Webpack은 &lt;a href=&quot;https://www.npmjs.com/package/webpack-cli&quot;&gt;Webpack Cli&lt;/a&gt;라고 하는 도구가 존재한다.&lt;/li&gt;
&lt;li&gt;단지 &lt;code&gt;Hello world&lt;/code&gt;를 찍기 위해서 필요한 설정들이 이렇게나 많이 필요하다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@babel/core&lt;/code&gt;과 같은 &lt;code&gt;@&lt;/code&gt;가 붙여져 있는 것들은 트랜스파일링 할때 사용된다. 일종의 플로그인이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span class=&quot;imageblock&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ee6daL/btqI9pPTfKi/3GwQm1LXvkSAmkTM7VEEDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ee6daL/btqI9pPTfKi/3GwQm1LXvkSAmkTM7VEEDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ee6daL/btqI9pPTfKi/3GwQm1LXvkSAmkTM7VEEDK/img.png&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ee6daL/btqI9pPTfKi/3GwQm1LXvkSAmkTM7VEEDK/img.png&quot;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;webpack&lt;/code&gt;은 &lt;code&gt;webpack.config.js&lt;/code&gt;라는 이름을 갖는 설정 파일을 갖는다. 이름 변경될 수 있지만 관례적으로 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;webpack&lt;/code&gt;은 &lt;code&gt;webpack.config.js&lt;/code&gt;읽어서 실행시켜주는데 실행시켜줄 때 &lt;code&gt;config&lt;/code&gt;객체를 export하게 된다. 그 때 &lt;code&gt;webpack&lt;/code&gt;은 받아서 &lt;code&gt;config&lt;/code&gt;에 있는 내용대로 실행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const config = {
...
}
module.exports = config;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;webpack은 기본적으로 node에서 실행되기 때문에 &lt;code&gt;require&lt;/code&gt;를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const webpack = require(&amp;quot;webpack&amp;quot;);
const path = require(&amp;quot;path&amp;quot;);
const HtmlWebpackPlugin = require(&amp;quot;html-webpack-plugin&amp;quot;);
const { CleanWebpackPlugin } = require(&amp;quot;clean-webpack-plugin&amp;quot;);
const { TsconfigPathsPlugin } = require(&amp;quot;tsconfig-paths-webpack-plugin&amp;quot;);&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://webpack.js.org/&quot;&gt;webpack 공식문서&lt;/a&gt;를 참고하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  Loader&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://webpack.js.org/contribute/writing-a-loader/&quot;&gt;&lt;code&gt;Loader&lt;/code&gt;&lt;/a&gt;는 리덕스의 미들웨어와 같은 역할을 한다고 생각하면 된다.&lt;/li&gt;
&lt;li&gt;첫번째 파일 &lt;code&gt;entry&lt;/code&gt; 부터 읽어들이는 파일들을 &lt;code&gt;loader&lt;/code&gt;한테 던저주고 &lt;code&gt;loader&lt;/code&gt;가 역할을 수행하고 다시 리턴을 해주는 것을 반복한다. (미들웨어의 동작과 비슷)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const config = {
  ...
  // entry
  entry: {
    main: [&amp;quot;core-js&amp;quot;, `./src/index.tsx`],
  },
  ...
};&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;loader 설정은 &lt;code&gt;module&lt;/code&gt; 속성 안에 작성되고 &lt;code&gt;rules&lt;/code&gt;라는 배열안에 객체로 작성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const config = {
  // ...
  module: {
    rules: [
      {
        // 어떤 확장자를 가진 파일만 받을 껀지 정규표현식으로 작성할 수 있다.
        test: /\.(ts|js)x?$/,
        // 어떤 것을 포함시킬지
        include: path.resolve(&amp;quot;src&amp;quot;),
        // 어떤 것을 제외 시킬지
        exclude: /node_modules/,
        use: {
          // 위에서 찾은 설정 파일들을 babel-loader 한테 전달해준다.
          loader: &amp;quot;babel-loader&amp;quot;,
        },
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        loader: &amp;quot;file-loader&amp;quot;,
        options: {
          name: &amp;quot;[name].[ext]&amp;quot;,
          outputPath: &amp;quot;./images/&amp;quot;,
        },
      },
    ],
  },
  // ...
};&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;  Plugin&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://webpack.js.org/contribute/writing-a-plugin/&quot;&gt;Plugin&lt;/a&gt;은 loader보다 훨씬 복잡하다.&lt;br&gt;복잡한 이유는 &lt;code&gt;webpack&lt;/code&gt;의 안쪽에 있는 Low-Level의 기능들을 &lt;code&gt;plugin&lt;/code&gt;한테 다 노출을 시켜주고 &lt;code&gt;plugin&lt;/code&gt;이 할 수 있게 한다.&lt;br&gt;그렇기 때문에 훨씬 더 많은 일을 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const config = {
  // ...
  plugins: [
    new webpack.SourceMapDevToolPlugin({}),
    new CleanWebpackPlugin(),
    // Html 플러그인으로 작성
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, &amp;quot;public/index.html&amp;quot;),
    }),
    // 환경변수값을 주입하는 플러그인
    new webpack.DefinePlugin({
      &amp;quot;process.env.NODE_ENV&amp;quot;: JSON.stringify(process.env.NODE_ENV),
    }),
  ],
  // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;보통의 plugin은 loader의 프로세스가 다 끝나고 실행된 다음에 그 결과물을 가지고 plugin이 작업을 한다.&lt;/li&gt;
&lt;li&gt;그렇기 때문에 &lt;code&gt;loader&lt;/code&gt;는 컨버팅이고 &lt;code&gt;plugin&lt;/code&gt;은 후처리라고 생각하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  각각의 webpack 설정 파일들을 쪼개는 이유는??&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;babel&lt;/code&gt;도 설정파일을 따로 생성해 사용한다. (&lt;code&gt;.babelrc&lt;/code&gt;)&lt;br&gt;애플리케이션을 만들때 관심사 분리라는 맥락으로 컴포넌트를 잘게 쪼개는데 &lt;code&gt;webpack&lt;/code&gt; 설정 파일들도 똑같다.&lt;br&gt;&lt;code&gt;plugins&lt;/code&gt;하나에 하나의 일, &lt;code&gt;loader&lt;/code&gt;도 하나에 하나의 일을 하게 디자인하는게 유지보수에 좋다라고 되어있다.&lt;br&gt;그래서 종류가 굉장히 많다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;  Babel?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://haranglog.tistory.com/15?category=941274&quot;&gt;3회차 때 진행했던 react 만들어보기&lt;/a&gt;에서 &lt;a href=&quot;https://babeljs.io/&quot;&gt;Babel&lt;/a&gt;에서 &lt;code&gt;/* @jsx createElement */&lt;/code&gt; 처럼 사용해서 jsx의 태그 방식을 파싱해서 함수 호출 방식으로 트랜스파일링 해주는 것을 담당해주는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF7qnX/btqI5S6O2Qp/EUHtrkRkfUkT5MH3X9lGGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF7qnX/btqI5S6O2Qp/EUHtrkRkfUkT5MH3X9lGGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF7qnX/btqI5S6O2Qp/EUHtrkRkfUkT5MH3X9lGGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF7qnX%2FbtqI5S6O2Qp%2FEUHtrkRkfUkT5MH3X9lGGk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.babelrc&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &amp;quot;sourceType&amp;quot;: &amp;quot;unambiguous&amp;quot;,
    &amp;quot;presets&amp;quot;: [
        [
            &amp;quot;@babel/preset-env&amp;quot;,
            {
                &amp;quot;modules&amp;quot;: false,
                &amp;quot;useBuiltIns&amp;quot;: &amp;quot;usage&amp;quot;,
                &amp;quot;corejs&amp;quot;: {
                    &amp;quot;version&amp;quot;: 3,
                    &amp;quot;proposals&amp;quot;: true
                }
            }
        ],
        // jsx의 태그 방식을 파싱해서 함수 호출 방식으로 트랜스파일링 해주는 것을 담당
        &amp;quot;@babel/preset-react&amp;quot;,
        &amp;quot;@babel/preset-typescript&amp;quot;
    ],
    &amp;quot;plugins&amp;quot;: [
        &amp;quot;@loadable/babel-plugin&amp;quot;,
        &amp;quot;babel-plugin-styled-components&amp;quot;,
        [
            &amp;quot;@babel/transform-runtime&amp;quot;,
            {
                &amp;quot;corejs&amp;quot;: 3
            }
        ],
        &amp;quot;@babel/plugin-proposal-optional-chaining&amp;quot;,
        [&amp;quot;@babel/plugin-proposal-decorators&amp;quot;, { &amp;quot;legacy&amp;quot;: true }],
        &amp;quot;@babel/plugin-proposal-class-properties&amp;quot;,
        &amp;quot;@babel/plugin-transform-exponentiation-operator&amp;quot;,
        &amp;quot;@babel/proposal-object-rest-spread&amp;quot;,
        &amp;quot;@babel/plugin-transform-async-to-generator&amp;quot;
    ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;결국 &lt;code&gt;Babel&lt;/code&gt;안에 &lt;code&gt;plugin&lt;/code&gt;이다. &lt;code&gt;Babel&lt;/code&gt; 자체도 각종 언어 종류를 트랜스파일링하는 구조들을 또 &lt;code&gt;plugin&lt;/code&gt;이라는 형식으로 가지고 있다. 그러니 &lt;code&gt;Babel&lt;/code&gt;이 다해주는 것이 아니고 &lt;code&gt;Babel&lt;/code&gt;안에 또 &lt;code&gt;plugin&lt;/code&gt;들이 존재하는 구조이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;  인프런 강의 듣고 &lt;code&gt;Webpack&lt;/code&gt;에 대해서 정리한 것입니다. &lt;a href=&quot;https://github.com/saseungmin/Webpack-Babel-Lint-tutorial&quot;&gt;참고&lt;/a&gt;&lt;br&gt;  &lt;a href=&quot;https://joshua1988.github.io/webpack-guide/&quot;&gt;캡틴 판교님의 webpack 핸드북&lt;/a&gt;&lt;br&gt;  인프런 강의하신 &lt;a href=&quot;https://jeonghwan-kim.github.io/series/2019/12/09/frontend-dev-env-npm.html&quot;&gt;김정환님의 블로그&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  React &amp;amp; Typescript 살펴보기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;김민태님 CodeSandBox 참고 &lt;a href=&quot;https://codesandbox.io/s/ordermonitor04-n51jrkq2wl?file=/src/index.tsx&quot;&gt;예제&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;redux&lt;/code&gt;는 특정 프레임워크나 라이브러리에 종속되어있는 상태관리자가 아니다. 그래서 &lt;code&gt;vanilla.js&lt;/code&gt;에서 사용도 가능하고 원한다면 &lt;code&gt;Vue.js&lt;/code&gt;, &lt;code&gt;Angular.js&lt;/code&gt;에서도 사용이 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import { createStore, applyMiddleware } from &amp;quot;redux&amp;quot;;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;그렇기 때문에 &lt;code&gt;react&lt;/code&gt;에서 직접 사용할려면 &lt;code&gt;react-redux&lt;/code&gt;를 사용하면 된다.&lt;/li&gt;
&lt;li&gt;리액트의 Context API&lt;code&gt;를 이용해서 내부적으로 구현된&lt;/code&gt;Provider`를 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;App /&amp;gt;&lt;/code&gt;를 &lt;code&gt;Provider&lt;/code&gt;로 감싸주고 &lt;code&gt;store&lt;/code&gt;를 주입시켜주면 &lt;code&gt;App&lt;/code&gt; 하위 어디서든 꺼내서 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import { Provider } from &amp;quot;react-redux&amp;quot;;
//...
render(
  &amp;lt;Provider store={store}&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/Provider&amp;gt;,
  rootElement
);&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;reducer&lt;/code&gt; 상태 값을 나타낼 인터페이스이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// types
export interface StoreState {  // typealias으로 변경해도 상관없다.
    monitoring: boolean;
    success: number;
    failure: number;
}
// reducers
const initializeState: StoreState = {
  monitoring: false,
  success: 0,
  failure: 0
};
export default (
  state: StoreState = initializeState,
) =&amp;gt; {
// ...
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;  typesafe-actions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;typesafe-actions&lt;/code&gt; 라이브러리의 &lt;code&gt;createAction&lt;/code&gt;을 사용해서 &lt;code&gt;actions&lt;/code&gt;을 생성할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import { createAction } from &amp;quot;typesafe-actions&amp;quot;;

export const startMonitoring = createAction(
  // action 이름(속성) 상수 선언이 필요없다.
  // 타입 속성에 넣는다.
  &amp;quot;@command/monitoring/start&amp;quot;,
  resolve =&amp;gt; {
    return () =&amp;gt; resolve();
  }
);

export const stopMonitoring = createAction(
  &amp;quot;@command/monitoring/stop&amp;quot;,
  resolve =&amp;gt; {
    return () =&amp;gt; resolve();
  }
);

export const fetchSuccess = createAction(&amp;quot;@fetch/success&amp;quot;, resolve =&amp;gt; {
  return () =&amp;gt; resolve();
});

export const fetchFailure = createAction(&amp;quot;@fetch/failure&amp;quot;, resolve =&amp;gt; {
  return () =&amp;gt; resolve();
});&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;그래서 &lt;code&gt;typesafe-actions&lt;/code&gt;를 사용하면 위와 같이 &lt;code&gt;createAction&lt;/code&gt; 첫 번째 인자로 상수를 사용하지 않고 문자열로 넘길 수가 있다. 대신 상수역할을 해준다.&lt;/li&gt;
&lt;li&gt;위에서 &lt;code&gt;createAction&lt;/code&gt;의 두 번째 인자는 데이터가 존재하면 &lt;code&gt;payload&lt;/code&gt;라는 속성으로 객체로 데이터를 넣어주고 어떤 타입인지를 감지하고 타이핑까지 해줘서 아래의 &lt;code&gt;reducer&lt;/code&gt; 에서 액션에서 어떤 속성인지 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;typesafe-actions&lt;/code&gt; 사용하면 상수선언이 생략되고 type과 payload 만드는 한꺼번에 처리를 해줘서 편리하게 처리가 가능하다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;typesafe-actions&lt;/code&gt;과 비슷한 역할을 하는 &lt;a href=&quot;https://redux-toolkit.js.org/&quot;&gt;&lt;code&gt;redux-toolkit&lt;/code&gt;&lt;/a&gt;도 존재하며 리덕스에서 사용하라고 공식문서에도 나온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;export default (
  state: StoreState = initializeState,
  action: ActionType&amp;lt;typeof Actions&amp;gt;
) =&amp;gt; {
  switch (action.type) {
    case getType(Actions.fetchSuccess):
      return {
        ...state,
        success: state.success + Math.floor(Math.random() * (100 - 1) + 1)
      };
    case getType(Actions.fetchFailure):
      return {
        ...state,
        failure: state.failure + Math.floor(Math.random() * (2 - 0))
      };
    default:
      console.log(action.type);
      return Object.assign({}, state);
  }
};&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;  redux-saga&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;현재 미들웨어로 &lt;code&gt;redux-saga&lt;/code&gt;를 사용하고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// index.tsx
import * as React from &amp;quot;react&amp;quot;;
import { render } from &amp;quot;react-dom&amp;quot;;
import { Provider } from &amp;quot;react-redux&amp;quot;;
import { createStore, applyMiddleware } from &amp;quot;redux&amp;quot;;
import { StoreState } from &amp;quot;./types&amp;quot;;
import reducer from &amp;quot;./reducers&amp;quot;;
import createSagaMiddleware from &amp;quot;redux-saga&amp;quot;;
import rootSaga from &amp;quot;./sagas&amp;quot;;
import App from &amp;quot;./App&amp;quot;;

const sagaMiddleware = createSagaMiddleware();
// sagaMiddleware
const store: StoreState = createStore(reducer, applyMiddleware(sagaMiddleware));
const rootElement: HTMLElement = document.getElementById(&amp;quot;root&amp;quot;);

// rootSaga 미들웨어 전달
sagaMiddleware.run(rootSaga);

render(
  &amp;lt;Provider store={store}&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/Provider&amp;gt;,
  rootElement
);&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;redux-saga&lt;/code&gt;는 비동기 처리를 위해서 &lt;strong&gt;제너레이터&lt;/strong&gt;를 사용한다.&lt;br&gt;제너레이터와 비동기에 대한 설명은 &lt;a href=&quot;https://haranglog.tistory.com/16&quot;&gt;우아한 테크러닝 4회차&lt;/a&gt;때 설명하셨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// sagas/index.ts
export default function* () {
  yield fork(monitoringWorkflow);
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;redux-saga/effects&lt;/code&gt; 패키지 밑에 있는 헬퍼함수들이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// sagas/index.ts
import { fork, all, take, race, delay, put } from &amp;quot;redux-saga/effects&amp;quot;;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;redux-saga/effects&lt;/code&gt;에서 불러오는 &lt;code&gt;fork, all, take, race, delay, put&lt;/code&gt;들은 객체를 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;//...
function* monitoringWorkflow() {
  while (true) {
    yield take(getType(Actions.startMonitoring));

    let loop = true;

    while (loop) {
      yield all([
        put({ type: getType(Actions.fetchSuccess) }),
        put({ type: getType(Actions.fetchFailure) })
      ]);

      const { stoped } = yield race({
        waitting: delay(200),
        stoped: take(getType(Actions.stopMonitoring))
      });

      if (stoped) {
        loop = false;
      }
    }
  }
}

export default function* () {
  // monitoringWorkflow 함수를 전달
  yield fork(monitoringWorkflow);
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fork(monitoringWorkflow)&lt;/code&gt;를 &lt;code&gt;console.log&lt;/code&gt;를 해보면 객체가 만들어지는 것을 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span class=&quot;imageblock&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ySKUz/btqI4e9R7aV/wuHgkdT0rFpVHQDDIlsrx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ySKUz/btqI4e9R7aV/wuHgkdT0rFpVHQDDIlsrx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ySKUz/btqI4e9R7aV/wuHgkdT0rFpVHQDDIlsrx1/img.png&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ySKUz/btqI4e9R7aV/wuHgkdT0rFpVHQDDIlsrx1/img.png&quot;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;put&lt;/code&gt; 함수는 액션을 &lt;code&gt;dispatch&lt;/code&gt;하는 것을 &lt;code&gt;redux-saga&lt;/code&gt;에게 시키는 것이고 &lt;code&gt;all&lt;/code&gt;을 사용하면 여러개의 액션을 &lt;code&gt;dispatch&lt;/code&gt;할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;take&lt;/code&gt;는 리덕스의 액션을 주고(문자열) 이 문자열에 액션이 들어오면 알려달라고 &lt;code&gt;next&lt;/code&gt;를 던져준다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;race&lt;/code&gt;는 여러개의 객체로 전달해도 상관 없고, 그 안에 객체 중에 제일 먼저 돌아오는 상태 값을 넘긴다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delay&lt;/code&gt;는 Promise객체이고 특정 시간동안 기달리고 다시 실행되게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const { stoped } = yield race({
  waitting: delay(200),
  stoped: take(getType(Actions.stopMonitoring))
});&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;redux-saga의 자세한 내용은 &lt;a href=&quot;https://redux-saga.js.org/&quot;&gt;공식문서&lt;/a&gt; 참고&lt;br&gt;&lt;a href=&quot;https://mskims.github.io/redux-saga-in-korean/&quot;&gt;번역본도 존재.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  6회차 후기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;벌써 다음주면 마지막 주차이다..  마지막 시간은 치킨 뜯으면서 해보자는데 진짜일까? ㅋㅋㅋ&lt;br&gt;3주동안 정말 리액트를 까보면서 구현해보니까 리액트에 대해서 더 잘 파악할 수 있는 계기가 된거같다.&lt;br&gt;이번주는 진짜? 리액트와 typescript를 사용하면서 구조를 이해해봤다. 또한, redux-saga에 대해서도 다시한번 더 알게된 시간이였다.&lt;br&gt;그리고 redux-toolkit은 다음번에 꼭 사용해보고 싶다. &lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>우아한 테크러닝 3기</category>
      <category>react</category>
      <category>typescript</category>
      <category>우아한 테크러닝</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/20</guid>
      <comments>https://haranglog.tistory.com/20#entry20comment</comments>
      <pubDate>Sun, 20 Sep 2020 20:45:14 +0900</pubDate>
    </item>
    <item>
      <title>✔ 우아한 테크러닝 3기: React &amp;amp; TypeScript 5회차</title>
      <link>https://haranglog.tistory.com/19</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TLy4L/btqI579QmPn/ovg2hKVw3Yp5zYLohyBdr0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TLy4L/btqI579QmPn/ovg2hKVw3Yp5zYLohyBdr0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TLy4L/btqI579QmPn/ovg2hKVw3Yp5zYLohyBdr0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTLy4L%2FbtqI579QmPn%2Fovg2hKVw3Yp5zYLohyBdr0%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;✌ 9월15일 (화) 우아한 테크러닝 3기 5회차 강의 정리&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  5회차 강의 목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;javascript로 만든 Redux 리뷰&lt;/li&gt;
&lt;li&gt;Redux의 비동기&lt;/li&gt;
&lt;li&gt;Redux 미들웨어 알아보기&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  Redux 리뷰&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://haranglog.tistory.com/14&quot;&gt;2회차때 만든 리덕스&lt;/a&gt;를 사용하여 &lt;code&gt;store&lt;/code&gt;와 &lt;code&gt;reducer&lt;/code&gt; 생성 해준다.&lt;/li&gt;
&lt;li&gt;자세한 내용은 위 링크 참조  &lt;/li&gt;
&lt;li&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import { createStore } from &amp;quot;./redux&amp;quot;;

function reducer(state = { counter: 0 }, action) {
  switch (action.type) {
    case &amp;quot;inc&amp;quot;:
      return {
        ...state,
        counter: state.counter + 1
      };
    default:
      return { ...state };
  }
}

const store = createStore(reducer);

store.subscribe(() =&amp;gt; {
  //Object {counter: 1}
  //Object {counter: 2}
  console.log(store.getState());
});

store.dispatch({
  type: &amp;quot;inc&amp;quot;
});

store.dispatch({
  type: &amp;quot;inc&amp;quot;
});&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;redux.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;export function createStore(reducer) {
  let state;
  const listeners = [];
  const getState = () =&amp;gt; ({ ...state });

  const dispatch = (action) =&amp;gt; {
    state = reducer(state, action);
    listeners.forEach((fn) =&amp;gt; fn());
  };

  const subscribe = (fn) =&amp;gt; {
    listeners.push(fn);
  };

  return {
    getState,
    dispatch,
    subscribe
  };
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;index.js&lt;/code&gt;에서 이뤄지는 모든 동작 즉, reducer따로, store 만드는것 따로, subscribe 따로, dispatch가 각각 따로 일어나는데 실제로 &lt;strong&gt;모든 동작은 동기적으로 작동한다.&lt;/strong&gt;&lt;br&gt;그렇기 때문에 &lt;strong&gt;순서가 완전히 보장이된다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그래서 &lt;code&gt;reducer&lt;/code&gt;는 &lt;strong&gt;순수함수&lt;/strong&gt; 여야만한다. (원칙)&lt;br&gt;순수함수란 외부와 아무런 &lt;code&gt;dependency&lt;/code&gt;없이 내부적으로 아무런 &lt;code&gt;side effect&lt;/code&gt; 없이 작동하는 함수이다.&lt;/p&gt;
&lt;p&gt;멱등성: 인풋이 똑같으면 아웃풋도 무조건 같아야되는 성질&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  순수하지 않은 작업은 뭘까??&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;실행될때마다 결과가 일정치 않은&lt;/strong&gt; 작업들을 순수하지 않는 작업이라한다.&lt;br&gt;대표적으로 &lt;strong&gt;비동기 작업&lt;/strong&gt;이고 그중애서도 대표적인 것은 &lt;strong&gt;API호출&lt;/strong&gt;이다.&lt;br&gt;그렇기 때문에 결과 예측이 안된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  그래서 순수하지 않는 상황은 어떻게 다룰까??&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;현재 상태에서는 API를 다루는 것은 불가능하다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;...
// api 받는 함수
function api(url, cb) {
  setTimeout(() =&amp;gt; {
    cb({ type: &amp;quot;응답이야&amp;quot;, data: [] });
  }, 2000);
}
...
function reducer(state = { counter: 0 }, action) {
  switch (action.type) {
    case &amp;quot;inc&amp;quot;:
      return {
        ...state,
        counter: state.counter + 1
      };
    case &amp;quot;fetch-user&amp;quot;:
      api(&amp;quot;/api/v1/users/1&amp;quot;, (users) =&amp;gt; {
        return { ...state, ...users };
      });
      break;
    default:
      return { ...state };
  }
}
...
store.dispatch({
  type: &amp;quot;fetch-user&amp;quot;
})&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Redux&lt;/code&gt;는 모든 로직을 동기적으로 처리하기 때문에 값을 기다리지 않는다.(&lt;code&gt;setTimeout&lt;/code&gt;) 그렇기 때문에 reducer가 호출되고 &lt;code&gt;api&lt;/code&gt;가 호출되고 반환을 하지만 현재 상태에서는 반환한 상태가 없게 된다.&lt;br&gt;그렇기 때문에 store의 상태를 바꾸는 것은 불가능하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;따라서 redux는 비동기 작업을 할 때 &lt;strong&gt;미들웨어&lt;/strong&gt;을 사용한다.&lt;/p&gt;
&lt;h2&gt;  Redux의 미들웨어&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;아래와 같은 형태들로 만들 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const myMiddleware = (store) =&amp;gt; (dispatch) =&amp;gt; (action) =&amp;gt; {
  dispatch(action);
};

// 일반 함수
function yourMiddleware(store) {
  return function (dispatch) {
    return function (action) {
      dispatch(action);
    };
  };
}

function ourMiddleware(store, dispatch, action) {
  dispatch(action);
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;myMiddleware&lt;/code&gt;와 &lt;code&gt;yourMiddleware&lt;/code&gt;는 문법만 다르고 같은 형태이고 &lt;code&gt;ourMiddleware&lt;/code&gt;은 함수가 하나이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 세 함수의 공통점은 실행하는 코드가 같다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다른점은 &lt;code&gt;myMiddleware&lt;/code&gt;,&lt;code&gt;yourMiddleware&lt;/code&gt;와 &lt;code&gt;ourMiddleware&lt;/code&gt;의 다른점은 중첩되어 있는 지점이 다르다.&lt;/p&gt;
&lt;p&gt;인자가 n개인 함수를 인자를 각각 하나씩 쪼갠 함수를 분리하는 기법을 함수형 프로그래밍에서는 &lt;strong&gt;커링&lt;/strong&gt;이라고 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// 커링을 이용한 함수를 이렇게 실행 할 수 있다.
myMiddleware(store)(store.dispatch)({ type: &amp;quot;inc&amp;quot; }); // Object {counter: 1}
yourMiddleware(store)(store.dispatch)({ type: &amp;quot;inc&amp;quot; }); // Object {counter: 2}
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ourMiddleware&lt;/code&gt; 호출 할 땐 인자를 한꺼번에 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;ourMiddleware(store, store.dispatch, { type: &amp;quot;inc&amp;quot; }); // Object {counter: 3}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;  커링을 사용해서 미들웨어을 사용하는 이유는 뭘까??&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;아래와 같이 어떤 타입이 들어왔나 로그를 찍고싶을 때가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;console.log(&amp;#39;action -&amp;gt; { type: &amp;quot;INC&amp;quot; }&amp;#39;);
myMiddleware(store)(store.dispatch)({ type: &amp;quot;inc&amp;quot; }); &lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;그치만 필요할때 마다 작성해줘야 되기 때문에 굉장히 번거롭다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dobbit.github.io/redux/advanced/Middleware.html#attempt-3-%EB%94%94%EC%8A%A4%ED%8C%A8%EC%B9%98-%EB%AA%BD%ED%82%A4%ED%8C%A8%EC%B9%AD%ED%95%98%EA%B8%B0&quot;&gt;Redux의 공식문서&lt;/a&gt;를 참고해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  디스패치 감싸기&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function dispatchAndLog(store, action) {
    console.log(&amp;#39;dispatching&amp;#39;, action);
    store.dispatch(action);
    console.log(&amp;#39;next state&amp;#39;, store.getState());
}
// 로깅함수 호출
dispatchAndLog(store, { type: &amp;quot;inc&amp;quot; });
dispatchAndLog(store, { type: &amp;quot;inc&amp;quot; });&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;이런식으로 로깅을 함수로 뽑아낼 수 있다.&lt;br&gt;&lt;span class=&quot;imageblock&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcGDz9/btqI57PC0TT/fYeoRb7GuhuPqrQXTwVZk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcGDz9/btqI57PC0TT/fYeoRb7GuhuPqrQXTwVZk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcGDz9/btqI57PC0TT/fYeoRb7GuhuPqrQXTwVZk1/img.png&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dcGDz9/btqI57PC0TT/fYeoRb7GuhuPqrQXTwVZk1/img.png&quot;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;하지만 위 상항도 하드코딩이 되어있기 때문에 코드를 수정하는 것은 매우 좋지 못한 방법이다. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  디스패치 몽키패칭하기&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;store.dispatch&lt;/code&gt;를 &lt;code&gt;next&lt;/code&gt;라는 변수를 만들어서 함수 자체를 넣어준다. (원본을 넣는다.) &lt;/li&gt;
&lt;li&gt;그리고 &lt;code&gt;store.dispatch&lt;/code&gt;를 &lt;code&gt;dispatchAndLog&lt;/code&gt; 함수로 바꿔준다.&lt;/li&gt;
&lt;li&gt;실행시간에 필요한 것으로 바꿔주고 필요하지 않아졌을 때 원래대로 복구시켜준다. &lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;let next = store.dispatch; 
// 바깥쪽 store.dispatch 에서는 로깅 찍히는 작업 바꿔준다. 
store.dispatch = function dispatchAndLog(action) { 
    console.log(&amp;#39;dispatching&amp;#39;, action); 
    // 원래의 것으로 dispatch 해준다. 클로저로 가지고 있다. 
    let result = next(action); 
    console.log(&amp;#39;next state&amp;#39;, store.getState()); 
    return result; 
};&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;이제 위와 같이 하면 &lt;code&gt;dispatchAndLog&lt;/code&gt; 함수를 사용하지 않고 &lt;code&gt;dispatch&lt;/code&gt;를 할 수 있게 되었다.&lt;/li&gt;
&lt;li&gt;하지만! &lt;strong&gt;만약 &lt;code&gt;dispatch&lt;/code&gt;에 이런 함수 변환(위 처럼 함수를 바꿔치기 하는 것. 즉, 로깅하는 함수와 또 다른 함수)을 두 개 이상 적용할 때는 어떻게 될까??&lt;/strong&gt; &lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  디스패치 두 개 이상 몽키패칭하기&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;로깅 이 외의 오류를 잡을 때를 콘솔에 값을 출력하는 로직을 추가시켜 보자. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;여기서 중요한 것은 안에 있는 코드가 어떤 코드인지는 중요하지 않다.&lt;/strong&gt;&lt;br&gt;중요한 점은 &lt;strong&gt;함수가 함수를 리턴하고 있고, 바깥 함수와 안쪽 함수가 분리되어있다는 점이다.&lt;/strong&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;patchStoreToAddLogging&lt;/code&gt;와 &lt;code&gt;dispatchAndLog&lt;/code&gt;&lt;br&gt;&lt;code&gt;patchStoreToAddCrashReporting&lt;/code&gt;와 &lt;code&gt;dispatchAndReportErrors&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;function patchStoreToAddLogging(store) { 
    let next = store.dispatch; 
    store.dispatch = function dispatchAndLog(action) { 
        console.log(&amp;#39;dispatching&amp;#39;, action);
        let result = next(action); 
        console.log(&amp;#39;next state&amp;#39;, store.getState()); 
        return result; 
    }; 
} 

function patchStoreToAddCrashReporting(store) {
  let next = store.dispatch;
  store.dispatch = function dispatchAndReportErrors(action) {
    try {
      return next(action);
    } catch (err) {
      console.error(&amp;quot;Caught an exception!&amp;quot;, err);
      Raven.captureException(err, {
        extra: { action, state: store.getState() }
      });
      throw err;
    }
  };
}&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;  여기서 잠깐 커링의 예시&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;add1&lt;/code&gt;함수는 사용자가 최종 계산에 개입할 여지가 전혀 없다. (10 + 20을 변화시킬 수 없다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const add1 = function (a, b) { 
    return a + b; 
}; 

const add2 = function (a) { 
    return function (b) {
        return a + b; 
    }; 
}; 

add1(10, 20); 
add2(10)(20);&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;add2&lt;/code&gt;함수는 사용자가 개입할 여지가 충분히 존재한다.&lt;/li&gt;
&lt;li&gt;그렇기 때문에 &lt;strong&gt;지연효과&lt;/strong&gt;를 줄 수 있다. &lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// 사용자 쪽에서 작업을 할 수있다. 
const addTen = add2(20); 
// do some.. 
addTen(20); 
addTen(120);&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;결국 &lt;strong&gt;커링은 사용자한테 인자와 인자 사이에 개입할 수 있는 여지를 열어 주는 것이다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  몽키패칭 숨기기&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;여기서 본격적인 커링의 모양이 등장한다. &lt;/li&gt;
&lt;li&gt;&lt;code&gt;dispatchAndLog&lt;/code&gt;를 &lt;code&gt;logger&lt;/code&gt;함수로 감싸고 &lt;code&gt;dispatchAndLog&lt;/code&gt;를 리턴한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;function logger(store) {
  let next = store.dispatch;

  // 앞에서:
  // store.dispatch = function dispatchAndLog(action) {

  return function dispatchAndLog(action) {
    console.log(&amp;#39;dispatching&amp;#39;, action);
    let result = next(action);
    console.log(&amp;#39;next state&amp;#39;, store.getState());
    return result;
  };
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;여기서 사용자인 리덕스가 미들웨어로 사용자가 만든 함수를 조작하고 싶은데 &lt;code&gt;add1(10, 20);&lt;/code&gt; 이런식으로 만들면 조작할 수 없으니 리덕스가 개입해서 원하는 형태로 재구성할 수 있는 여지를 주는 커링 테크닉을 사용하는 것이다. &lt;code&gt;add2(10)(20);&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;각각의 미들웨어를 적용시키는 &lt;code&gt;applyMiddlewareByMonkeypatching&lt;/code&gt; 함수를 아래와 같이 작성할 수 있다. &lt;/li&gt;
&lt;li&gt;Redux 안에 실제 몽키패칭을 적용할 수 있게 돕는 헬퍼를 제공할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;function applyMiddlewareByMonkeypatching(store, middlewares) { 
    middlewares = middlewares.slice(); 
    middlewares.reverse(); 
    // 각각의 미들웨어로 디스패치 함수를 변환 
    middlewares.forEach(middleware =&amp;gt; store.dispatch = middleware(store) ); 
} 

// 여러 미들웨어를 적용 
applyMiddlewareByMonkeypatching(store, [logger, crashReporter]);&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;하지만 이건 몽키패칭을 숨기기만하고 아직 사용중이다.&lt;/li&gt;
&lt;li&gt;몽키패칭을 제거하기 위해서 아래와 같이 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// 바깥 
function logger(store) { 
    // 두번째 함수 
    return function wrapDispatchToAddLogging(next) { 
        // 마지막 함수 
        return function dispatchAndLog(action) { 
            // 결국 할려는 것 
            console.log(&amp;#39;dispatching&amp;#39;, action); 
            let result = next(action); 
            console.log(&amp;#39;next state&amp;#39;, store.getState()); 
            return result; 
        }; 
    } 
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;미들웨어가 &lt;code&gt;next()&lt;/code&gt; 디스패치 함수를 &lt;code&gt;store&lt;/code&gt; 인스턴스에서 읽어오는 대신 매개변수로 받을 수 있다.&lt;/li&gt;
&lt;li&gt;화살표 함수를 이용해 커링을 간단하게 나타낼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const logger = store =&amp;gt; next =&amp;gt; action =&amp;gt; { 
    console.log(&amp;#39;dispatching&amp;#39;, action); 
    let result = next(action); 
    console.log(&amp;#39;next state&amp;#39;, store.getState()); 
    return result; 
};&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;결국 최종적으로 마지막 안쪽 함수에만 쓰면된다.&lt;br&gt;등록된 미들웨어들을 순서대로 연결시켜 놓은 구조를 만들 수 있게 리덕스가 할 수 있게 열어놓는 구조를 만들기 위해서 커링 테크닉을 사용한 것이다. &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  Redux에 미들웨어 추가하기&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;미들웨어를 추가해준다. &lt;/li&gt;
&lt;li&gt;위 경우에서는 &lt;code&gt;setTimeout&lt;/code&gt;를 사용한 비동기적 함수는 실행되지 않았다. &lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// index.js 
const logger = (store) =&amp;gt; (next) =&amp;gt; (action) =&amp;gt; { 
    console.log(&amp;quot;logger:&amp;quot;, action.type); 
    next(action); 
}; 

const monitor = (store) =&amp;gt; (next) =&amp;gt; (action) =&amp;gt; { 
    setTimeout(() =&amp;gt; { 
        console.log(&amp;quot;monitor:&amp;quot;, action.type); 
        next(action); 
    }, 2000); 
};&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;미들웨어를 받는 부분을 작성해준다.&lt;br&gt;&lt;code&gt;middlewares&lt;/code&gt;는 미들웨어를 배열로 받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// redux.js 
export function createStore(reducer, middlewares = []) { 
    ... 
    middlewares = Array.from(middlewares).reverse(); 
    let lastDispatch = store.dispatch; 
    middlewares.forEach((middleware) =&amp;gt; { 
        lastDispatch = middleware(store)(lastDispatch); 
    }); 
    return { ...store, dispatch: lastDispatch }; 
    ... 
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;middlewares.reverse();&lt;/code&gt;를 해주는 이유는 &lt;code&gt;middleware(store)(lastDispatch)&lt;/code&gt; 형태로 호출되며 반환값은 계속해서 앞에 미들웨어에 전달된다.&lt;br&gt;그래서 &lt;code&gt;reverse&lt;/code&gt;로 뒤집어서 가장 뒤인 안쪽에서 부터 바깥쪽으로 &lt;code&gt;dispatch&lt;/code&gt;가 전달된다. 그렇기 때문에 최종 실행되는 &lt;code&gt;dispatch&lt;/code&gt; 함수는 기존 맨 처음의 &lt;code&gt;store&lt;/code&gt;의 &lt;code&gt;dispatch&lt;/code&gt; 함수가 되게 된다. &lt;/li&gt;
&lt;li&gt;결과적으로 아래와 같이 콘솔이 찍히고, 미들웨어를 순서를 변경해도 그대로 &lt;strong&gt;동기적&lt;/strong&gt;으로 실행된 결과가 나온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span class=&quot;imageblock&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bISCzW/btqJeudM1vi/VaNNvsbvNuiyNwxKz3tMO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bISCzW/btqJeudM1vi/VaNNvsbvNuiyNwxKz3tMO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bISCzW/btqJeudM1vi/VaNNvsbvNuiyNwxKz3tMO1/img.png&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bISCzW/btqJeudM1vi/VaNNvsbvNuiyNwxKz3tMO1/img.png&quot;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;모든 소스코드는 &lt;a href=&quot;https://github.com/saseungmin/woowa_tech_learning_react_typescript/tree/master/week_3_1&quot;&gt;Github&lt;/a&gt; 참고&lt;br&gt;김민태님 &lt;a href=&quot;https://gist.github.com/ibare/0eb8597551070bf1ebf8e797439913a3&quot;&gt;Gist&lt;/a&gt; 참고&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  5회차 후기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;이번 미들웨어 수업은 이해하기가 어려웠다. 그래도 블로깅을 하면서 이해해볼려고 노력했다.. &lt;br&gt;공부가 많이 필요한 부분인 거 같다.&lt;br&gt;바로바로 블로깅을 했어야 했지만 생각보다 시간적 여유가 부족한거 같다.. 그래도 꼭 8회차까지 다써야겠다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>우아한 테크러닝 3기</category>
      <category>react</category>
      <category>Redux</category>
      <category>리덕스</category>
      <category>리덕스 미들웨어</category>
      <category>리액트</category>
      <category>우아한 테크러닝</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/19</guid>
      <comments>https://haranglog.tistory.com/19#entry19comment</comments>
      <pubDate>Sat, 19 Sep 2020 19:03:01 +0900</pubDate>
    </item>
    <item>
      <title>나는 왜 프론트 앤드를 선택했을까?  </title>
      <link>https://haranglog.tistory.com/17</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clYtAu/btqIxYlZtUi/tyhxJZFjFgOEEscmcT33hK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clYtAu/btqIxYlZtUi/tyhxJZFjFgOEEscmcT33hK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clYtAu/btqIxYlZtUi/tyhxJZFjFgOEEscmcT33hK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclYtAu%2FbtqIxYlZtUi%2FtyhxJZFjFgOEEscmcT33hK%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;내가 왜 &lt;strong&gt;프론트 앤드 관심이 생기고 열심히 하려는지 대해서 나를 다시 돌아볼 필요성&lt;/strong&gt;을 느꼈다.&lt;br&gt;이런 글을 쓰는 것도 재밌을 거 같고 나를 돌아보는 시간이 될 거 같아 써보기로 마음먹었다. ✨&lt;br&gt;마음은 먹지만 글 쓰는 건 의식에 흐름대로 쓸 것이다. 글에 두서가 없고 정신이 없으니 참고. ✌&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  대학 시절 나는?&lt;/h2&gt;
&lt;h3&gt;  컴퓨터공학과를 선택한 이유&lt;/h3&gt;
&lt;p&gt;공부랑은 거리가 멀어 대전의 한 대학교에 입학했고, 무슨 고집인지는 모르겠지만, 과는 컴퓨터공학과 아니면 갈 생각이 없었기 때문에 컴퓨터공학과를 선택하였다.&lt;br&gt;왜 컴퓨터공학과를 가고 싶었냐는 질문에는 그냥 아마 게임을 좋아해서 인 거 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;고등학교 때는?&lt;br&gt;고2 때 롤에 빠져서 정신 못 차렸을 시절이 있었다. 물론 고3 때가 제일 피크였긴 했다. 한창 공부를 많이 할 시간에 롤을 했으니 말이다.&lt;br&gt;이게 어느 정도였느냐면 고등학교를 점심 먹고 수업을 안 듣고 몰래 나가거나(결국, 걸림….) 담임 선생님 책상에서 몰래 조퇴증을 가져와서 글씨 잘 쓰는 친구한테 선생님 글씨처럼 써달라고 해서 매일 야자랑 방과 후 수업을 안 하고 집이 아닌 피시방을 갔다.  &lt;br&gt;사유는 무슨 기술배우는 쪽이나 음악 쪽으로 적으면 앞에서 검사할 때 프리패스였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;하지만 뭐 게임뿐만은 아니었다. 컴퓨터 조립하는 것을 엄청나게 좋아한다. 지금까지도 주변 지인이나 친구들 컴퓨터를 직접 조립하고 싶어서&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;컴퓨터 조립할 땐 누구? 나! 내가 부품만 싸게 사서 다 공짜로 해줄게! 윈도우랑 오피스까지!! 설치비용 안 받고! 그렇지만 밥 한 끼 사줄 수는 있잖아? ✌&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이렇고 다녔다. ㅎ 덕분에 지금은 내가 조립해서 쓰고 있는 친구들만 15명 정도 되는 것 같다. 물론 A/S도 내 담당이지만.. &lt;br&gt;그렇게 조립을 했으니 집에 남는 컴퓨터 부품과 박스들도 있고 조립 실력은 뭐…. ㅎㅎ &lt;del&gt;컴퓨터 수리점이나 차릴까….&lt;/del&gt;&lt;br&gt;&lt;strong&gt;결론은 실제 컴퓨터공학과랑 아무 관련이 없던 것이었다.  &lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;  대학 생활 2년 동안 배운 건?&lt;/h3&gt;
&lt;p&gt;결론부터 얘기하자면 공부적으로 배운 건 &lt;strong&gt;없다.&lt;/strong&gt; 입학 후 군대 가기 전 2년 동안 너무 야무지게 놀아버렸다. ㅎㅎ   분명 내가 가고 싶은 컴퓨터공학과를 갔고 수업도 열심히 들어야지라는 생각은 수업을 들으며 들을수록 흥미를 잃어버렸다. 대부분의 수업이 이론 중심의 수업이 많았기 때문에 흥미를 잃었던 거 같다. &lt;del&gt;학점 경고까지 맞았다는 얘기가….&lt;/del&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;동아리&lt;br&gt;학교에 다니면서 봉사 동아리를 들어갔다.&lt;br&gt;이 동아리 생활이 너무 재밌었다. 봉사활동이 쓰레기 줍고 그런 봉사활동이 아니라 초, 중, 고 애들을 놀러 가면 인솔시키고 관리시키는 대학생 자원봉사자로 나가는 것이다.&lt;br&gt;어쨌든 봉사활동뿐만 아니라 동아리 안에서의 친구들과 선배들 동아리 사람들과 놀았던 것이 잊지 못할 추억 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;del&gt;하늘나라로 간 내 친구 나래! 잘 지내지?? 요즘 못 가서 미안  조만간 찾아갈게&lt;/del&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;배운 거 하나 있네. 인간관계.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;  나의 전환점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;내 전환점은 아무리 생각해도 &lt;strong&gt;군대&lt;/strong&gt;다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;많은 남자가 다 군대 가서 정신 차렸다고 했지만, 나처럼 정신 차린 사람도 드물 거 같다. &lt;em&gt;더 빨리 정신 차렸어야 했는데&lt;/em&gt; 라는 후회는 있지만 지금도 안 늦었다고 생각한다.&lt;/p&gt;
&lt;p&gt;군대는 철원 15사단을 나왔다. 아마 생각보다 힘들었던 거 같다. 그래도 정신적으로는 힘들지 않았다. 동기들하고 지냈던 시간 나름 재밌었다. 육체적으로 힘들어서 문제였지…. 그래서 이 육체적으로 힘든 걸 겪으면서 군대에서 어느 순간 흠 공부가 한번 해보고 싶은걸? 이라는 생각이 가졌다. 아마 그때가 휴가를 받기 위해서 정보처리 기능사를 취득하려고 했던 게 시작이었던 거 같다.&lt;br&gt;그렇게 취득하려고 밤에 연등 신청하며 공부를 해보니까 생각보다 나름 재밌었다. 아 &lt;em&gt;공부도 재밌을 수가 있구나&lt;/em&gt; 라고 생각이 들었던 게 그때가 처음이었던 거 같다.&lt;/p&gt;
&lt;h3&gt;  군대 전역 후 대학 생활&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;전역 후는 공부를 해봐야겠다라는 다짐으로 복학을 했다.  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;물론 처음부터 따라가는 건 쉽지 않았지만, 남들보다 많이 늦었다는 생각 때문인가? 더 열심히 했던 거 같다. (지식이라곤 정보처리 기능사 취득한 게 다였으니…. 그래도 진짜 많이 도움됐다.)&lt;/p&gt;
&lt;p&gt;그렇게 학과 수업을 듣고 재수강도 하고 열심히 했고 처음으로 성적 우수 장학금도 받아봤다. &lt;em&gt;이런 맛에 공부하는 건가…?&lt;/em&gt; 이런 생각이 들었다. ㅋㅋ 또 4학년이 돼서 정보처리 기사도 한 번에 땄다.&lt;/p&gt;
&lt;p&gt;그렇지만 이런 상태로는 어디 하나 취업하기 쉽지 않고 내가 분야를 어디 쪽으로 해야 할지도 몰랐던 상태라 가이드 해줄 사람도 없어서 대학 일자리본부에 상담? 을 받고 추천을 받아서 4학년 2학기 중간고사 끝난 후 국비 교육으로 학원 가서 JAVA 기반 SPRING 수업을 들었다. (취업계처럼 학점을 인정을 해줘서 중간고사 끝나고부터 들을 수 있었다.)&lt;/p&gt;
&lt;h2&gt;  졸업 후 그 다음은?&lt;/h2&gt;
&lt;h3&gt;  국비 교육&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;위에 글에 이어서 SPRING 기반 국비 교육을 들으며 확실히 개발이 재밌다는 생각에 확신을 할 수 있었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;학원에서도 6개월 동안 매일 같이 9시에 나와서 9시까지 공부를 하고 집에 갔다. 근데 힘들진 않았다. 오히려 문제 풀고 해결해가는 과정을 느끼며 재밌다는 생각이 더욱 강했던 거 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;그래서 개발자들은 그나마 스트레스가 덜 받는다는 말이 맞는 말일 수도 있다고 생각했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그렇게 6개월간 교육을 열심히 듣고 잘해서? 그런지 몰라도 우수 학습자 상도 받았다.&lt;/p&gt;
&lt;p&gt;그 후 SPRING 기반 전자정부 프레임워크 관세청에 상주하고 있는 회사에 면접을 봤고 붙었다.&lt;br&gt;하지만 뭔가 확신이 안 들었다. 가장 컸던 건 sm이라는 점이 가장 컸던 거 같았다.&lt;/p&gt;
&lt;h3&gt;  그래서 왜 프론트 앤드를 선택했는데?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;그 이후에 한 달 반 정도 혼자 공부를 하면서 &lt;strong&gt;이때 가장 많이 나 스스로 뭘 해야 할지 가장 확실해 진 거 같다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 기간이 나한테 굉장히 가치 있고 의미 있던 시간이었던 거 같다.&lt;br&gt;혼자 한 달 반 정도 내가 정보를 얻는 방법은 구글 뿐이었던 거 같다.&lt;/p&gt;
&lt;p&gt;한 달 반 동안 굉장한 정보를 많이 찾아봤던 거 같다. 뭐 이력서도 써보고 포트폴리오도 만들어보고 이래저래 많이 했고, 가장 중요한 건 내가 할 줄 아는 건 SPRING 기반 프레임워크인데 계속 그쪽으로 파고들지 않고 프론트 앤드를 접했던 게 가장 컸던 게 아닐까 생각이 든다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;접해본 시점은 우아한 형제들에서 우아한 테크캠프를 지원하면서부터였던 거 같다. (결과는 2차에서 탈락.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;우아한 테크캠프를 지원하고 자바스크립트 알고리즘 공부를 했고 그때부터 내가 알고 있던 자바스크립트는 옛날 버전의 자바스크립트라는 것을 깨달았고 es6 자바스크립트 이건 어디서 사용하지라는 궁금증이 생겼다.&lt;br&gt;그 후에 잡코리아나 사람인이나 그런 곳에서 올라오는 지원 자격을 보면서 프론트앤드, 백앤드라는 용어를 알게 되었고 프론트앤드에서 Javascript 기반 프레임워크의 react, vue, Angular등이 적혀있는 것을 보면서 이것저것 많이 찾아보았다.&lt;/p&gt;
&lt;p&gt;그렇게 찾아보면서 &lt;em&gt;react의 장점이 뭘까? react는 뭐하는 애지? 왜 이걸 많이 쓰지?&lt;/em&gt; 라는 생각과 함께 찾아보게 되었고, 공식 문서를 보고 velopert님의 블로그를 보고 접해보며 흥미를 느끼게 시작하였다.&lt;br&gt;그래서 react를 한 번 공부 해보자는 다짐으로 velopert님이 저자인 책을 읽으면서 github에 정리하며 공부를 본격적으로 하기 시작했다. 새로운 것을 배우며 배울수록 학습하며 할수록 Javascript 프레임워크에 대해서 재미를 엄청나게 느꼈고 &lt;strong&gt;와! 이거다. 이게 진짜다!&lt;/strong&gt; 라는 생각과 함께 머리를 띵하게 치는 느낌을 들었다. 또한 UI를 꾸미는 데에 있어서 하나 둘씩 내가 원하는 스타일대로 꾸며지는 게 기분이 좋아졌다.&lt;br&gt;그 후로 react, 모던 자바스크립트를 중심으로 필요한 것들을 찾거나 책을 사서 공부하기 시작하였다.&lt;br&gt;그리고 이렇게 &lt;strong&gt;블로깅을 하는 것과 Github에 계속 기록을 남기는 재미도 한몫하는 거 같다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;여기까지가 내가 프론트 앤드를 접해서 공부하는 이유이다.&lt;br&gt;&lt;strong&gt;결론은 한마디로 즐기면서 인생을 살아갈 수 있겠다! ✨ 요정도?&lt;/strong&gt;&lt;br&gt;지금까지 프론트 앤드 공부한 지는 6개월 정도 된 거 같다. 아직 너무나도 부족하기에 꾸준히 공부할 것이다.  &lt;br&gt;트랜드가 끊임없이 바뀌는 직업이기에 꾸준함이 중요하다고 생각한다. &lt;br&gt;지치지 말고 포기하지 않기.  &lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>회고</category>
      <category>프론트앤드</category>
      <category>회고</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/17</guid>
      <comments>https://haranglog.tistory.com/17#entry17comment</comments>
      <pubDate>Sun, 13 Sep 2020 16:25:11 +0900</pubDate>
    </item>
    <item>
      <title>✔ 우아한 테크러닝 3기: React &amp;amp; TypeScript 4회차</title>
      <link>https://haranglog.tistory.com/16</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djUuoL/btqIAOo6KYf/KKLfjuxqUMKb2TXvdpyw01/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djUuoL/btqIAOo6KYf/KKLfjuxqUMKb2TXvdpyw01/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djUuoL/btqIAOo6KYf/KKLfjuxqUMKb2TXvdpyw01/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjUuoL%2FbtqIAOo6KYf%2FKKLfjuxqUMKb2TXvdpyw01%2Fimg.jpg&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;✌ 9월10일 (목) 우아한 테크러닝 3기 4회차 강의 정리&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  4회차 강의 목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;  React 컴포넌트의 상태와 관리&lt;/li&gt;
&lt;li&gt;  커뮤니케이션!!&lt;/li&gt;
&lt;li&gt;  비동기와 제너레이터&lt;ul&gt;
&lt;li&gt;타입스크립트는 다음주부터!  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  React 컴포넌트의 상태와 관리&lt;/h2&gt;
&lt;p&gt;React 애플리케이션을 만든다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;pre&gt;&lt;code&gt;import React from &amp;quot;react&amp;quot;;
import ReactDOM from &amp;quot;react-dom&amp;quot;;
ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
      &amp;lt;App /&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById(&amp;quot;root&amp;quot;)
);&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;App.js&lt;/code&gt;&lt;pre&gt;&lt;code&gt;import React from &amp;quot;react&amp;quot;;
const App = () =&amp;gt; {
  return (
      &amp;lt;div&amp;gt;
          &amp;lt;header&amp;gt;
              &amp;lt;h1&amp;gt;React and TypeScript&amp;lt;/h1&amp;gt;
          &amp;lt;/header&amp;gt;
          &amp;lt;ul&amp;gt;
              &amp;lt;li&amp;gt;1회차: Overview&amp;lt;/li&amp;gt;
              &amp;lt;li&amp;gt;2회차: Redux 만들기&amp;lt;/li&amp;gt;
              &amp;lt;li&amp;gt;3회차: React 만들기&amp;lt;/li&amp;gt;
              &amp;lt;li&amp;gt;4회차: 컴포넌트 디자인 및 비동기&amp;lt;/li&amp;gt;
          &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
  );
};
export default App;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위와 같은 형태를 되어있는 것을 &lt;code&gt;li&lt;/code&gt; 태그(상태 값) 데이터를 객체로 만들어 &lt;code&gt;index.js&lt;/code&gt;에서 &lt;code&gt;App&lt;/code&gt;컴포넌트로 값을 넘겨준다.(아래)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;pre&gt;&lt;code&gt;import React from &amp;quot;react&amp;quot;;
import ReactDOM from &amp;quot;react-dom&amp;quot;;
import App from &amp;quot;./App&amp;quot;;   
const rootElement = document.getElementById(&amp;quot;root&amp;quot;);
const sessionList = [
  { title: &amp;quot;1회차: Overview&amp;quot; },
  { title: &amp;quot;2회차: Redux 만들기&amp;quot; },
  { title: &amp;quot;3회차: React 만들기&amp;quot; },
  { title: &amp;quot;4회차: 컴포넌트 디자인 및 비동기&amp;quot; }
];
ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;App store={{ sessionList }} /&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  rootElement
);&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;App.js&lt;/code&gt;&lt;pre&gt;&lt;code&gt;import React from &amp;quot;react&amp;quot;;
const App = (props) =&amp;gt; {
  const {sessionList} = props.store;
  return (
      &amp;lt;div&amp;gt;
          &amp;lt;header&amp;gt;
              &amp;lt;h1&amp;gt;React and TypeScript&amp;lt;/h1&amp;gt;
          &amp;lt;/header&amp;gt;
          &amp;lt;ul&amp;gt;
             {sessionList.map((session) =&amp;gt; (
                  &amp;lt;li&amp;gt;{session.title}&amp;lt;/li&amp;gt;
              ))}
          &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
  );
};
export default App;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이렇게 &lt;code&gt;li&lt;/code&gt;태그를 &lt;code&gt;map&lt;/code&gt;을 사용하여 생성할 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;하지만 이 코드는 좋지 못하다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;모든 리액트 컴포넌트 안에 리턴되는 &lt;code&gt;JSX&lt;/code&gt;들이 있는데 코드가 코드로써 렌더링되는 코드들이 많아지면 &lt;strong&gt;readability(가독성)&lt;/strong&gt; 이 떨어진다. 그렇기 때문에 컴포넌트를 한번 더 감싸서 만드는 것이 좋다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;App.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &amp;quot;react&amp;quot;;
// li 태그 분리
const SessionItem = ({ title }) =&amp;gt; &amp;lt;li&amp;gt;{title}&amp;lt;/li&amp;gt;;
// App 컴포넌트
const App = (props) =&amp;gt; {
  const {sessionList} = props.store;
  return (
      &amp;lt;div&amp;gt;
          &amp;lt;header&amp;gt;
              &amp;lt;h1&amp;gt;React and TypeScript&amp;lt;/h1&amp;gt;
          &amp;lt;/header&amp;gt;
          &amp;lt;ul&amp;gt;
             {sessionList.map((session) =&amp;gt; (
                  &amp;lt;SessionItem title={session.title} /&amp;gt;
              ))}
          &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
  );
};
export default App;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이렇게 분리를 하면 훨씬 더 좋은 &lt;strong&gt;가독성&lt;/strong&gt;이 생긴다.&lt;br&gt;하지만 지금의 컴포넌트는 &lt;strong&gt;상태를 가지고 있지 않는 컴포넌트&lt;/strong&gt;이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;버튼(토글형식)을 누르면 오름차순, 내림차순으로 바뀌는 상태를 가진 컴포넌트를 만들어보자!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;App.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &amp;quot;react&amp;quot;;
const App = (props) =&amp;gt; {
  let displayOrder = &amp;quot;ASC&amp;quot;;
  const { sessionList } = props.store;
  // order라는 이름으로 인덱스를 넣어준다.
  const orderedSessionList = sessionList.map((session, i) =&amp;gt; ({
      ...session,
      order: i,
  }));
  const toggleDisplayOrder = () =&amp;gt; {
      displayOrder = displayOrder === &amp;quot;ASC&amp;quot; ? &amp;quot;DESC&amp;quot; : &amp;quot;ASC&amp;quot;;
  };

  return (
      &amp;lt;div&amp;gt;
          &amp;lt;header&amp;gt;
              &amp;lt;h1&amp;gt;React and TypeScript&amp;lt;/h1&amp;gt;
          &amp;lt;/header&amp;gt;
          &amp;lt;button onClick={toggleDisplayOrder}&amp;gt;재정렬&amp;lt;/button&amp;gt;
          &amp;lt;ul&amp;gt;
              {orderedSessionList.map((session) =&amp;gt; (
                  &amp;lt;SessionItem title={session.title} key={session.id}/&amp;gt;
              ))}
          &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
  );
};
export default App;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;하지만 위와 같은 상태에서는 버튼을 클릭해봤자 &lt;code&gt;displayOrder&lt;/code&gt;의 값은 변경되지 않고 다시 렌더링되지도 않는다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  함수형 컴포넌트 왜 렌더링되지 않았을까?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;위 상황에서 함수 컴포넌트는 &lt;code&gt;onClick={toggleDisplayOrder}&lt;/code&gt;를 넣어서 버튼을 클릭하고 값을 바꿔봐도 갱신이 되지않는다.&lt;br&gt;리액트 입장에서 이 &lt;code&gt;App&lt;/code&gt;이 다시 호출되서 Virtual DOM이 다시 만들어지고 다시 DOM에다가 다시 업데이트를 해야하는데 안에서 다시 호출한다는 무언가의 신호(signal)을 함수 바깥으로 보낼 수가 없다. 왜냐하면 함수는 이미 끝났기 때문이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그래서 초창기에는 클래스 컴포넌트를 사용할 수 밖에 없었던 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  클래스 컴포넌트는 어떻게 상태가 바뀔까?&lt;/h3&gt;
&lt;p&gt;함수형 컴포넌트를 클래스 컴포넌트로 간단하게 바꿔본것이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;App.js&lt;/code&gt;&lt;pre&gt;&lt;code&gt;class ClassApp extends React.Component {
constructor(props) {
  // this.props 컨텍스트 객체
  super(props); // 컨벤션
  // this.onToggleDisplayOrder = this.onToggleDisplayOrder.bind(this);
  this.state = {
    displayOrder: &amp;quot;ASC&amp;quot;
  };
}
// onToggleDisplayOrder(){
//   // 그냥 함수 실행 컨텍스트를 따른다.
//   this.setState({
//     displayOrder:displayOrder ===&amp;#39;ASC&amp;#39; ? &amp;#39;DESC&amp;#39; : &amp;#39;ASC&amp;#39;
//   })
// }
toggleDisplayOrder = () =&amp;gt; {
  this.setState({
    displayOrder: displayOrder === &amp;quot;ASC&amp;quot; ? &amp;quot;DESC&amp;quot; : &amp;quot;ASC&amp;quot;
  });
};
render() {
  return (
    &amp;lt;div&amp;gt;
      여기여기
      &amp;lt;button onClick={this.onToggleDisplayOrder}&amp;gt;정렬&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;여기서 &lt;code&gt;super&lt;/code&gt;는 &lt;code&gt;extends&lt;/code&gt;뒤에 있는 것이고 &lt;a href=&quot;https://velog.io/@cyranocoding/JavaScript%EC%97%90%EC%84%9C%EC%9D%98-OOP-Inheritance%EC%99%80-Prototype-Chain%EA%B3%BC-Class-%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC-%EB%B0%8F-%EC%9D%B4%ED%95%B4-spjypizora&quot;&gt;prototype chain&lt;/a&gt;의 상위 부모이기 때문에 super한테 그대로 넘겨주면 리액트가 &lt;code&gt;props&lt;/code&gt;에다가 넣어준다. (컨텍스트 객체)&lt;/li&gt;
&lt;li&gt;기본값을 &lt;code&gt;this.state&lt;/code&gt;안에다가 넣어준다.&lt;/li&gt;
&lt;li&gt;여기서 일반함수 &lt;code&gt;onToggleDisplayOrder&lt;/code&gt;는 실행 컨텍스트를 따라서 &lt;code&gt;this&lt;/code&gt;가 바뀔 수 있는데, arrow 함수는 &lt;a href=&quot;https://velog.io/@bisu8018/Understanding-Execution-Context-and-Execution-Stack-in-Javascript&quot;&gt;렉시컬 컨텍스트(문맥 컨텍스트)&lt;/a&gt;를 따르기 때문에 지금 컴파일 타임의 이 순간(코드흐름)대로 따라가게 된다. 그래서 바인딩도 필요가 없게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  그래서 중요한건 어떻게 상태를 가질 수 있을까?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이런 과정의 이후에는 이 컨텍스트 객체만 가지고 만들어진 인스턴스 객체를 가지고 리액트를 핸들링하기 떄문에 없어지지 않는다. 즉, 다시 호출된다는 개념이 아니라 이미 존재하는 객체가 업데이트 되었으면 그 객체 안에 &lt;code&gt;render&lt;/code&gt;메소드를 호출하는 개념이다.&lt;/li&gt;
&lt;li&gt;그래서 &lt;code&gt;this&lt;/code&gt;를 다시 만드는 것이 아니기 때문에 클래스 컴포넌트가 상태를 가질 수 있는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  함수형 컴포넌트 어떻게 상태를 가질까?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;애초에 함수형 컴포넌트는 상태를 갖는건 불가능하다.&lt;/li&gt;
&lt;li&gt;하지만 react 팀에서 그 함수의 호출과정 언제 호출되는지, 순서등과 같은 것들을 모두 다 제어하고 있기 때문에 그거에 맞춰서 각자의 함수에 상태를 저장하는 것을 제공해주면 함수도 상태를 가질 수 있다라고 나온 것이 &lt;strong&gt;react hooks&lt;/strong&gt; 이다.&lt;/li&gt;
&lt;li&gt;결국 클래스 컴포넌트의 &lt;code&gt;render&lt;/code&gt;가 다시 불러오는 것과 같은 맥락이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  함수형 컴포넌트를 선호하는 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;App&lt;/code&gt;클래스 컴포넌트만 봐도 상태가 등장하는 것에 여러 메소드들이 분산이 되어있다.&lt;/li&gt;
&lt;li&gt;함수형 컴포넌트는 이와 다른데 함수하나에 모두 몰려있는 것을 볼 수가 있는데 이런 것을 &lt;strong&gt;응집성&lt;/strong&gt;이라고 하는데 모여있으면 훨씬 보기에 좋고 여러가지 측면에서 장점이 있다고 얘기를 한다. 또한 선언적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  함수형 컴포넌트의 Hook을 사용한 마무리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;App.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &amp;quot;react&amp;quot;;
const SessionItem = ({ title }) =&amp;gt; &amp;lt;li&amp;gt;{title}&amp;lt;/li&amp;gt;;
const App = (props) =&amp;gt; {
const [displayOrder, toggleDisplayOrder] = React.useState(&amp;quot;ASC&amp;quot;);
const { sessionList } = props.store;
const orderedSessionList = sessionList.map((session, i) =&amp;gt; ({
  ...session,
  order: i
}));

const onToggleDisplayOrder = () =&amp;gt; {
  toggleDisplayOrder(displayOrder === &amp;quot;ASC&amp;quot; ? &amp;quot;DESC&amp;quot; : &amp;quot;ASC&amp;quot;);
};

return (
  &amp;lt;div&amp;gt;
    &amp;lt;header&amp;gt;
      &amp;lt;h1&amp;gt;React and TypeScript&amp;lt;/h1&amp;gt;
    &amp;lt;/header&amp;gt;
    &amp;lt;p&amp;gt;전체 세션 갯수: 4개 {displayOrder}&amp;lt;/p&amp;gt;
    &amp;lt;button onClick={onToggleDisplayOrder}&amp;gt;재정렬&amp;lt;/button&amp;gt;
    &amp;lt;ul&amp;gt;
      {orderedSessionList.map((session) =&amp;gt; (
        &amp;lt;SessionItem title={session.title} key={session.id} /&amp;gt;
      ))}
    &amp;lt;/ul&amp;gt;
  &amp;lt;/div&amp;gt;
);
};
export default App;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이렇게 &lt;code&gt;useState&lt;/code&gt; Hook을 사용하여 상태를 관리할 수 있게 되었다.&lt;br&gt;전체 소스 코드는 &lt;a href=&quot;https://github.com/saseungmin/woowa_tech_learning_react_typescript/tree/master/week_2_2&quot;&gt;GitHub&lt;/a&gt; 참고&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7q2YM/btqIs83oHkc/tSmO13iWHB2D03SSgTvOok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7q2YM/btqIs83oHkc/tSmO13iWHB2D03SSgTvOok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7q2YM/btqIs83oHkc/tSmO13iWHB2D03SSgTvOok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7q2YM%2FbtqIs83oHkc%2FtSmO13iWHB2D03SSgTvOok%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;  함수형 컴포넌트의 Hooks는 클로저이다??  &lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;실제로 &lt;code&gt;Hooks&lt;/code&gt;는 클로저가 아닌거 같다!   하지만 &lt;code&gt;Hooks&lt;/code&gt;를 사용할땐 당연히 클로저가 전달된다!&lt;/li&gt;
&lt;li&gt;위의 &lt;code&gt;displayOrder&lt;/code&gt;의 값의 상태를 변경할려면 당연히 &lt;code&gt;toggleDisplayOrder&lt;/code&gt;함수를 호출해야 되고, 이 함수는 &lt;strong&gt;반드시 다른 함수 안에 콜백으로 들어갈 수 밖에 없는 구조이다.&lt;/strong&gt;    이 상황에서 &lt;strong&gt;클로저가 잡하는 것이다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;왜나하면 &lt;code&gt;toggleDisplayOrder&lt;/code&gt; 함수가 호출되는 시점에 호출되면 안되는 것은 호출을 하면 다시 &lt;code&gt;render&lt;/code&gt;가 될 것이고 그렇게 &lt;code&gt;render&lt;/code&gt;가 되고 다시 &lt;code&gt;toggleDisplayOrder&lt;/code&gt; 함수가 호출되기 때문에 무한루프에 빠지게 되는 것이다.&lt;blockquote&gt;
&lt;p&gt;그렇기 때문에 Hook 자체가 클로저가 아니라 함수안에서 클로저가 잡히는 것이다!  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  클로저 같은 것들이 메모리가 만들어졌으면 이걸 언제 해제하거나 제거할 수 있을까?? (메모리 할당/해제)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;아래의 예를 보자.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ex)
useEffect(() =&amp;gt; {
  document.title = &amp;#39;&amp;#39;;
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위를 확인해보면 &lt;code&gt;document.title&lt;/code&gt;은 리액트와 무관한 바깥에서 일어나는 일이다.&lt;br&gt;이런 것들을 &lt;a href=&quot;https://ko.reactjs.org/docs/hooks-effect.html&quot;&gt;&lt;strong&gt;side-effect&lt;/strong&gt;&lt;/a&gt;라고 하는데 제어가 불가능하고 부수효과를 이르킬수 있는 요소들을 말하는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;useEffect&lt;/code&gt;안에서 &lt;code&gt;return&lt;/code&gt;을 함수로 해주면 &lt;strong&gt;실제로&lt;/strong&gt; UI에서 사라질 때 이 함수를 호출을 해주는데 정리할 리소스들을 해제할 것들을 하라고 나와있다. (&lt;a href=&quot;https://ko.reactjs.org/docs/hooks-effect.html#example-using-hooks-1&quot;&gt;공식문서 참조&lt;/a&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ex)
useEffect(() =&amp;gt; {
  // ...
  return () =&amp;gt; {

  }
})&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;하지만 위에서 &lt;code&gt;return&lt;/code&gt;을 함수로 호출하는 방법은 정리 대상의 요소들은 실질적인 대상에 불과하고 &lt;strong&gt;메모리, 변수, 클로저 공간, 스코프 공간 이런 것들은 대상이 아니다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JavaScript 에서 변수안에 있는 데이터 혹은 객체를 임의로 지우는 방법은 존재하지 않는다.&lt;/strong&gt; (언어적으로 제공하지 않는다.)&lt;br&gt;하지만, 무한히 생성되는 것은 아니고 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Memory_Management&quot;&gt;&lt;strong&gt;GC(가비지 컬렉션)&lt;/strong&gt;&lt;/a&gt;이라는 메커니즘이 존재하는데 사용하지 않는 것들은 수집해서 자바스크립트가 &lt;code&gt;Mark-and-sweep&lt;/code&gt;이라는 알고리즘을 사용하여 제거를 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그때 컴포넌트가 사라지면 같이 그 안에 존재하던 일반 객체 뿐만 아니라 클로저와 같은 스코프 공간들도 참조가 없어지면 GC가 일어나는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;자바스크립트에서 그런 종류의 데이터 스토리지들을 &lt;strong&gt;명시적으로 제거하는 방법은 제공하지 않기 때문에&lt;/strong&gt; react에서 &lt;code&gt;clean up&lt;/code&gt; 대상은 클로저 같은 것이 아니기 때문에 &lt;strong&gt;굳이 메모리에 대해서 신경을 쓰지 않아도 된다.&lt;/strong&gt; (알아서 해준다.)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  커뮤니케이션에 대해서..&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;커뮤니케이션은 일종의 &lt;strong&gt;프로토콜&lt;/strong&gt;(정보를 주고 받기 위한 약속)과 같다.&lt;/li&gt;
&lt;li&gt;이렇듯 일하면서나 면접에서 봐도 이 프로토콜이 안 맞는 경우가 굉장히 많다. 그리고 더욱 더 큰 문제는 &lt;strong&gt;프로토콜이 안 맞는다라는 것을 의식조차 못하고 있는 사람&lt;/strong&gt;들도 꽤 많다는 것이다.&lt;/li&gt;
&lt;li&gt;그렇기에 프로토콜이라는 것은 정보를 주고 받는다는 건(이해를 한다는 건) 어느 정도의 맥락(수준)이 일치 되야 한다는 것이다.&lt;br&gt;이 수준의 layer라고 생각을 하면 어떤 A를 상대방에게 전달할 때 상대방이 이해할 수 있는 layer를 파악하고 있는 것이 굉장히 중요하다. 즉 이말은 개발자가 개발자가 아닌 사람한테 개발용어를 설명한다는 것과 비슷한 것이다.&lt;br&gt;그런 부분을 &lt;strong&gt;잘 알고 있어야하고 일방적인 소통을 하면 안된다는 점이다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;✨ &lt;strong&gt;학습하는 부분에 있어서도 같은 맥락&lt;/strong&gt;이라 볼 수 있다.&lt;/li&gt;
&lt;li&gt;학습도 나와의 커뮤니케이션이라는 것으로 그 지식이 나와 맞으면 그 지식이 잘 들어온다. 하지만 맞지 않는다면 그 원하고자 하는 지식은 눈에 들어오지 않는 것이다.&lt;/li&gt;
&lt;li&gt;그렇기 때문에 어느 layer의 지식이 나와의 프로토콜에 맞을까를 생각해서 스스로 조정이 필요하다. &lt;/li&gt;
&lt;li&gt;또한 어느 지식에 대해서 그 하나만 몰입하지 않았으면 좋겠고 너무 고급기술만 배울려고 하지 않았으면 좋겠다.&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;  신입 개발자 또는 주니어 개발들에게..&lt;/strong&gt;&lt;br&gt;신입분들은 대부분 의욕이 넘쳐나고 충만한 상태인데 그 의욕은 굉장히 좋지만 반대로 매우 위험한 상태일 수도 있다.&lt;br&gt;어느 한쪽에 몰입하여 잘 못 빠지게 되면 훅 들어가 버릴 수가 있다.&lt;br&gt;그렇기 때문에 항상 자기를 돌아봐보는 시간을 갖는 것이 좋을 거 같다. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  비동기와 제너레이터&lt;/h2&gt;
&lt;h3&gt;  제너레이터&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;제너레이터는 &lt;code&gt;function *&lt;/code&gt; 으로 사용된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function* foo() {}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 코드를 확인해보자.&lt;br&gt;&lt;code&gt;x&lt;/code&gt;값이 확정짓기 전에 계산을 수행할 수 없기 때문에 순차적으로 실행될 수 밖에 없다.&lt;br&gt;즉, &lt;code&gt;y&lt;/code&gt;의 변수에 &lt;code&gt;x&lt;/code&gt;의 의존성이 있기 때문에 &lt;code&gt;x&lt;/code&gt;가 없이 &lt;code&gt;y&lt;/code&gt;가 생길 수 없게 되는 것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const x = 10;
const y = x*10;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;함수인 경우는??&lt;br&gt;함수는 호출을 하면 그냥 함수만 담고 실제로 &lt;code&gt;x&lt;/code&gt;값을 &lt;strong&gt;확정하는 순간을 지연시킨다.&lt;/strong&gt;(지연호출)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const x = () =&amp;gt; 10;
const y = x*10;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Promise&lt;/code&gt;도 이 지연과 굉장히 비슷하다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const p = new Promise(function (resolve, reject) {
// 함수로 실행해서 지연을 일으킨다.
  setTimeout(() =&amp;gt; {
    resolve(&amp;quot;1&amp;quot;);
  }, 100);
});&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;제너레이터는 &lt;strong&gt;제너레이터 객체를 반환&lt;/strong&gt;하는데 제너레이터 객체는 &lt;strong&gt;코루틴&lt;/strong&gt;이라는 함수의 구현체다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;코루틴이란?? &lt;br&gt;함수는 인자를 받고 무언가 계산을 하고 리턴을 한다. 그리고 한 번 호출하고 결과를 받거나 그 다음 스텝을 실행한다.&lt;br&gt;이런 상황을 폭 넓게 넓혀 호출자한테 리턴을 여러 번 할 수 있게 해주는 것이다.&lt;br&gt;리턴을 &lt;strong&gt;여러 번 한다는 것&lt;/strong&gt;은 다시 함수가 호출될 때 처음부터 시작되는 것이 아니라 &lt;strong&gt;마지막 리턴한 지점부터 다시 시작한다는 컨셉&lt;/strong&gt;이고 함수를 조금 더 일반화한 컨셉으로 확장해서 기능을 추가를 해 놓은 것이 &lt;strong&gt;코루틴&lt;/strong&gt;이다.&lt;br&gt;자바스크립트에서는 그 코루틴의 개념을 일부 차용해서 &lt;strong&gt;제너레이터&lt;/strong&gt;라고 명명하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;일반적인 함수인 경우는 &lt;code&gt;make&lt;/code&gt;함수의 반환 값을 &lt;code&gt;1&lt;/code&gt;이 될 것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function make(){
  return 1;
} // 1
// 제너레이터
function* make() {
  return 1;
}
console.log(make());&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래와 같이 &lt;code&gt;function*&lt;/code&gt;를 사용하면 이 제너레이터 객체를 반환한다.&lt;br&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QSt0n/btqIvX8GWdL/BfTXAis86z1cyN72WQCDY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QSt0n/btqIvX8GWdL/BfTXAis86z1cyN72WQCDY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QSt0n/btqIvX8GWdL/BfTXAis86z1cyN72WQCDY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQSt0n%2FbtqIvX8GWdL%2FBfTXAis86z1cyN72WQCDY1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;제너레이터는 &lt;code&gt;yield&lt;/code&gt;과 같이 사용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function* makeNumber() {
let num = 1;
// 무한 루프이지만 문제가 없다.
while (true) {
  // 제너레이터 안의 리턴이지만 함수를 끝내지 않고 바깥으로 내보낸다.
  // 기존 상태를 저장하고 있다.
  yield num++;
}
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래에 &lt;code&gt;makeNumber()&lt;/code&gt; 제너레이터를 생성해도 값이 반환되지 않고 객체를 넘겨준다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const j = makeNumber();&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위의 &lt;code&gt;makeNumber&lt;/code&gt;를 호출하면 제너레이터 객체를 넘겨주고 여기에는 &lt;code&gt;next&lt;/code&gt;라는 메소드가 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;console.log(j.next());&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;콘솔에 찍힌 값을 확인해보면 &lt;code&gt;value&lt;/code&gt;는 &lt;code&gt;yield&lt;/code&gt;로 반환된 값이며 &lt;code&gt;done&lt;/code&gt;은 &lt;code&gt;true&lt;/code&gt;일 경우 제너레이터가 종료된다.&lt;br&gt;즉, &lt;code&gt;yield&lt;/code&gt;나 &lt;code&gt;return&lt;/code&gt;이 나올 때 까지 실행하는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXxQO5/btqIxYlrppP/x1YsC3feWL43hB2E1Mn4Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXxQO5/btqIxYlrppP/x1YsC3feWL43hB2E1Mn4Y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXxQO5/btqIxYlrppP/x1YsC3feWL43hB2E1Mn4Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXxQO5%2FbtqIxYlrppP%2Fx1YsC3feWL43hB2E1Mn4Y0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;제너레이터는 &lt;code&gt;next&lt;/code&gt;는 값을 넣을 수도 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function* makeNumber() {
  let num = 1;
  while (true) {
    const x = yield num++;
    console.log(x);
  }
}
const j = makeNumber();
console.log(j.next());
console.log(j.next(&amp;quot;a&amp;quot;));&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 사진과 같이 &lt;code&gt;next&lt;/code&gt;에 매개변수로 넘겨준 문자열 &lt;code&gt;a&lt;/code&gt;가 &lt;code&gt;yield&lt;/code&gt;의 반환값으로 담겨서 사용할 수도 있는 것을 확인할 수 있다.&lt;br&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dNcbMD/btqIqmgUt9b/b5lLoEuEtymQ3amEVUxhFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dNcbMD/btqIqmgUt9b/b5lLoEuEtymQ3amEVUxhFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dNcbMD/btqIqmgUt9b/b5lLoEuEtymQ3amEVUxhFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdNcbMD%2FbtqIqmgUt9b%2Fb5lLoEuEtymQ3amEVUxhFk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;제너레이터를 활용해서 비동기함수를 동기적으로 나타낼 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const delay = (ms) =&amp;gt; new Promise((resolve) =&amp;gt; setTimeout(resolve, ms));
// 동기 함수처럼 보인다.
// 바깥쪽에서 상황을 해결한다.
function* main() {
  console.log(&amp;quot;시작&amp;quot;);
  yield delay(3000);
  console.log(&amp;quot;3초뒤&amp;quot;);
}
const it = main();
const { value } = it.next();
console.log(value);&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위의 코드를 실행하면 &lt;code&gt;시작&lt;/code&gt;이 출력되고 3초 뒤에 &lt;code&gt;3초 뒤&lt;/code&gt;라고 출력된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;async/await&lt;/code&gt;를 사용해서 비슷하게 사용할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// async
async function main2() {
console.log(&amp;quot;시작&amp;quot;);
await delay(3000);
console.log(&amp;quot;3초뒤&amp;quot;);
}
main2();&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;async/await&lt;/code&gt;는 &lt;code&gt;Promise&lt;/code&gt;에 최적화 되어있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그렇지만 제너레이터를 사용할 경우 &lt;code&gt;Promise&lt;/code&gt; 형태의 값이 반환되지 않아도 사용이 가능하기 때문에 더 다양한 상황에서 응용이 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;전체 소스는 &lt;a href=&quot;https://github.com/saseungmin/woowa_tech_learning_react_typescript/blob/master/week_2_2/asynchronous.js&quot;&gt;GitHub&lt;/a&gt;를 참고  &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  4회차 후기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;오늘도 역시나 역시 배운게 많았다. 생소한 용어도 많이 있었다. 찾아서 공부를 더 해봐야겠다. &lt;br&gt;다음 주부터는 typescript에 대해서 배운다는데 집중해서 들어야겠다.. &lt;br&gt;벌써 반이나 와버렸다.. 시간이 너무 빠르다. 블로깅하는 것이 쉽지않은데 끝까지 아자아자! 해야겠다!!!✨&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>우아한 테크러닝 3기</category>
      <category>react</category>
      <category>typescript</category>
      <category>우아한 테크러닝</category>
      <category>우아한 테크러닝 3기 4회차</category>
      <category>우아한 테크러닝3기</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/16</guid>
      <comments>https://haranglog.tistory.com/16#entry16comment</comments>
      <pubDate>Fri, 11 Sep 2020 23:26:23 +0900</pubDate>
    </item>
    <item>
      <title>✔ 우아한 테크러닝 3기: React &amp;amp; TypeScript 3회차</title>
      <link>https://haranglog.tistory.com/15</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eWgQFH/btqIsLeBSDp/joKMrnsYTgfMKwOz5UK3N1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eWgQFH/btqIsLeBSDp/joKMrnsYTgfMKwOz5UK3N1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eWgQFH/btqIsLeBSDp/joKMrnsYTgfMKwOz5UK3N1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeWgQFH%2FbtqIsLeBSDp%2FjoKMrnsYTgfMKwOz5UK3N1%2Fimg.jpg&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;✌ 9월08일 (화) 우아한 테크러닝 3기 3회차 강의 정리&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  3회차 강의 목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;리액트 만들어보기&lt;/li&gt;
&lt;li&gt;리액트의 기본적인 부분들과 리액트를 구현해보자!  &lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;  리액트를 구현해보는 이유!&lt;/strong&gt;&lt;br&gt;개발자가 사용자입장에서만 사용하지 않았으면 좋겠다.&lt;br&gt;이렇게 구현해보면서 더 좋은 개발자가 되었으면 좋겠다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  React를 만들기 전에..&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;list&lt;/code&gt;와 같은 데이터를 기반으로 JavaScript를 사용할 땐 아래와 같이 &lt;code&gt;rootElement.innerHTML&lt;/code&gt;사용하여 화면에 표시할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// index.js
const list = [
  { title: &amp;quot;React에 대해 알아봅시다.&amp;quot; },
  { title: &amp;quot;Redux에 대해 알아봅시다.&amp;quot; },
  { title: &amp;quot;TypeScript에 대해 알아봅시다.&amp;quot; }
];
const rootElement = document.getElementById(&amp;quot;root&amp;quot;);
function app() {
  rootElement.innerHTML = `
      &amp;lt;ul&amp;gt;
            ${list.map((item) =&amp;gt; `&amp;lt;li&amp;gt;${item.title}&amp;lt;/li&amp;gt;`).join(&amp;quot;&amp;quot;)}
      &amp;lt;/ul&amp;gt;
  `;
}
app();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위의 &lt;code&gt;app()&lt;/code&gt;함수는 &lt;code&gt;list&lt;/code&gt;를 바깥에서 직접 가져다 쓰기 때문에 좋지 못하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;바깥의 영향을 안 받게 &lt;strong&gt;순수 함수&lt;/strong&gt;로 만들어 준다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 생략..
function app(items) {
  rootElement.innerHTML = `
      &amp;lt;ul&amp;gt;
            ${items.map((item) =&amp;gt; `&amp;lt;li&amp;gt;${item.title}&amp;lt;/li&amp;gt;`).join(&amp;quot;&amp;quot;)}
      &amp;lt;/ul&amp;gt;
  `;
}
app(list);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;  React가 탄생한 이유&lt;/h3&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위와 같이 &lt;code&gt;rootElement.innerHTML&lt;/code&gt;의 DOM을 직접 사용하여 작성한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이럴때 데이터가 많아지고 UI도 많아지고, UI가 많아지다보니 어떨땐 A라는 UI를 사용자에게 보여주고, 어떨땐 B라는 UI를 사용자에게 보여주고, C라는 이미지를 보여주게 된다. 이런것을 &lt;strong&gt;라우팅&lt;/strong&gt;이라고 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;데이터도 위와 같이 코드에 박혀 있는 것이 아니고 서버에 있다보니 API의 호출을 통해서 비동기적으로 필요한 상황에 적시적소 원하는 타이밍에 데이터를 가져온다. 이렇듯 &lt;strong&gt;끊임없이 변화한다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이렇게 변하다보니 한달 뒤에 또는 두달 세달 뒤에 코드를 고칠려고 할 때 생각이 잘 안난다...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;빨리 생각이 날 수 있는 구조가 있는데 여기에 큰 원칙으로 좋은 아키텍처는 &lt;strong&gt;같은 것끼리 묶어주고, 다른 것끼리 분리하는 것이다.(대원칙)&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 원칙의 가장 중요한 첫 번째 할 수 있는 일은 &lt;strong&gt;네이밍을 잘 지어주는 것으로 네이밍만 잘해줘도 70%는 해결된다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위 코드처럼 &lt;code&gt;Real DOM&lt;/code&gt;을 가지고 HTML UI를 조작하는 것은 안전성이 매우 떨어진다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그 이유는 &lt;code&gt;Real DOM&lt;/code&gt;은 너무 Low Level API이기 때문이여서 추상화 수준이 높지 못하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이것으로 조작하다보면 필연적으로 &lt;strong&gt;복잡도가 올라가게 되는 것이다.&lt;/strong&gt; 그 말은 즉, &lt;strong&gt;수정하기가 매우 까다롭다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이런 상황을 해결하기 위해 &lt;code&gt;react&lt;/code&gt;와 같은 것들이 나오게 되었다! &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;브라우저는 HTML같은 것을 DOM이라는 트리 구조의 데이터로 변환해서 관리한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이처럼 자바스크립트 자체로 직접 접근하는 것 또한 까다로우니까 &lt;code&gt;Virtual DOM&lt;/code&gt;을 사용한  React가 나왔다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;사실상 HTML 문자열을 브라우저가 DOM이라는 트리 구조의 객체 형태로 만드는 것이나 DOM을 &lt;code&gt;Virtual DOM&lt;/code&gt;으로 만드는 것이다 결국 똑같은 컨셉이다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  React의 기본 이해와 Virtual DOM&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;기본 구조&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &amp;quot;react&amp;quot;;
import ReactDOM from &amp;quot;react-dom&amp;quot;;
function App() {
  return (
      &amp;lt;div&amp;gt;
          &amp;lt;h1&amp;gt;Hello?&amp;lt;/h1&amp;gt;
          &amp;lt;ul&amp;gt;
              &amp;lt;li&amp;gt;React&amp;lt;/li&amp;gt;
              &amp;lt;li&amp;gt;Redux&amp;lt;/li&amp;gt;
              &amp;lt;li&amp;gt;MobX&amp;lt;/li&amp;gt;
              &amp;lt;li&amp;gt;Typescript&amp;lt;/li&amp;gt;
          &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
  );
}
ReactDOM.render(&amp;lt;App /&amp;gt;, document.getElementById(&amp;quot;root&amp;quot;));&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JSX문법이며 Babel이 변환해준다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Babel의 &lt;code&gt;react plugin&lt;/code&gt;이 리액트 테그를 변환해주는 것이다. 그렇기 때문에 &lt;strong&gt;실제로는 결국 그냥 함수 호출에 불과하다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ReactDOM&lt;/code&gt;은 &lt;code&gt;render&lt;/code&gt; 정적 메소드를 갖는데 2개의 인자를 받는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;첫 번째 인자는 화면에 렌더링할 컴포넌트이며 두 번째 인자는 컴포넌트를 렌더링할 HTML 요소이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Babel에서 jsx를 javasript로 변환
&amp;quot;use strict&amp;quot;;
var _react = _interopRequireDefault(require(&amp;quot;react&amp;quot;));
var _reactDom = _interopRequireDefault(require(&amp;quot;react-dom&amp;quot;));
function _interopRequireDefault(obj) { return obj &amp;amp;&amp;amp; obj.__esModule ? obj : { default: obj }; }
function App() {
    return /*#__PURE__*/_react.default.createElement(&amp;quot;div&amp;quot;, null, /*#__PURE__*/_react.default.createElement(&amp;quot;h1&amp;quot;, null, &amp;quot;Hello?&amp;quot;), /*#__PURE__*/_react.default.createElement(&amp;quot;ul&amp;quot;, null, /*#__PURE__*/_react.default.createElement(&amp;quot;li&amp;quot;, null, &amp;quot;React&amp;quot;), /*#__PURE__*/_react.default.createElement(&amp;quot;li&amp;quot;, null, &amp;quot;Redux&amp;quot;), /*#__PURE__*/_react.default.createElement(&amp;quot;li&amp;quot;, null, &amp;quot;MobX&amp;quot;), /*#__PURE__*/_react.default.createElement(&amp;quot;li&amp;quot;, null, &amp;quot;Typescript&amp;quot;)));
}
_reactDom.default.render( /*#__PURE__*/_react.default.createElement(App, null), document.getElementById(&amp;quot;root&amp;quot;));&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt; react의 &lt;code&gt;createElement&lt;/code&gt;는 Virtual DOM을 만들게 된다.&lt;br&gt; 자바스크립트 사이드에서는 &lt;code&gt;createElement&lt;/code&gt;라는 함수를 이용해서 Virtual DOM이 만들어지고 리액트는 자신이 만든 것이라 잘 알기 때문에 Virtual DOM을 만든 다음에 그것을 그대로 Real DOM으로 변환해주는 것이다.&lt;br&gt;그리고 개발자들에게는 &lt;code&gt;createElement&lt;/code&gt;를 안쓰기때문에 컴포넌트로 좀 더 쉽게 사용하기 위해서 JSX를 제공하는 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;import React from &amp;quot;react&amp;quot;;
import ReactDOM from &amp;quot;react-dom&amp;quot;;
// StudyList 이름을 가질 수 있다.
function StudyList() {
  return (
      &amp;lt;ul&amp;gt;
          &amp;lt;li&amp;gt;React&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;Redux&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;MobX&amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;Typescript&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
  );
}
function App() {
  return (
      &amp;lt;div&amp;gt;
          &amp;lt;h1&amp;gt;Hello?&amp;lt;/h1&amp;gt;
          &amp;lt;StudyList /&amp;gt;
      &amp;lt;/div&amp;gt;
  );
}
ReactDOM.render(&amp;lt;App /&amp;gt;, document.getElementById(&amp;quot;root&amp;quot;));&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위와 같이 분리를 해서 컴포넌트 이름을 지정해서 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;이렇게 이름을 줄 수 있는 구조는 더 이상 MarkUp을 보고서는 우리가 어떤 데이터가 어디에 두었는지를 고민하지 않고 컴포턴트 이름만(잘 지어졌을 경우) 보고서 확인이 가능함으로써 읽기 쉬워지고, 읽기 쉬어짐에 따라 고치기도 쉬워진다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위의 StudyList 함수에서 반환하는 JSX 엘리먼트들을 Babel로 변환되면 아래와 같은 형태이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function StudyList() {
  return /*#__PURE__*/_react.default.createElement(&amp;quot;ul&amp;quot;, null, /*#__PURE__*/_react.default.createElement(&amp;quot;li&amp;quot;, {
    className: &amp;quot;item&amp;quot;
  }, &amp;quot;React&amp;quot;), /*#__PURE__*/_react.default.createElement(&amp;quot;li&amp;quot;, {
    className: &amp;quot;item&amp;quot;
  }, &amp;quot;Redux&amp;quot;), /*#__PURE__*/_react.default.createElement(&amp;quot;li&amp;quot;, {
    className: &amp;quot;item&amp;quot;
  }, &amp;quot;MobX&amp;quot;), /*#__PURE__*/_react.default.createElement(&amp;quot;li&amp;quot;, {
    className: &amp;quot;item&amp;quot;
  }, &amp;quot;Typescript&amp;quot;));
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;변환된 상태를 참고하여 객체로 바꾸어보면 아래의 &lt;code&gt;vdom&lt;/code&gt;과 같은 형태이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const vdom = {
  type: &amp;quot;ul&amp;quot;,
  props: {},
  children: [
      { type: &amp;quot;li&amp;quot;, props: { className: &amp;quot;item&amp;quot; }, children: &amp;quot;React&amp;quot; },
      { type: &amp;quot;li&amp;quot;, props: { className: &amp;quot;item&amp;quot; }, children: &amp;quot;Redux&amp;quot; },
      { type: &amp;quot;li&amp;quot;, props: { className: &amp;quot;item&amp;quot; }, children: &amp;quot;Typescript&amp;quot; },
      { type: &amp;quot;li&amp;quot;, props: { className: &amp;quot;item&amp;quot; }, children: &amp;quot;MobX&amp;quot; },
  ],
};&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;이렇게 확인해보면 왜 React가 결국 맨 상위 단 하나의 최상위 부모 컨포넌트가 있을 수 밖에 없는지 알 수 있는 구조이다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이렇듯 react는 Virtual DOM이 단 하나인 이유이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  React 만들어보기!&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;위의 JSX가 변환된 모습을 보면 &lt;code&gt;createElement&lt;/code&gt;는 아래와 같은 형태로 이루어져있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// props는 없으면 빈 객체, children는 여러 개의 배열
function createElement(type, props = {}, ...children) {
return { type, props, children };
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위에서 만들어본 &lt;code&gt;vdom&lt;/code&gt;을 &lt;code&gt;createElement&lt;/code&gt;를 이용하여 만들 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 예시
const vdom = createElement(&amp;quot;ul&amp;quot;, {}, createElement(&amp;quot;li&amp;quot;, {}, &amp;quot;React&amp;quot;));&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wsVZY/btqIdrWKYqn/1wBfsvfa8KWiiBHW2p31sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wsVZY/btqIdrWKYqn/1wBfsvfa8KWiiBHW2p31sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wsVZY/btqIdrWKYqn/1wBfsvfa8KWiiBHW2p31sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwsVZY%2FbtqIdrWKYqn%2F1wBfsvfa8KWiiBHW2p31sk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;createElement&lt;/code&gt;는 vdom 객체를 만드는 함수고 render할 때, &lt;code&gt;render&lt;/code&gt; 객체는 계속 쫒아가면서 realdom으로 변환한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;최종적으로 다 만들면 root 컴포넌트 컨테이너한테 &lt;code&gt;append child&lt;/code&gt;를 하는 방식이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;바벨의 옵션을 이용해서 맨 상단에 주석을 넣어준다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 &lt;code&gt;@jsx&lt;/code&gt;의 지시어를 이용해서 내가 원하는 이름을 주면 &lt;code&gt;createElement&lt;/code&gt;가 원하는 이름으로 변경이 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* @jsx createElement */
function StudyList(props) {
  return (
      &amp;lt;ul&amp;gt;
          &amp;lt;li className=&amp;quot;item&amp;quot;&amp;gt;React&amp;lt;/li&amp;gt;
          &amp;lt;li className=&amp;quot;item&amp;quot;&amp;gt;Redux&amp;lt;/li&amp;gt;
          &amp;lt;li className=&amp;quot;item&amp;quot;&amp;gt;MobX&amp;lt;/li&amp;gt;
          &amp;lt;li className=&amp;quot;item&amp;quot;&amp;gt;Typescript&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 말을 즉, 바벨만 가지고 Virtual DOM을 만들 메소드(트랜스파일될 메소드)를 임의로 바꿔서 내가 만든 다른 것으로 교체가 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 과정은 컴파일 타임이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// createElement의 이름으로 변환되었다.
&amp;quot;use strict&amp;quot;;
/* @jsx createElement */
function StudyList(props) {
return createElement(&amp;quot;ul&amp;quot;, null, createElement(&amp;quot;li&amp;quot;, {
  className: &amp;quot;item&amp;quot;
}, &amp;quot;React&amp;quot;), createElement(&amp;quot;li&amp;quot;, {
  className: &amp;quot;item&amp;quot;
}, &amp;quot;Redux&amp;quot;), createElement(&amp;quot;li&amp;quot;, {
  className: &amp;quot;item&amp;quot;
}, &amp;quot;MobX&amp;quot;), createElement(&amp;quot;li&amp;quot;, {
  className: &amp;quot;item&amp;quot;
}, &amp;quot;Typescript&amp;quot;));
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;@jsx&lt;/code&gt;지시어를 사용하면 js문법을 찾아서 자동으로 변환을 해준다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이렇게 &lt;code&gt;createElement&lt;/code&gt;로 변하게 되고 우리가 만든 &lt;code&gt;createElement&lt;/code&gt;함수로 들어가게 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* @jsx createElement */
function createElement(type, props = {}, ...children) {
return { type, props, children };
}
function StudyList(props) {
return (
  &amp;lt;ul&amp;gt;
    &amp;lt;li className=&amp;quot;item&amp;quot; label=&amp;quot;haha&amp;quot;&amp;gt; React &amp;lt;/li&amp;gt;
    &amp;lt;li className=&amp;quot;item&amp;quot;&amp;gt;Redux&amp;lt;/li&amp;gt;
    &amp;lt;li className=&amp;quot;item&amp;quot;&amp;gt;TypeScript&amp;lt;/li&amp;gt;
    &amp;lt;li className=&amp;quot;item&amp;quot;&amp;gt;Mobx&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
);
}
function App() {
return (
  &amp;lt;div&amp;gt;
    &amp;lt;h1&amp;gt;Hello?&amp;lt;/h1&amp;gt;
    &amp;lt;StudyList item=&amp;quot;abcd&amp;quot; id=&amp;quot;hoho&amp;quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
);
}
//  변환되면 console.log(createElement(App, null));
console.log(&amp;lt;App /&amp;gt;); &lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이렇게 &lt;code&gt;console.log(&amp;lt;App /&amp;gt;);&lt;/code&gt; 해보게 되면 아래 사진처럼 &lt;code&gt;App&lt;/code&gt;함수가 들어간다.&lt;br&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vBuaH/btqIdsnSBTp/KNk3KxkKEOO7KqxJ9pB640/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vBuaH/btqIdsnSBTp/KNk3KxkKEOO7KqxJ9pB640/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vBuaH/btqIdsnSBTp/KNk3KxkKEOO7KqxJ9pB640/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvBuaH%2FbtqIdsnSBTp%2FKNk3KxkKEOO7KqxJ9pB640%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;type&lt;/code&gt;의 타입을 비교하는 로직을 추가해준다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function createElement(type, props = {}, ...children) {
if (typeof type === &amp;quot;function&amp;quot;) {
    // 호출해준다.
  return type.apply(null, [props, ...children]);
}
return { type, props, children };
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  리액트에서 사용자 컴포넌트가 대문자로 시작하는 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;변환된 App안의 태그들을 확인해보면 문자열로 들어가는 것을 확인할 수 있다.&lt;pre&gt;&lt;code&gt;function app() {
return (
  &amp;lt;div&amp;gt;
    &amp;lt;h1&amp;gt;Hello?&amp;lt;/h1&amp;gt;
    &amp;lt;StudyList item=&amp;quot;abcd&amp;quot; id=&amp;quot;hoho&amp;quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
);
}
// 변환
function app() {
return createElement(&amp;quot;div&amp;quot;, null, createElement(&amp;quot;h1&amp;quot;, null, &amp;quot;Hello?&amp;quot;), createElement(StudyList, {
  item: &amp;quot;abcd&amp;quot;,
  id: &amp;quot;hoho&amp;quot;
}));
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;하지만 대문자로 시작한 App은 함수 그 자체로 넣어버린다. (Type이 function)&lt;pre&gt;&lt;code&gt;// 대문자일 때
console.log(&amp;lt;App /&amp;gt;);
// 변환
console.log(createElement(App, null));
// 소문자일 때
console.log(&amp;lt;app /&amp;gt;);
// 변환
console.log(createElement(&amp;quot;app&amp;quot;, null));&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;이처럼 리액트에서는 사용자 컴포넌트는 무조건 대문자로 시작해서 사용해야 한다. &lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;createElement&lt;/code&gt;로 생성된 객체를 실제 DOM으로 구성하는 render 함수를 작성한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;createElement&lt;/code&gt;로 생성된 객체를 보면 같은 형태로 계속 이루어져있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그리고 마지막에는 전부 appendChild를 해준다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 재귀함수
function renderElement(node) {
if (typeof node === &amp;quot;string&amp;quot;) {
  return document.createTextNode(node);
}
const el = document.createElement(node.type);
node.children.map(renderElement).forEach((element) =&amp;gt; {
  el.appendChild(element);
});
return el;
}
function render(vdom, container) {
container.appendChild(renderElement(vdom));
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;props&lt;/code&gt;를 넘겨받는 컴포넌트의 경우에도 같은 형식으로 적용할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function Row(props) {
return &amp;lt;li&amp;gt;{props.label}&amp;lt;/li&amp;gt;;
}
function StudyList(props) {
return (
  &amp;lt;ul&amp;gt;
    &amp;lt;Row label=&amp;quot;하하하&amp;quot; /&amp;gt;
    &amp;lt;li className=&amp;quot;item&amp;quot; label=&amp;quot;haha&amp;quot;&amp;gt;React&amp;lt;/li&amp;gt;
    &amp;lt;li className=&amp;quot;item&amp;quot;&amp;gt;Redux&amp;lt;/li&amp;gt;
    &amp;lt;li className=&amp;quot;item&amp;quot;&amp;gt;TypeScript&amp;lt;/li&amp;gt;
    &amp;lt;li className=&amp;quot;item&amp;quot;&amp;gt;Mobx&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
);
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;render&lt;/code&gt; 함수를 사용하여 real DOM의 HTML에 붙여 넣을 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render(&amp;lt;App /&amp;gt;, document.getElementById(&amp;quot;root&amp;quot;));&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  &lt;strong&gt;전체 소스는 &lt;a href=&quot;https://github.com/saseungmin/woowa_tech_learning_react_typescript/blob/master/week_2_1/react.js&quot;&gt;GitHub&lt;/a&gt; 확인해주세요!  &lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;  React의 클래스 컴포넌트와 함수 컴포넌트&lt;/h2&gt;
&lt;h3&gt;  클래스 컴포넌트&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;클래스 컴포넌트는 라이프사이클 API를 사용하여 state를 생성자에서 초기화할 수 있다.&lt;pre&gt;&lt;code&gt;class Hello extends React.Component {
constructor(props) {
  super(props);
  this.state = {
    count: 1
  };
}
componentDidMount() {
  this.setState({ count: this.state.count + 1 });
}
render() {
  return &amp;lt;p&amp;gt;안녕하세요!&amp;lt;/p&amp;gt;;
}
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/components-and-props.html&quot;&gt;react 공식문서&lt;/a&gt; 확인하기 ❗❗&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  함수 컴포넌트(Hook)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;함수 컴포넌트는 초창기에는 &lt;strong&gt;상태는 함수가 호출될 때 마다 생성되기 때문에&lt;/strong&gt; 유지될 수 없었기 때문에 상태를 관리하지 못한다고 여겨져 사용하지 않았다.&lt;/li&gt;
&lt;li&gt;아래처럼 안에 변수는 함수 내부의 상태처럼 여겨졌다.&lt;pre&gt;&lt;code&gt;function App() {
  let x = 10;
  return (
      &amp;lt;div&amp;gt;
          &amp;lt;h1&amp;gt;상태&amp;lt;/h1&amp;gt;
          &amp;lt;Hello /&amp;gt;
      &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;하지만 함수 컴포넌트도 Hooks가 나온 뒤 상태 관리를 할 수 있게 되었다.&lt;pre&gt;&lt;code&gt;function App() {
// 반환하는 값은 배열
const [counter, setCounter] = useState(1);
// const counter = result[0];
// const setCounter = result[2];
return (
  &amp;lt;div&amp;gt;
    &amp;lt;h1 onClick={() =&amp;gt; setCounter(counter + 1)}&amp;gt;상태{counter}&amp;lt;/h1&amp;gt;
    &amp;lt;Hello /&amp;gt;
  &amp;lt;/div&amp;gt;
);
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;h1 태그를 클릭하면 counter 상태가 1씩 증가한다.&lt;/li&gt;
&lt;li&gt;hook은 항상 최상위(at the Top Level)에서만 Hook을 호출해야 해야한다. &lt;a href=&quot;https://ko.reactjs.org/docs/hooks-rules.html&quot;&gt;react Hooks 규칙&lt;/a&gt; 참고&lt;/li&gt;
&lt;li&gt;최상위가 아닌 부분에서 호출 될 경우 전역 배열에 원하지 않는 값을 반환하여 문제가 발생 할 경우가 생길 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/hooks-intro.html&quot;&gt;공식 문서&lt;/a&gt;를 참고하자!  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  우아한 테크러닝 3기 3회차 후기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;이번 시간은 리액트를 직접 만들어보았다.  &lt;br&gt;만들어보면서 계속 파보면서 여태까지 아무것도 모르고 리액트를 사용했다고 생각했다..&lt;br&gt;이번 계기로 왜? 라는 질문을 계속 스스로에게 해야할 거 같다.&lt;br&gt;그냥 표면적으로 보이는 것만 보지 말고 원리를 이해하도록 노력해야 할 거 같다.&lt;br&gt;3회차까지 들으면서 김민태 강사님의 열정과 정성이 느껴져서 너무 좋았다.(2회차는 10시 반까지.. 3회차도 7시부터 QnA 시간..)&lt;br&gt;내일이면 반을 했는데 끝나면 아쉬울거 같다. 열심히 듣자. &lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>우아한 테크러닝 3기</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/15</guid>
      <comments>https://haranglog.tistory.com/15#entry15comment</comments>
      <pubDate>Wed, 9 Sep 2020 22:52:47 +0900</pubDate>
    </item>
    <item>
      <title>✔ 우아한 테크러닝 3기: React &amp;amp; TypeScript 2회차</title>
      <link>https://haranglog.tistory.com/14</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PtNwC/btqH0qjSAle/HBXENt5YKSmklFy6ALkrvK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PtNwC/btqH0qjSAle/HBXENt5YKSmklFy6ALkrvK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PtNwC/btqH0qjSAle/HBXENt5YKSmklFy6ALkrvK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPtNwC%2FbtqH0qjSAle%2FHBXENt5YKSmklFy6ALkrvK%2Fimg.jpg&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;✌ 9월 3일 우아한 테크러닝 3기 2회차 정리&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  2회차 강의 목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript에 대한 지식을 A~Z까지 할 수 있는 만큼 해보기! (70%)&lt;/li&gt;
&lt;li&gt;JavaScript의 아주 기초적인 내용보단 전체적인 흐름을 타면서 스펙들을 쭉 훑어보기&lt;/li&gt;
&lt;li&gt;상태관리자인 &lt;code&gt;Redux&lt;/code&gt;를 직접 만들어보기! 만들어보면서 어떤 기술로 이루어져있는지! (30%)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  JavaScript 훑어보기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;변수&lt;/strong&gt;에 이 &lt;strong&gt;값&lt;/strong&gt;이라는 것을 넣을 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var x = 10; //x: 변수 10: 값
let y = 10;
const z = 10;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  값에 대해서&lt;/h3&gt;
&lt;p&gt;  &lt;strong&gt;값에 대해서 굉장히 강조를 하셨다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;아주 바닥의 &lt;strong&gt;절대불변의 원칙&lt;/strong&gt;같은 것들을 세워놓고 &lt;em&gt;(ex. 1=1, 1+1=2 불변(공리))&lt;/em&gt; 이런 원칙을 충분히 &lt;strong&gt;이해&lt;/strong&gt;하면 아무리 복잡한 것이라도 그 원칙으로 하나씩 벗겨나가면 굳이 안 배우고도 이해하고 그것들을 파악해 나가는 장점이 있는데 &lt;strong&gt;프로그래밍 언어&lt;/strong&gt;도 마찬가지이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  JavaScript에서의 값&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;자바스크립트에서는 값이라고 정의한 녀석들은 변수에 넣을 수 있다.&lt;/li&gt;
&lt;li&gt;그렇다면, 문자열도 값이라고 하니까 변수에 넣을 수 있고, 숫자, 객채도 넣을 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;함수&lt;/strong&gt;도 자바스크립트에서 값이라고 규정했기 때문에 당연히 함수도 값으로써 변수에 넣을 수 있다. 그렇기 때문에 &lt;strong&gt;자바스크립트에서는 거의 모든 값이 값이다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  JavaScript에서의 함수&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;자바스크립트의 함수는 반드시 값을 반환하게 되어있다. (&lt;code&gt;undefinded&lt;/code&gt;라도..)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function foo(){
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;  &lt;strong&gt;값을 반환하는 두가지 방법&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;명시적으로 리턴하는 방법.(&lt;code&gt;return&lt;/code&gt;이 없으면 &lt;code&gt;undefinded&lt;/code&gt; 반환)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;호출할 때 앞에 &lt;code&gt;new&lt;/code&gt; 연산자로 호출하면 이 함수는 &lt;code&gt;new&lt;/code&gt; 연산자의 작용으로 명시적 리턴이 없어도 객체를 반환하는 &lt;strong&gt;인스턴스 객체&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;new foo();
let y = foo;&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;  JavaScript 함수가 굉장히 유연함(어려운) 점은?&lt;/h4&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/function&quot;&gt;함수 선언문(function declaration)&lt;/a&gt; (세미콜론이 없다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/function&quot;&gt;함수 표현식(function expression)&lt;/a&gt;(세미콜론이 붙는다.)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 함수 선언문(함수 정의문)
function foo(){}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;함수 표현식은 값이라고 취급했기(변수에 넣을 수 있다.) 때문에 세미콜론이 붙을 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 함수 표현식
const bar = function bar() {};
// bar안에 함수가 존재하니까 호출이 가능하다.
bar();&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이때 이 함수가 이름을 가져도 함수 바깥쪽에서는 이 안 쪽 &lt;code&gt;bar&lt;/code&gt;를 모른다. 그래서 &lt;code&gt;const bar&lt;/code&gt;변수가 함수 이름을 대신한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그러힉 떄문에 안쪽 선언된 &lt;code&gt;bar&lt;/code&gt;는 의미가 없어서 보통 &lt;strong&gt;이름 없는 함수&lt;/strong&gt;로 사용하여 생략이 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;표면적으로는 이렇게 생각할 수 있지만 정확히는 &lt;strong&gt;함수를 값으로 취급할 땐 이름을 생략할 수 있다라고 말한다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;함수가 값으로 취급해서 변수에 넣고 있기 때문에 이름을 생략할 수 있는 것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const bar = function () {};&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;만약 단독으로 이름 없는 함수를 사용한다면 에러가 발생한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function () {}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 상황은 함수를 값으로 취급하는 상황이 아니기 때문이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그렇다면 내가 이 함수를 값으로 취급하고 싶을 땐 명시적으로 자바스크립트 엔진에게 알려줘야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;보통 자바스크립트는 &lt;strong&gt;값을 만들 때 괄호&lt;/strong&gt;를 쓰게 되어있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(function () {})&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;하지만 이 상황은 함수를 만들기만 하고 넣지 않은 상태이다. 이유는 함수는 이름이 있어야 호출할 수 있기 때문이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;단 한가지 방법은 &lt;strong&gt;만들자마자 즉시 호출하는 방법&lt;/strong&gt;이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(function () {})();&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이것이 _Immediate Function Call_이라고 해서 만들자마자 호출이 되고 호출이 끝나고 리턴이 되면 &lt;strong&gt;이름이 없기 때문에 다시 호출할 수 없는 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로그래밍 상에서 단 한 번만 호출하고 싶은 상황이 플요할 때 이런 방법을 사용한다.(초기화 코드)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  콜백함수와 합성 함수(Higher-Order Function, 일급 함수)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;위에서의 개념을 확장하여 &lt;strong&gt;변수가 어디 등장하는지 생각해보자.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;변수는 다시 생각해보면 결국 변수를 읽으면 &lt;strong&gt;값&lt;/strong&gt;이다. 그렇기 때문에 결국 변수도 값이되는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그러면 변수는 값이 등장하는 위치정도라고 생각할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function foo(x){
  return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;인자로 &lt;code&gt;x&lt;/code&gt;를 넣어주면 이 &lt;code&gt;x&lt;/code&gt;도 결국 변수이자 값이 되는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;리턴할 때도 값이니까 함수를 리턴할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;함수에서도 값이 위치할 수 있다는 소리로 &lt;code&gt;foo&lt;/code&gt;함수를 호출할 때 값으로써 &lt;code&gt;x&lt;/code&gt;로 전달한 값을 함수도 전달할 수 있다는 얘기이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function foo(x) {
x();
return function () {};
}
const y = foo(function () {});&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다시 &lt;code&gt;foo&lt;/code&gt;함수가 리턴하는 값은 함수이기 떄문에 다시 함수를 받을 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 인자로 함수를 전달하는 방법을 &lt;strong&gt;콜백함수&lt;/strong&gt;라고 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;또한, 값이 함수를 리턴되는 겂을 소프트웨어 공학에서는 보통 &lt;strong&gt;함수 합성이라고 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;실행되는 순간 만들어내는 테크닉일 때 많이 쓰인다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;함수를 인자로 받고, 함수를 리턴하는 함수, 이런 함수를 &lt;strong&gt;일급함수(Higher-Order Function)&lt;/strong&gt; 라고 한다. 리액트에서는 같은 개념으로 (HOC, Higher-Oder Component)가 있다.&lt;/p&gt;
&lt;p&gt;함수를 이해할 때 함수를 어떤 값을 입력으로 주면 계산해서 출력하는 애라고 이해하지말고, 코드를 묶고 있는 값으로 이해하면 훨씬 쉽다.&lt;br&gt;함수를 리턴하는 함수는 어떤 계산 결과를 즉시 확정해서 내보낼 수 없는 것이다.&lt;br&gt;완벽하게 확정할 수 없는 것을 확정할 수 있는 코드를 줘서 리턴하는 것과 비슷한 컨셉이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  &lt;strong&gt;JavaScript의 문법은 크게 보면 두가지로 나눌 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;식: 코드를 실행하면 실행의 결과가 값으로 마무리가 되면 그건 전부다 식이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ex)
0, 1+10, foo(), 1+10+foo()&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;문: 실행했을 때 실행의 결과가 안나온다.&lt;/p&gt;
&lt;p&gt;조건문(conditional statement) : if, switch, (삼항연산자는 식)&lt;br&gt;반복문: while, do while, for.. (foreach, map, reduce등은 함수 호출 식)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;식과 문의 차이는 세미콜론이 있냐 없냐 차이로 결국 세미콜론은 식의 마무리라 할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  Arrow Function(화살표 함수)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;return&lt;/code&gt;의 생략은 이 함수가 한 줄이라는 것 자체가 바로 &lt;code&gt;return&lt;/code&gt;하겠다라고 하는 것을 함유하고 있다.&lt;/li&gt;
&lt;li&gt;화살표 함수는 이름이 없기 때문에 &lt;strong&gt;재귀호출이 불가능하다.&lt;/strong&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const bar = x =&amp;gt; x*2;
console.log(bar(2)); // 4
//
const myX = 10;
const myY = (x) =&amp;gt; x * 10;
console.log(myX, myY(1)); // 10, 10&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;화살표 함수는 자신의 &lt;code&gt;this&lt;/code&gt;가 없다.&lt;/li&gt;
&lt;li&gt;화살표 함수는 &lt;strong&gt;바로 바깥 범위에서 &lt;code&gt;this&lt;/code&gt;를 찾는 것으로 검색을 끝내게 된다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;화살표 함수는 &lt;code&gt;arguments&lt;/code&gt; 객체를 바인드 하지 않는다.&lt;/li&gt;
&lt;li&gt;화살표 함수는 생성자로서 사용될 수 없으며 &lt;code&gt;new&lt;/code&gt;와 함께 사용하면 오류가 발생한다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Foo = () =&amp;gt; {};
const foo = new Foo(); // TypeError: Foo is not a constructor&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;화살표 함수는 &lt;code&gt;prototype&lt;/code&gt; 속성이 없다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Foo = () =&amp;gt; {};
console.log(Foo.prototype); // undefined&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  &lt;strong&gt;&lt;code&gt;new&lt;/code&gt;연산자를 호출하면 내부적으로 어떤 메커니즘이 작동하는가?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;빈 객체를 만들고 함수한테 전달하는데 이 함수한테 &lt;strong&gt;빈 객체가 무엇인지를 알려줄 방법&lt;/strong&gt;으로 &lt;code&gt;this&lt;/code&gt;라고 명명하는 것이다.&lt;/li&gt;
&lt;li&gt;그래서 이 함수 안에 &lt;code&gt;this&lt;/code&gt;는 &lt;code&gt;new&lt;/code&gt;연산자를 호출했을 때 생성된 빈 객체이다. (&lt;strong&gt;동적 바인딩&lt;/strong&gt;)&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function my() {
//자바스크립트가 생성한 빈객체
// 동적 바인딩을 이용하는 것
this.name = 10;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new&lt;/code&gt;연산자로 호출한 함수는 &lt;code&gt;return&lt;/code&gt;이 명시적으로 없어도 반환 값으로 새로 만든 &lt;code&gt;this&lt;/code&gt;가 리턴이 된다.&lt;/li&gt;
&lt;li&gt;이런 과정이 내부적인 메커니즘은 생성자가 동작된 것이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new&lt;/code&gt;연산자는 이렇게 새로운 객체를 호출할 때마다 만들어 낸다.&lt;/li&gt;
&lt;li&gt;함수가 그런 객체를 구조적으로 형태를 만들어 내는 의미에서 생성자라고 부른다. 내부적인 메커니즘으로는 &lt;code&gt;prototype&lt;/code&gt;이라는 메커니즘을 사용한다.(다음시간에..)&lt;/li&gt;
&lt;li&gt;이렇게 만들어진 객체를 &lt;strong&gt;인스턴스 객체&lt;/strong&gt;라고 한다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 인스턴스 객체
const g = new my();
if (g instanceof my) {
  console.log(&amp;#39;true&amp;#39;); //  true
}
console.log(g); // my {name: 10}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;그래서 es6에서 좀 더 명시적으로 나타내기 위해서 &lt;code&gt;class&lt;/code&gt;가 등장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  Class&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Class&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class bar {
  // 생성자
  constructor() {
    this.age = 10;
  }
}
console.log(new bar()); // bar {age: 10}&lt;/code&gt;&lt;/pre&gt;
  &lt;strong&gt;함수와 클래스가 다른 점&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;constructor&lt;/code&gt;가 명확하게 들어나있다.(명시적)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new&lt;/code&gt;연산자를 앞에다 붙여서 호출을 했을 때 &lt;strong&gt;함수&lt;/strong&gt;는 &lt;code&gt;new&lt;/code&gt; 연산자와 &lt;code&gt;new&lt;/code&gt; 연산자 없이 호출이 가능하기 때문에 사용자 입장에서는 &lt;code&gt;new&lt;/code&gt; 함수를 감지할 수 없다. (명시적이지 못하다.)&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 앞에 대문자로 생성자 함수를 구분하는 처절함..
const Bar(){
  this.age = 10;
}
const bar = new Bar();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;class&lt;/code&gt;로 만들면  &lt;code&gt;new&lt;/code&gt;로 &lt;strong&gt;호출하지 않으면&lt;/strong&gt; 호출이 &lt;strong&gt;불가능&lt;/strong&gt;하다.&lt;/li&gt;
&lt;li&gt;여기서 &lt;code&gt;this&lt;/code&gt;는 &lt;strong&gt;실행 컨텍스트 지시어&lt;/strong&gt;라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  this와 클로저(closure)&lt;/h3&gt;
&lt;h4&gt;  this&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;person 객체&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const person = {
name: &amp;quot;사승민&amp;quot;,
// arrow는 this가 변하지 않는다.
getName() {
  // this는 person
  // 실행 컨텍스트
  return this.name;
}
};
console.log(person.getName()); // 사승민&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;person&lt;/code&gt;의 &lt;code&gt;name&lt;/code&gt;은 &lt;strong&gt;실행 컨텍스트&lt;/strong&gt;라고 한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;실행 컨텍스트&lt;/strong&gt;는 실행하는 순간 소유자를 자바스크립트 엔진이 실행할 때 확인한다. 그래서 함수 안에서 &lt;code&gt;this&lt;/code&gt;를 사용했을 때 호출하는 순간에 소유자와 연결된다.&lt;/li&gt;
&lt;li&gt;❗ 여기서 문제는 소유자가 사라지는 순간이 있다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const man = person.getName;
console.log(man()); // 값없음&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;this&lt;/code&gt;를 선언하지 않았기 때문에 실행하는 순간 호출자가 확인이 안되서 전역 스코프의 전역 객체에 연결된다는 룰을 가지고 있다.(브라우저의 전역객체는 &lt;code&gt;window&lt;/code&gt; 객체)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  &lt;strong&gt;this를 이렇게 만든 이유&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;this&lt;/code&gt;를 이렇게 저렇게 바꾸어가며 활용해라.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;this&lt;/code&gt;를 고정하는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;모든 함수가 제공하는 &lt;code&gt;bind()&lt;/code&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const man1 = person.getName.bind(person);
console.log(man1()); // 사승민&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;call()&lt;/code&gt; : &lt;code&gt;this&lt;/code&gt;로 선택한 값과 매개변수로 함수를 호출한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apply()&lt;/code&gt; : &lt;code&gt;this&lt;/code&gt;로 선택한 값과 매개변수로 함수를 호출한다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;person.getName.call(person);
// &amp;quot;사승민&amp;quot;
person.getName.apply(person);
// &amp;quot;사승민&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;call()&lt;/code&gt;과 &lt;code&gt;apply()&lt;/code&gt;의 동작은 같고, 차이점은 함수에 매개변수를 넘기는 방법으로 &lt;code&gt;apply&lt;/code&gt;의 매개변수는 배열이고 &lt;code&gt;call&lt;/code&gt;은 문자열을 나열한 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  클로저(closure)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;함수가 호출되면 함수의 스코프가 생성된다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function foo(x) {
// 스코프
return function(){
  return x;
}
}
const result = foo(10);
console.log(result()); // 10&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;이렇게 안쪽 &lt;code&gt;function&lt;/code&gt;은 그 스코프 안에 만들어진다.&lt;/li&gt;
&lt;li&gt;이때 &lt;code&gt;foo&lt;/code&gt; 함수 안에서 접근하는 어떤 변수가 자기 스코프 체인 바깥 쪽 &lt;strong&gt;자신을 감싸고 있는 스코프 체인에 있는 변수&lt;/strong&gt;라면 그걸 함께 들고 있는 영역을 &lt;strong&gt;클로저&lt;/strong&gt;라고 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;foo&lt;/code&gt;함수가 리턴이 될 때 &lt;code&gt;foo&lt;/code&gt;함수의 스코프는 같이 사라지는데 받는 인자 &lt;code&gt;x&lt;/code&gt;는 없어질 거 같지만 실제 스코프에 있는 &lt;code&gt;x&lt;/code&gt;는 없어지고 그걸 따로 저장하고 있는 안쪽 &lt;code&gt;function&lt;/code&gt; 함수는 &lt;code&gt;x&lt;/code&gt;를 &lt;strong&gt;클로저 영역에 따로 저장하고 있기 때문에&lt;/strong&gt; 이 때 호출될 때 여전히 &lt;strong&gt;&lt;code&gt;x&lt;/code&gt;가 존재한다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;이렇게 값을 따로 저장하고 있기 때문에 값을 보호할 용도로 많이 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  &lt;strong&gt;클로저를 사용한 모듈 패턴&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const person = {
  age: 10,
}
person.age = 500;
// 클로저이요ㅕㅇ해서 막았따
function makePerson(){
  let age = 10;
  // 모듈패턴
  // 값을 보호해야할때 이렇게 사용했다.
  return {
    getAge(){
      return age;
    },
    setAge(x){
      // 1보다 작고 130보다 크면 age
      age = x &amp;gt; 1 &amp;amp;&amp;amp; x &amp;lt; 130 ? x: age;
    }
  }
}
let p = makePerson();
console.log(p.getAge()); // 10
p.setAge(200);
console.log(p.getAge()); // 10 200은 130보다 크니 기본 age
p.setAge(30);
console.log(p.getAge()); // 30&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;  비동기&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;setTimeout&lt;/code&gt;을 사용한 비동기&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;setTimeout(function foo(x) {
    console.log(&amp;quot;아싸&amp;quot;);
  setTimeout(function (y) {
    console.log(&amp;quot;우싸&amp;quot;);
  }, 2000);
}, 1000);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setTimeout&lt;/code&gt;을 사용하여 비동기적으로 실행하였다.&lt;/li&gt;
&lt;li&gt;1초가 지난 뒤 &lt;code&gt;아싸&lt;/code&gt;가 콘솔에 찍히고, 2초가 지난 뒤 &lt;code&gt;우싸&lt;/code&gt;가 찍힌다.&lt;/li&gt;
&lt;li&gt;이렇게 여러 단계로 함수를 실행하게 되면 함수에 함수에 함수.. 계속 이렇게 되어 복잡한 구조로 나아간다.&lt;/li&gt;
&lt;li&gt;이러한 복잡한 구조를 &lt;strong&gt;Callback hell&lt;/strong&gt;또는 &lt;strong&gt;콜백 지옥&lt;/strong&gt;이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  Promise(프로미스)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;위와 같이 콜백 지옥을 해결하기 위해 나온 것이 &lt;code&gt;Promise&lt;/code&gt;이다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// promise1
const p1 = new Promise((resolve, reject) =&amp;gt; {
    // 이것 또한 클로저
  setTimeout(() =&amp;gt; {
    // 이 안에서만 호출
    resolve(&amp;quot;응답1&amp;quot;);
  }, 1000);
});
// promise2
const p2 = new Promise((resolve, reject) =&amp;gt; {
  setTimeout(() =&amp;gt; {
    resolve(&amp;quot;응답2&amp;quot;);
  }, 1000);
});
// 메소드 체이닝
p1.then(
  p2.then((r) =&amp;gt; console.log(r)))
  .catch(function(){});
// 응답2
// Promise {&amp;lt;fulfilled&amp;gt;: &amp;quot;응답1&amp;quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new Promise&lt;/code&gt;는 인스턴스 객체를 리턴하는데 이 인스턴스 객체 안에는 &lt;code&gt;then&lt;/code&gt;이라고 하는 속성(메소드) 함수가 들어가 있다.&lt;/li&gt;
&lt;li&gt;이 비동기 작업이 성공하면 &lt;code&gt;then&lt;/code&gt;을 사용하며 &lt;code&gt;then&lt;/code&gt;은 resolve를 호출한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reject&lt;/code&gt;함수를 호출하면 &lt;code&gt;catch&lt;/code&gt;안에 기술된 함수를 호출해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  비동기 함수(Async/await)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;비동기 함수의 background는 결국 &lt;code&gt;Promise&lt;/code&gt;이다.&lt;/li&gt;
&lt;li&gt;비동기함수를 사용하면 동기적인 코드로 작성할 수 있다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const delay = ms =&amp;gt; new Promise(resolve =&amp;gt; setTimeout(resolve, ms));
async function main(){
console.log(&amp;#39;1&amp;#39;);
try {
  const x = await delay(2000);
} catch (error) {
  //reject
  console.log(error);
}
console.log(&amp;#39;2&amp;#39;);
}
main();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;1이 찍히고 &lt;code&gt;delay&lt;/code&gt; 함수가 실행되고 2초 뒤 2가 실행된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;await&lt;/code&gt;연산자는 &lt;code&gt;Promise&lt;/code&gt; 객체 즉, &lt;code&gt;thenable&lt;/code&gt; 객체가 있으면 안에서 &lt;code&gt;resolve&lt;/code&gt;가 호출됬을 때 &lt;code&gt;resolve&lt;/code&gt; 함수가 반환하는 값을 마치 리턴 값으로 넘겨주는 식으로 된다.&lt;/li&gt;
&lt;li&gt;여기서 javascript에서 많이 사용하지 않던 &lt;code&gt;try/catch&lt;/code&gt;문을 사용하여 &lt;code&gt;reject&lt;/code&gt;가 에러로 잡히게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  &lt;strong&gt;결국 콜백의 깊은 depth를 일자로 펼치기 위해서 Promise가 만들어졌고 그 Promise의 backgroud 위에 진짜 일자 동기 코드를 쓰기 위해서 async 함수가 만들어졌다.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;  제너레이터&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;redux-saga&lt;/code&gt;할 때 진행..&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  &lt;strong&gt;커링이란?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인자가 여러 개 있을 때 인자 하나하나가 함수 하나씩 하나씩 배분해서 쪼개는 것을 &lt;strong&gt;커링&lt;/strong&gt;이라고 부른다.&lt;/li&gt;
&lt;li&gt;클로저는 항상 커링이 된다. &lt;/li&gt;
&lt;li&gt;클로저는 커링을 위한 기술중 하나이다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function foo(a){
return function(b){
  return function(c){
    return a + b + c;
  }
}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  Redux 만들어보기&lt;/h2&gt;
&lt;h3&gt;  리덕스의 store 생성&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;중앙에 스토어 하나를 생성하는 개념이다.(객체이다.)&lt;/li&gt;
&lt;li&gt;객체를 만드는 함수를 제공하는데 그것이 리덕스에서 &lt;code&gt;createStore&lt;/code&gt;이다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// redux.js
export const createStore = () =&amp;gt; {
  const state = {};
  return state;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;리덕스의 컨셉은 밑으로 흘러가면서 데이터를 절대 컴포넌트가 직접적으로 변경 불가하다. 즉, &lt;strong&gt;immutable&lt;/strong&gt;하고 컴포넌트는 데이터를 받아서 그리기만 해준다.&lt;/li&gt;
&lt;li&gt;그렇기 때문에 클로저로 상태를 숨겨주어야 한다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export const createStore = () =&amp;gt; {
  const state = {};
  // 새로운 객체를 내보낸다.
    const getState = () =&amp;gt; ({ ...state });
  return {getState};
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;바깥에서 상태를 알고싶으면 &lt;code&gt;store&lt;/code&gt;객체의 &lt;code&gt;getState&lt;/code&gt;를 호출해야만 볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  상태를 변경하기&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;그 상태를 바꾸는 코드는 리덕스한테 상태를 바꾸는 함수로 전달해준다.&lt;/li&gt;
&lt;li&gt;이것을 &lt;strong&gt;리듀서&lt;/strong&gt;라고 부른다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// index.js
function reducer(state = {}, action) {
if (action.type === &amp;quot;increment&amp;quot;) {
  return {
    ...state,
    count: state.count ? state.count + 1 : 1
  };
  //state.abc = &amp;#39;OK&amp;#39;;
} else if (action.type === &amp;quot;reset&amp;quot;) {
  return {
    ...state,
    count: action.resetCount
  };
}
return state;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;createStore&lt;/code&gt;한테 리듀서 함수를 전달해주어야 한다.&lt;/li&gt;
&lt;li&gt;그러기 위해서는 약속이 필요한데 리듀서 함수의 상태가 변경될 때 이 리듀서 함수를 호출하면 현재 상태를 넘겨준다.&lt;/li&gt;
&lt;li&gt;무엇을 바꾸는지 모르기 때문에 간단하게 이 문제를 해결할 수 있는데 두번째 인자에는 객체 하나를 풀고, 이 객체는 &lt;code&gt;type&lt;/code&gt;이라는 문자열을 갖고있는 속성(&lt;code&gt;action&lt;/code&gt;)을 현재 &lt;code&gt;state&lt;/code&gt; 상태로 업데이트 시켜준다.(새로운 객체를 반환)&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// createStore 한테 리듀서 함수 전달.
// redux.js
export function createStore(reducer) {
  let state;
  const getState = () =&amp;gt; ({ ...state });
  const dispatch = (action) =&amp;gt; {
    state = reducer(state, action);
  };
  return {
    getState,
    dispatch
  };
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// index.js
import { createStore } from &amp;quot;./redux&amp;quot;;
function reducer(state = {}, action) {
  if (action.type === &amp;quot;increment&amp;quot;) {
      return {
        ...state,
        count: state.count ? state.count + 1 : 1
      };
        //state.abc = &amp;#39;OK&amp;#39;;
  } else if (action.type === &amp;quot;reset&amp;quot;) {
      return {
        ...state,
        count: action.resetCount
      };
  }
  return state;
}
const store = createStore(reducer);
function actionCreator(type, data) {
  return {
    ...data,
    type: type
  };
}
store.dispatch(actionCreator(&amp;quot;increment&amp;quot;)); // Object {count: 1}
store.dispatch(actionCreator(&amp;quot;increment&amp;quot;)); // Object {count: 2}
store.dispatch(actionCreator(&amp;quot;increment&amp;quot;)); // Object {count: 3}
store.dispatch(actionCreator(&amp;quot;increment&amp;quot;)); // Object {count: 4}
store.dispatch(actionCreator(&amp;quot;increment&amp;quot;)); // Object {count: 5}
store.dispatch(actionCreator(&amp;quot;reset&amp;quot;));     // Object {count: undefined}
store.dispatch(actionCreator(&amp;quot;increment&amp;quot;)); // Object {count: 1}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;action 문자열을 상수처리와 &lt;code&gt;store.dispatch(actionCreator(&amp;quot;increment&amp;quot;));&lt;/code&gt;이렇게 계속 쓸 수는 없다. &lt;code&gt;increment&lt;/code&gt;함수와 &lt;code&gt;reset&lt;/code&gt;함수를 생성해준다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// index.js
import { createStore } from &amp;quot;./redux&amp;quot;;
const INCREMENT = &amp;quot;increment&amp;quot;;
const RESET = &amp;quot;reset&amp;quot;;
function reducer(state = {}, action) {
  if (action.type === INCREMENT) {
      return {
        ...state,
        count: state.count ? state.count + 1 : 1
      };
        //state.abc = &amp;#39;OK&amp;#39;;
  } else if (action.type === RESET) {
      return {
        ...state,
        count: action.resetCount
      };
  }
  return state;
}
const store = createStore(reducer);
function actionCreator(type, data) {
  return {
    ...data,
    type: type
  };
}
// 증가
function increment() {
    store.dispatch(actionCreator(INCREMENT));
}
// 리셋
function reset() {
    store.dispatch(actionCreator(RESET, { resetCount: 0 }));
}
increment(); // Object {count: 1}
increment(); // Object {count: 2}
increment(); // Object {count: 3}
increment(); // Object {count: 4}
increment(); // Object {count: 5}
reset();     // Object {count: 0}
increment(); // Object {count: 1}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  변경된 상태를 확인하기&lt;/h3&gt;
&lt;p&gt;  &lt;strong&gt;스토어에 바뀐 상태를 어떻게 알까?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;직접 &lt;code&gt;getState&lt;/code&gt;를 호출해서 알 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;하지만 UI에서 언제 store가 바뀔지 알아서 코드로 적시할 수 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;리듀서에서 리덕스가 &lt;code&gt;dispatch&lt;/code&gt;가 불러서 리듀서 함수를 호출해서 UI가 쓴 코드로 &lt;code&gt;state&lt;/code&gt;를 바꾼 것을 방금 내부의 클로저에 잡혀있는 내 &lt;code&gt;store&lt;/code&gt;에다가 변경됬다는 것을 바깥 쪽으로 통지를 해주어야 한다. 즉, 함수를 호출해서 사용한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;통지함수 &lt;code&gt;update&lt;/code&gt;함수 생성&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;subscribe&lt;/code&gt; 함수는 인자를 하나 받아서 &lt;code&gt;listeners&lt;/code&gt;배열에 저장한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;listeners&lt;/code&gt;에 &lt;code&gt;subscribe&lt;/code&gt;함수에서 받은 &lt;code&gt;function&lt;/code&gt;을 넣는다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// redux.js
export function createStore(reducer) {
  let state;
  const listeners = [];
  const getState = () =&amp;gt; ({ ...state });

  const dispatch = (action) =&amp;gt; {
    state = reducer(state, action);
    listeners.forEach((fn) =&amp;gt; fn());
  };

  const subscribe = (fn) =&amp;gt; {
    listeners.push(fn);
  };

  return {
    getState,
    dispatch,
    subscribe
  };
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;//index.js
// 생략..
const store = createStore(reducer);
function update() {
    console.log(store.getState());
}
store.subscribe(update);
// 생략..&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  리덕스 만들기 전체 소스는 &lt;a href=&quot;https://github.com/saseungmin/woowa_tech_learning_react_typescript/tree/master/week_1_2/src&quot;&gt;GitHub&lt;/a&gt; 참조 ✌&lt;/p&gt;
&lt;h2&gt;  2회차 후기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;이번 시간은 정말 많은 것을 배운 시간이였다!! ✨&lt;br&gt;리액트를 사용하고 공부를 하였지만, 직접 리덕스를 만들어보고 그 안에 어떤식으로 흘러가고 어떤 기술이 쓰였는지에 대해서 한번도 생각해보지 못했던 내 자신한테 부끄러워지만 이번 강의로 확실히 알고가서 너무 좋았다.  &lt;br&gt;앞으로 표면적으로 보이는 것만 사용하지 않고, 한 번더 생각하고 &lt;em&gt;이건 왜 이렇게 되는 걸까?&lt;/em&gt; 라는 고민을 해보도록 노력해야겠라는 생각을 많이 했다.  &lt;br&gt;또한, 자바스크립트의 단순하고 당연히 이정도는 알고있지고 생각했던 건 오산이였다..&lt;br&gt;자바스크립트의 기본적인 개념이더라도 &lt;em&gt;이런 과정이 있어서 이렇게 생겼났다&lt;/em&gt; 라는 것을 명확하게 설명해주셔서 간단하다고 생각했던 개념들도 다른관점에서 볼 수 있던 계기가 되었다.  &lt;br&gt;앞으로의 강의가 더욱 더 기대가 되고 이번에 운 좋게 강의를 들어서 너무 좋은 기회를 잡았다고 생각이 들었다!!  &lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>우아한 테크러닝 3기</category>
      <category>react</category>
      <category>typescript</category>
      <category>우아한 테크러닝 3기</category>
      <category>우아한 테크러닝 3기 2회차</category>
      <category>우아한 테크러닝 후기</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/14</guid>
      <comments>https://haranglog.tistory.com/14#entry14comment</comments>
      <pubDate>Mon, 7 Sep 2020 22:45:59 +0900</pubDate>
    </item>
    <item>
      <title>✔ 우아한 테크러닝 3기: React &amp;amp; TypeScript  1회차</title>
      <link>https://haranglog.tistory.com/13</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;2384&quot; width=&quot;410&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z5mx8/btqHGVjXCCC/5UdeNKV2HdFJutNmtugnFk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z5mx8/btqHGVjXCCC/5UdeNKV2HdFJutNmtugnFk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z5mx8/btqHGVjXCCC/5UdeNKV2HdFJutNmtugnFk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ5mx8%2FbtqHGVjXCCC%2F5UdeNKV2HdFJutNmtugnFk%2Fimg.jpg&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;2384&quot; width=&quot;410&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;  우아한 테크러닝 3기 교육생&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;원래 오프라인으로 100명정도 뽑아서 한다고 하는데, 이번에는 코로나의 영향으로 온라인으로 진행되었습니다.&lt;/li&gt;
&lt;li&gt;거기다가 이번에는 코로나 때문에 띄어 앉아야해서 약 30명 정도 뽑을 생각이시라고 하셨지만, 코로나가 심해져 온라인으로 진행되서 약 400여명의 교육생으로 뽑았다고 하셨습니다.&lt;/li&gt;
&lt;li&gt;근데 이 교육을 듣고싶어서 지원한 사람만 1200여명..   열정적인 사람이 굉장히 많은 거 같다..&lt;/li&gt;
&lt;li&gt;이번 교육은 zoom을 이용해서 온라인으로 진행되었고, 강사님은 우아한형제들의 김민태님(웹프론트엔드개발 그룹장)이 진행하셨습니다.&lt;/li&gt;
&lt;li&gt;또한, 9월 한달동안 화, 목으로 진행되고 총 8회차로 진행될 예정이고 저도 계속 끝날때마다 블로깅을 할 예정입니다. ✌&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  우아한 테크러닝 3기 그 첫 시간 소개&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;첫 시간이라 그런지 교육적인 부분보다 굉장히 좋은 말씀을 많이 해주셨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  우리들의 고민..&lt;/h3&gt;
&lt;h4&gt;  내가 지금 하고 있는게 맞는 것인가? (내가 지금 잘하고 있나..?)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;이 강의를 신청할 때 왜 이 테크러닝을 듣고싶은가에 대한 질문이 있었는데 거기의 대부분의 고민&lt;ul&gt;
&lt;li&gt;개발을 할때 동작은 되고 해결은 되는데 이게 잘하고 있는건지에 대한 의구심이 가장 크다. 때문에 이 테크러닝을 통해서 &lt;strong&gt;네트워킹을 하고싶고 시니어들한테 받는 피드백과 조언들을 원한다.&lt;/strong&gt; (코드 품질에 대한 문제, 코드리뷰)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;아키텍처나 설계&lt;/strong&gt;에 대한 고민.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;적정 기술&lt;/strong&gt;에 대한 고민으로 react 상태관리를 해야할 때, &lt;code&gt;Mobx&lt;/code&gt;를 써야하나, &lt;code&gt;Redux&lt;/code&gt;를 사용해야하나에 대한 고민&lt;/li&gt;
&lt;li&gt;살재로 면접볼때 개발자들에게 &lt;em&gt;이 기술을 왜 선택했어요?&lt;/em&gt; 물어봤을 때, 제대로 고민해서 기술을 선택한 사람들은 굉장히 드물었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;그래서 그런 것들은 포괄해서 전달하고 싶은 것들은 ❓&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;도구의 발전&lt;ul&gt;
&lt;li&gt;20~30년 전에는 바닥(예를 들어 Node를 만든다.)부터 만들어 Hello World를 띄우기 이런것들의 고민이였다면, 지금은 도구의 발달로 생산성 향상에 도움을 주는 것들이 많이 생겨났다.&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;h3&gt;  4주간 진행하면서 자주 들을 키워드&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;450&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJiNXS/btqHK33fnUu/L5oT9j67020h1mr3XV75R1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJiNXS/btqHK33fnUu/L5oT9j67020h1mr3XV75R1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJiNXS/btqHK33fnUu/L5oT9j67020h1mr3XV75R1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJiNXS%2FbtqHK33fnUu%2FL5oT9j67020h1mr3XV75R1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;450&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;상태의 관리와 어떻게 다룰 것인가?&lt;/li&gt;
&lt;li&gt;환경: 런타임 환경뿐아니라 개발환경에 대한 큰 범주&lt;/li&gt;
&lt;li&gt;제품: 실제 서비스에 나갈 제품에 대한 것&lt;/li&gt;
&lt;li&gt;미션: 그래서 무엇보다 어떤 도구를 사용하더라도 그 도구가 원천적으로 해결하고자 했던 &lt;strong&gt;미션&lt;/strong&gt;을 잘 이해나는 것이 그 도구를 가장 잘 사용하는 방법이다.&lt;/li&gt;
&lt;li&gt;상대적: 이런것들이 모두 상대적인것들.. 이해하고 적용하는 것에 따라서 한마디로 정의하기는 에매&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  이 강의 목표&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;각각에 대한 기본기를 알려주고 디테일한 부분은 포인트를 집어주고 나머지는 그 부분에 대해서 계속 학습을 이어가면서 쌓아가고, 이 강의가 시작해서 끝났다고 끝이 아니고 네트워킹을 이어갈 수 있게 만들 수 있게 하고 싶다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  강의를 하면서 주로 다룰 도구&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/play&quot;&gt;TypeScript Playground&lt;/a&gt; : 4.0이후 굉장히 좋아짐&lt;/li&gt;
&lt;li&gt;예제는 &lt;a href=&quot;https://codesandbox.io/index2&quot;&gt;코드 샌드박스&lt;/a&gt;에서 만든다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/&quot;&gt;react 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redux.js.org/&quot;&gt;redux 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mobx.js.org/README.html&quot;&gt;Mobx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redux-saga.js.org/&quot;&gt;Redux-Saga 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;UI 프레임워크로는 &lt;a href=&quot;https://blueprintjs.com/&quot;&gt;blueprintjs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;테스팅관점에서 사용 &lt;a href=&quot;https://testing-library.com/&quot;&gt;Testing-library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  TypeScript&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Type을 명시하는 것.&lt;/li&gt;
&lt;li&gt;타입 추론을 할 수 있는 &lt;strong&gt;타입을 명시하지 않아도&lt;/strong&gt; TypeScript가 &lt;strong&gt;알아서 추론&lt;/strong&gt;해서 타입을 세팅해준다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;let foo = 10;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;실제로 문제 일어날 때 다른 타입의 값을 넣을 때 오류가 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// 타입을 명시적으로 적는 방법으로 변수뒤에다 콜론을 하고 뒤에다 적는다. 
let foo : number = 10; foo = false;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;  명시적인 것과 암묵적인 것&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;추론은 안쪽에서 일어나기 때문에 암묵적이니까 명시적인 것보다는 읽는 사람한테 전달해주는 정보량이 적다.&lt;/li&gt;
&lt;li&gt;몇 십년 전에는 코드가 복잡해도 잘 짜여저 보였지만, 요즘은 난해한 코드보단 명시적이고 읽기 쉬운 코드(코드가 길더라도)를 선호한다.&lt;/li&gt;
&lt;li&gt;타입 스펙을 만드는 개발자들도 비슷한 생각을 하고 있고, 또 이것이 요즘의 트랜드.&lt;/li&gt;
&lt;li&gt;JavaScript 또한 예전 스펙에서 요즘 나오는 새로운 문법들이 명시적으로 표현을 확장 시키는 것이 보인다.&lt;/li&gt;
&lt;li&gt;대표적으로 가변인자 같은 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// 밖에서 봤을 땐 좋지 않구나. 
// 함수 내부에 인자가 아무것도 없기 때문에 가변인자를 받는지 모른다. 암묵적.. 
function bar() { 
    arguments. 
    return 0; 
}
bar(10,20);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;그래서 spread operator(스프레드 문법)이 나온것도 이런 이유.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function bar(...args) { 
  return 0; 
}
bar(10,20);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;  type alias&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;원시타입은 일반화되어있기 때문에 다양하게 사용가능하다.&lt;/li&gt;
&lt;li&gt;하지만 타입에 의미를 부여하고 싶을 땐 단점이 된다.&lt;/li&gt;
&lt;li&gt;그래서 표현력을 넣고 싶을때 &lt;code&gt;type alias&lt;/code&gt;가 스펙을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// 대입처럼 보이지만 type alias 오른쪽에있는 타입의 별명을 붙여준다. 
type Age = number; 
let age: Age = 10;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;객체 타입&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// 객체 type alias 
type Foo = { 
  age: Age; // 세미콜론 
  name: string; 
}

const foo: Foo = {  
  age: 10, // 콤마  
  name: &amp;#39;kim&amp;#39;  
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;결국 최종적인 타입은 Primitive type.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  interface&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;type alias와 비슷하지만 둘 중에 어떤 상황에서 어떤 것을 사용하는 것이 좋을까? 에 대한 얘기는 추후에 알려준다고 하신다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;type Age = number; 

interface Bar { 
  age: Age; 
  name: string; 
} 

const bar: Bar = { 
  age: 10, 
  name: &amp;quot;Kim&amp;quot;, 
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;  에러 검출&lt;/h3&gt;
&lt;h4&gt;  타입스크립트 문법 요소에는 크게 두가지가 존재&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;컴파일 타임에서 작동되는 요소 (type Alias, generic, interface)&lt;/li&gt;
&lt;li&gt;런타임까지 가는 경우 에러를 잡는 요소 (JavaScript로 반환되는 요소)&lt;ul&gt;
&lt;li&gt;JavaScript로 개발을 할 때는 에디터 상태에서 나는 오류로 나오는 것은 사실상 런타임에 나오는 에러와 거의 일치하다.&lt;/li&gt;
&lt;li&gt;때문에 타입스크립트가 제공하는 benefit은 거의 다 컴파일타임에 제공하는 장점이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  TypeScript를 사용한 CRA(Create React App)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;CRA는 react app을 가장 빠르게 만드는 방법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; yarn create-react-app woowahan --template typescript&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;이렇게 하면 &lt;code&gt;tsconfig.json&lt;/code&gt; 파일과 tsx로 이루어진 cra가 생성된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tsconfig.json&lt;/code&gt; 파일은 typescript 컴파일러 옵션을 설정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import React from &amp;#39;react&amp;#39;; 
import ReactDOM from &amp;#39;react-dom&amp;#39;; 

// 인터페이스 
interface AppProps { 
  title: string; 
  color: string; 
} 

// 타입 정의 
// 상태 : 시간의 흐름에 따라서 변화하는 것 
// 현재 상태는 불변 
function App(props: AppProps){ 
  return ( 
    &amp;lt;h1&amp;gt;{props.title}&amp;lt;/h1&amp;gt; 
  ) 
}

// 버추얼 돔의 랜더를 이용 
// 두개의 인자 
// render 최상위에서만 실행 
// 단일 버추얼 돔 
ReactDOM.render( 
  // 첫번째 인자 리액트 컴포넌트를 DOM으로 변경한 다음에 두번째 인자 HTML 컨테이너에 붙쳐넣는다. 
  // StrictMode =&amp;gt; 경고해주는 안내해주는 개발툴에서 제공하는 툴 
  &amp;lt;React.StrictMode&amp;gt; 
      &amp;lt;App title=&amp;quot;Tech Hello?&amp;quot; color=&amp;quot;blue&amp;quot;/&amp;gt; 
  &amp;lt;/React.StrictMode&amp;gt;, 
  // 돔으로 변경후 root 컴포넌트에 붙친다. 
  document.getElementById(&amp;#39;root&amp;#39;) 
);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;  Babel에서의 JSX transpiler&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Babel 안에 있는 JSX transpiler가 오른쪽처럼 변환해준다.&lt;/li&gt;
&lt;li&gt;그렇기 때문에 안썻다고 생각해도 리액트 컴포넌트는 항상 리액트 패키지 import를 시켜야한다.&lt;/li&gt;
&lt;li&gt;그 이유는 리액트 패키지는 변환된 다음에 쓰기 때문에 변환하기 전에 반드시 포함해준다.&lt;/li&gt;
&lt;li&gt;JSX는 결국 CreateElement라고 하는 리액트가 제공한 함수를 좀 더 편하게 만들어놓은 (마치 HTML 태깅하듯이) 구조일 뿐이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1513&quot; data-origin-height=&quot;310&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmZl8x/btqHJqqxcK9/Q7QWUFBA41lU1tRitsvz7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmZl8x/btqHJqqxcK9/Q7QWUFBA41lU1tRitsvz7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmZl8x/btqHJqqxcK9/Q7QWUFBA41lU1tRitsvz7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmZl8x%2FbtqHJqqxcK9%2FQ7QWUFBA41lU1tRitsvz7K%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1513&quot; data-origin-height=&quot;310&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// 첫 번째 인자는 HTML 태그처럼 생긴 태크명, 두번째인자는 Property, 세번째 인자는 Child
function App() {
  return /*#__PURE__*/_react.default.createElement(&amp;quot;h1&amp;quot;, {
    id: &amp;quot;header&amp;quot;
  }, &amp;quot;hello &amp;quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;  CRA의 장점과 문제점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;장점: 간단하고, 왠만한 것들은 다 세팅되어있다.&lt;/li&gt;
&lt;li&gt;단점&lt;ul&gt;
&lt;li&gt;장점 그 외의 모든 것은 단점&lt;/li&gt;
&lt;li&gt;CRA가 제공하지 않는 세팅을 할 때 굉장히 까다롭다.&lt;/li&gt;
&lt;li&gt;CRA의 유연하지 못한 몇가지들을 이렇게 바꿨으면 좋겠다라고 말을해도 CRA 기여자분들은 보수적이라서 잘 받아주지 않는다.&lt;/li&gt;
&lt;li&gt;가장 크리티컬한 문제는 다양한 환경에 대한 대응이 어렵다.(local, product 두가지만 지원)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eject&lt;/code&gt;를 사용해 설정 파일을 빼서 커스터마이징이 가능하지만, 매우 복잡하고 하지말라고 권고를 하고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  상태관리&lt;/h2&gt;
&lt;h3&gt;  Flux&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;제일 먼저 나온 approach가 flux 아키텍처 &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  Redux&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;redux는 flux를 조금 더 정형화하고 더 쓰기 편하고 simple하게 제공되는 라이브러리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  Mobx&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Mobx는 redux의 대채품보다는 완전히 상태를 관리하는 페러다임을 바꿔버린 다른 방식이다.&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;h2&gt;  첫번째 시간 수강 후기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;일단 강의하시는 김민태님께서 굉장히 친절하게 알려주셨고 그에 따라 배울점이 많다고 느꼈습니다.&lt;/li&gt;
&lt;li&gt;중간에 쉬는 시간이 두번정도 있었지만 그 시간 마저도 질문에 답변을 해주는 QnA 시간을 가졌습니다.&lt;/li&gt;
&lt;li&gt;react를 공부를 하면서 뭔가 잘 못 공부하고 있다는 생각이 들었고 그에 따라서 자극을 많이 받았던 기회였습니다.&lt;/li&gt;
&lt;li&gt;교육생들 또한 정말 열정적이고 배우고자하는 의지?가 많아서 벌써 슬랙방도 파졌고 이번 기회에 네트워킹이 잘되서 스터디를 할 수 있는 기회가 생겼으면 좋겠습니다.&lt;/li&gt;
&lt;li&gt;앞으로 남은 교육기간도 기대가 되고 집중해서 들을려고 노력해야겠습니다..  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  주니어들한테 해주고 싶은 말(김민태 님께서..)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;주니어들이 시니어와 비교했을 때 압도적으로 유리한 부분은 &lt;strong&gt;시간&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;이 시간이 있다는 benefit을 충분히 활용해서 초조하게 여러가지 공부를 하는 거 보다는 시간이 충분히 있으니 여유를 가지고 했으면 좋겠다.&lt;/li&gt;
&lt;li&gt;이 시간으로 여러 정보를 습득할 수 있는데 이걸 방해하는 것이 초조한 생각이다.&lt;/li&gt;
&lt;li&gt;이런 생각이 시간이 지날수록 자기 자신을 많이 힘들고 지치게 한다.&lt;/li&gt;
&lt;li&gt;때문에 &lt;strong&gt;마라톤을 시작했다 생각하고 여유를 가져라!&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>우아한 테크러닝 3기</category>
      <category>react</category>
      <category>React &amp;amp; TypeScript</category>
      <category>typescript</category>
      <category>우아한 테크러닝 3기</category>
      <category>우아한 테크러닝 3기 후기</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/13</guid>
      <comments>https://haranglog.tistory.com/13#entry13comment</comments>
      <pubDate>Wed, 2 Sep 2020 23:10:28 +0900</pubDate>
    </item>
    <item>
      <title>[React]리액트에서 react-draft-wysiwyg , DraftJS 사용하여 에디터(Editor) 적용 후 Redux로 상태관리하기</title>
      <link>https://haranglog.tistory.com/12</link>
      <description>&lt;h2&gt;✔ 개요&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;블로그 글쓰기나 기타 게시글을 쓸 때 textarea로 작성을 하면 많이 밋밋하고, 기능적으로 많은 구현을 하기 힘듭니다. 그렇기 때문에 글쓰기가 필요하고 게시글을 만들 때 오프소스 에디터들을 사용하여 좀 더 많은 기능의 게시글을 작성할 수가 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;많은 오프소스 에디터들 중에 &lt;a href=&quot;https://jpuri.github.io/react-draft-wysiwyg&quot;&gt;&lt;b&gt;react-draft-wysiwyg&lt;/b&gt;&lt;/a&gt;(리액트드래프트위지윅) 에디터를 사용해봤습니다.&lt;/p&gt;
&lt;p&gt;다른 오픈소스 기반 리액트 에디터의 종류를 사용해보고 싶으면 &lt;a href=&quot;https://ourcodeworld.com/articles/read/1065/top-15-best-rich-text-editor-components-wysiwyg-for-reactjs&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;사용하면서 react-draft-wysiwyg의 장점은 기본적인 사용은 쉽고, 유명한 페이스북에서 관리하는 에디터인 &lt;a href=&quot;https://draftjs.org/&quot;&gt;draft.js&lt;/a&gt;를 사용하기 쉽게 리액트로 구성한 것입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만, 기능적으로 디테일이 부족하다는 점이 있다고 느꼈습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;예를 들어, 한글을 입력할 때 바로바로 onChange가 되지않아 글자를 작성하고 바로 Shift + Enter키(&lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; 태그 없이 띄어쓰기)를 할 때 상태값이 변하지않아 꼬이는 현상이 발생합니다. 그에 따라, 기존에 작성중이던 글자들이 삭제되는 현상이 발생합니다.&lt;br /&gt;이 문제는 저의 코딩방식에 문제인 줄 알았지만 react-draft-wysiwyg의 공식 사이트의 데모에서도 같은 현상이 발생합니다.&lt;br /&gt;또한, 이미지 등록 시 발생하는 &lt;code&gt;Unknown DraftEntity key: null.&lt;/code&gt; 에러가 발생하는데, 이 문제도 이미지를 등록 후 한글로 작성 시에 생기는 오류인거 같습니다.&lt;br /&gt;최근에도 &lt;a href=&quot;https://github.com/jpuri/react-draft-wysiwyg/issues?q=Unknown+DraftEntity+key%3A+null.&quot;&gt;Issues&lt;/a&gt;에 올라오는 것으로 보아 아직까지 해결이 안된거 같습니다.&lt;br /&gt;개인 프로젝트를 진행하면서 &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/issues/108&quot;&gt;버그사항을 정리&lt;/a&gt;해본 것입니다. 참고해주세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  react-draft-wysiwyg 설치 및 기본 설정&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;draft.js&lt;/code&gt;를 기반으로 작성된 것이기 때문에 &lt;code&gt;draft-js&lt;/code&gt;도 함께 설치해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;// npm
$ npm i react-draft-wysiwyg draft-js
// yarn
$ yarn add react-draft-wysiwyg draft-js&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;styled-components&lt;/code&gt;를 사용하여 css스타일을 잡아주었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;import React, { useState } from 'react';
import { Editor } from 'react-draft-wysiwyg';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import styled from 'styled-components';
import { EditorState } from 'draft-js';

const MyBlock = styled.div`
    .wrapper-class{
        width: 50%;
        margin: 0 auto;
        margin-bottom: 4rem;
    }
  .editor {
    height: 500px !important;
    border: 1px solid #f1f1f1 !important;
    padding: 5px !important;
    border-radius: 2px !important;
  }
`;

const TestEditorForm = () =&amp;gt; {
  // useState로 상태관리하기 초기값은 EditorState.createEmpty()
  // EditorState의 비어있는 ContentState 기본 구성으로 새 개체를 반환 =&amp;gt; 이렇게 안하면 상태 값을 나중에 변경할 수 없음.
  const [editorState, setEditorState] = useState(EditorState.createEmpty());

  const onEditorStateChange = (editorState) =&amp;gt; {
    // editorState에 값 설정
    setEditorState(editorState);
  };

  return (
    &amp;lt;MyBlock&amp;gt;
      &amp;lt;Editor
        // 에디터와 툴바 모두에 적용되는 클래스
        wrapperClassName=&quot;wrapper-class&quot;
        // 에디터 주변에 적용된 클래스
        editorClassName=&quot;editor&quot;
        // 툴바 주위에 적용된 클래스
        toolbarClassName=&quot;toolbar-class&quot;
        // 툴바 설정
        toolbar={{
          // inDropdown: 해당 항목과 관련된 항목을 드롭다운으로 나타낼것인지
          list: { inDropdown: true },
          textAlign: { inDropdown: true },
          link: { inDropdown: true },
          history: { inDropdown: false },
        }} 
        placeholder=&quot;내용을 작성해주세요.&quot;
        // 한국어 설정
        localization={{
          locale: 'ko',
        }}
        // 초기값 설정
        editorState={editorState}
        // 에디터의 값이 변경될 때마다 onEditorStateChange 호출
        onEditorStateChange={onEditorStateChange}
      /&amp;gt;
    &amp;lt;/MyBlock&amp;gt;
  );
};

export default TestEditorForm;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;이렇게 하면 리액트 Hooks를 사용하여 간단하게 상태관리를 하여 동작시킬 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;작성을 해보면 잘 작동이되는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;  주의할 점은 그냥 엔터를 치게되면 엔터칠때마다 &lt;code&gt;&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt;태그가 붙게되어 &lt;code&gt;pre&lt;/code&gt;블록과 &lt;code&gt;code&lt;/code&gt;블록은 원하지 않는 형태로 나타납니다. 그렇기 때문에 &lt;code&gt;Shift + Enter&lt;/code&gt;키를 사용하여 &lt;code&gt;&amp;lt;br/&amp;gt;&lt;/code&gt; 태그만 붙게 해주어 사용해야됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;더 자세한 기능은 &lt;a href=&quot;https://jpuri.github.io/react-draft-wysiwyg/#/docs&quot;&gt;공식문서&lt;/a&gt;를 참고해주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp88d1/btqHAl96duB/wQKcRHT61PpRHbiitaMtF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp88d1/btqHAl96duB/wQKcRHT61PpRHbiitaMtF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp88d1/btqHAl96duB/wQKcRHT61PpRHbiitaMtF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp88d1%2FbtqHAl96duB%2FwQKcRHT61PpRHbiitaMtF1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;  에디터로 작성한 상태값을 HTML태그로 변환하여 게시글 띄우기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;현재 상태에서 &lt;code&gt;console.log()&lt;/code&gt;을 찍어보면 아래 이미지와 같은 값으로 나오게 되는데 이 &lt;code&gt;editorState&lt;/code&gt; 값을 HTML태그로 변경해줄려면 &lt;a href=&quot;https://www.npmjs.com/package/draftjs-to-html&quot;&gt;&lt;code&gt;draftjs-to-html&lt;/code&gt;&lt;/a&gt;의 도움이 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;70%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chYIq2/btqHGVC3Jxk/PCfb5sBFryxHjg8pmTkhRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chYIq2/btqHGVC3Jxk/PCfb5sBFryxHjg8pmTkhRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chYIq2/btqHGVC3Jxk/PCfb5sBFryxHjg8pmTkhRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchYIq2%2FbtqHGVC3Jxk%2FPCfb5sBFryxHjg8pmTkhRk%2Fimg.png&quot; width=&quot;70%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;// draftjs-to-html 라이브러리 설치
$ npm i draftjs-to-html&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이젠 &lt;code&gt;div&lt;/code&gt;태그 안에 &lt;code&gt;editorState&lt;/code&gt; 상태 값을 HTML태그로 변환하여 넣을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;// 생략..
// convertToRaw: editorState 객체가 주어지면 원시 JS 구조로 변환.
import { EditorState, convertToRaw } from 'draft-js';
// convertToRaw로 변환시켜준 원시 JS 구조를 HTML로 변환.
import draftToHtml from 'draftjs-to-html';

// 생략..
// 변환시켜준 editorState 값을 넣기 위한 div 태그 css 
const IntroduceContent = styled.div`
  position: relative;
  border: 0.0625rem solid #d7e2eb;
  border-radius: 0.75rem;
  overflow: hidden;
  padding: 1.5rem;
  width: 50%;
  margin: 0 auto;
  margin-bottom: 4rem;
`;

const TestEditorForm = () =&amp;gt; {
  // 생략..
  // editorState의 현재 contentState 값을 원시 JS 구조로 변환시킨뒤, HTML 태그로 변환시켜준다.
  const editorToHtml = draftToHtml(convertToRaw(editorState.getCurrentContent()));

  return (
    &amp;lt;&amp;gt;
    // 생략..
    // dangerouslySetInnerHTML: https://ko.reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
    &amp;lt;IntroduceContent dangerouslySetInnerHTML={{__html: editorToHtml}}/&amp;gt;
    &amp;lt;/&amp;gt;
  );
};

export default TestEditorForm;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이제 에디터에 작성한 상태값이 변경이 될때마다 잘 바뀌는 것을 볼 수 있습니다.&lt;/li&gt;
&lt;li&gt;하지만, 해보시면 아시겠지만 한글로 작성 할 시점에 바로바로 변경이 되는것이 아니라 띄어쓰기나 &lt;code&gt;.&lt;/code&gt;을 찍을때 마다 변경하는 것을 볼 수 있습니다.&lt;/li&gt;
&lt;li&gt;CSS는 하고싶은 대로 변경을 해주면 됩니다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1zSwO/btqHK38xj0i/9IP4UjkptjKTHixyUYFDa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1zSwO/btqHK38xj0i/9IP4UjkptjKTHixyUYFDa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1zSwO/btqHK38xj0i/9IP4UjkptjKTHixyUYFDa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1zSwO%2FbtqHK38xj0i%2F9IP4UjkptjKTHixyUYFDa1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  HTML태그를 draft.js로 변환하여 Editor에 초기값 세팅하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이 경우는 게시글을 &lt;b&gt;수정&lt;/b&gt;할 때 필요로 합니다.&lt;/li&gt;
&lt;li&gt;게시글을 수정할 때는 기존에 작성된 값을 넣어주어 수정을 할 수 있게 해주어야 합니다. 그렇기 때문에 받은 HTML의 값을 draft Editor가 알아볼 수 있도록 변환해준 뒤 넣어 주어야 합니다.&lt;/li&gt;
&lt;li&gt;변환하기 위해서는 &lt;a href=&quot;https://www.npmjs.com/package/html-to-draftjs&quot;&gt;&lt;code&gt;html-to-draftjs&lt;/code&gt;&lt;/a&gt;를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;$ npm i html-to-draftjs&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이제 게시글을 수정하는 상황으로 생각하고 임의의 값을 선언해주어 변환해보겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useState, useEffect } from 'react';
// 생략..
// ContentState: https://draftjs.org/docs/api-reference-content-state/
import { EditorState, convertToRaw, ContentState } from 'draft-js';
// 생략..
import htmlToDraft from 'html-to-draftjs';

// 생략..

const TestEditorForm = () =&amp;gt; {
  // 생략..
  const htmlToEditor = `&amp;lt;pre&amp;gt;const editorToHtml = 
        draftToHtml(convertToRaw(editorState.getCurrentContent()));&amp;lt;/pre&amp;gt;
        &amp;lt;p style=&quot;text-align:center;&quot;&amp;gt;&amp;lt;strong&amp;gt;ㅎㅇㅎㅇㅎㅇㅎㅇㅎㅇㅎㅇㅎㅇㅎㅇ
        &amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;`;

  useEffect(() =&amp;gt; {
    const blocksFromHtml = htmlToDraft(htmlToEditor);
    if (blocksFromHtml) {
      const { contentBlocks, entityMap } = blocksFromHtml;
      // https://draftjs.org/docs/api-reference-content-state/#createfromblockarray
      const contentState = ContentState.createFromBlockArray(contentBlocks, entityMap);
      // ContentState를 EditorState기반으로 새 개체를 반환.
      // https://draftjs.org/docs/api-reference-editor-state/#createwithcontent
      const editorState = EditorState.createWithContent(contentState);
      setEditorState(editorState);
    }
  // 처음 마운트됬을 때만 실행되야 된다.
  // eslint-disable-next-line
  },[]);
  return (
    // 생략..
  );
};

export default TestEditorForm;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;보통 수정할 때에는 처음에 랜더링될때의 값만 넣어주어야하기 때문에 빈배열을 넣어주어 처음 랜더링될때만 실행합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이렇게 하면 처음에 초기값이 적용된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;여기서 &lt;code&gt;// eslint-disable-next-line&lt;/code&gt;를 해준 이유는 경고 메시지가 뜨기 때문입니다.&lt;/p&gt;
&lt;p&gt;이 방법 말고 아래 방법처럼 &lt;code&gt;useRef&lt;/code&gt;를 사용하여 처음 랜더링될 때(&lt;code&gt;true&lt;/code&gt;일 때)만 실행되고 그 다음부터는 &lt;code&gt;false&lt;/code&gt;로 변경해주어 처음 랜더링될 때만 실행되게 할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useState, useEffect, useRef } from 'react';
// 생략..
const TestEditorForm = () =&amp;gt; {
  // 생략..
  const rendered = useRef(false);

  useEffect(() =&amp;gt; {
    if (rendered.current) return;
    rendered.current = true;
    const blocksFromHtml = htmlToDraft(htmlToEditor);
    if (blocksFromHtml) {
      const { contentBlocks, entityMap } = blocksFromHtml;
      const contentState = ContentState.createFromBlockArray(contentBlocks, entityMap);
      const editorState = EditorState.createWithContent(contentState);
      setEditorState(editorState);
    }
  },[htmlToEditor]);
  return (
    // 생략..
  );
};

export default TestEditorForm;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;  Redux를 사용하여 상태 관리하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;여기서는 자세하게 설명하지 않겠습니다. 이유는 너무 많은 내용을 담을거 같아서 내용이 길어지기 때문입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;간략하게 설명하겠습니다. 이 editor를 사용하여 사이드 프로젝트인 스터디 모집 사이트에서 사용했습니다. 자세한 내용은 &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin&quot;&gt;Github&lt;/a&gt;에 올려두었습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;아래는 리덕스까지 적용한 간단한 예제 코드 입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;간단하게 보여주기 위해서 &lt;code&gt;component&lt;/code&gt; 와 &lt;code&gt;container&lt;/code&gt;를 분리하지 않았습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;사용하실때에는 분리하여 사용하시기 바랍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useState, useEffect, useRef, useCallback } from 'react';
import { Editor } from 'react-draft-wysiwyg';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import styled from 'styled-components';
import { EditorState, convertToRaw, ContentState } from 'draft-js';
import draftToHtml from 'draftjs-to-html';
import htmlToDraft from 'html-to-draftjs';
import { useDispatch, useSelector } from 'react-redux';
import { changeField } from 'src/modules/write';

const MyBlock = styled.div`
  .wrapper-class {
    width: 50%;
    margin: 0 auto;
    margin-bottom: 4rem;
  }
  .editor {
    height: 500px !important;
    border: 1px solid #f1f1f1 !important;
    padding: 5px !important;
    border-radius: 2px !important;
  }
`;

const IntroduceContent = styled.div`
  position: relative;
  border: 0.0625rem solid #d7e2eb;
  border-radius: 0.75rem;
  overflow: hidden;
  padding: 1.5rem;
  width: 50%;
  margin: 0 auto;
  margin-bottom: 4rem;
`;

const TestEditorForm = () =&amp;gt; {
  const dispatch = useDispatch();
  const { content } = useSelector(({ write }) =&amp;gt; ({
    content: write.content,
  }));
  const [editorState, setEditorState] = useState(EditorState.createEmpty());
  const rendered = useRef(false);

  // 리덕스 스토어 연결
  const onChangeField = useCallback((payload) =&amp;gt; dispatch(changeField(payload)), [dispatch]);

  // HTML 변환 공통 함수
  const editorToHtml = editorState =&amp;gt; {
    return draftToHtml(convertToRaw(editorState.getCurrentContent()))
  }

  const onEditorStateChange = (editorState) =&amp;gt; {
    setEditorState(editorState);
    // 리덕스 changeField
    onChangeField({
      key: 'content',
      value: editorToHtml(editorState),
    });
  };

  useEffect(() =&amp;gt; {
    if (rendered.current) return;
    rendered.current = true;
    const blocksFromHtml = htmlToDraft(content);
    if (blocksFromHtml) {
      const { contentBlocks, entityMap } = blocksFromHtml;
      const contentState = ContentState.createFromBlockArray(contentBlocks, entityMap);
      const editorState = EditorState.createWithContent(contentState);
      setEditorState(editorState);
    }
  },[content]);

  return (
    &amp;lt;&amp;gt;
      &amp;lt;MyBlock&amp;gt;
        &amp;lt;Editor
          wrapperClassName=&quot;wrapper-class&quot;
          editorClassName=&quot;editor&quot;
          toolbarClassName=&quot;toolbar-class&quot;
          toolbar={{
            list: { inDropdown: true },
            textAlign: { inDropdown: true },
            link: { inDropdown: true },
            history: { inDropdown: false },
          }}
          placeholder=&quot;내용을 작성해주세요.&quot;
          localization={{
            locale: 'ko',
          }}
          editorState={editorState}
          onEditorStateChange={onEditorStateChange}
        /&amp;gt;
      &amp;lt;/MyBlock&amp;gt;
      &amp;lt;IntroduceContent dangerouslySetInnerHTML={{ __html: editorToHtml(editorState) }} /&amp;gt;
    &amp;lt;/&amp;gt;
  );
};

export default TestEditorForm;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;에디터에 작성한 내용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sHJmj/btqHCWQhzDp/k3djKkrSAFXqbEqA6t8fmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sHJmj/btqHCWQhzDp/k3djKkrSAFXqbEqA6t8fmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sHJmj/btqHCWQhzDp/k3djKkrSAFXqbEqA6t8fmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsHJmj%2FbtqHCWQhzDp%2Fk3djKkrSAFXqbEqA6t8fmK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;redux dev tool&lt;/code&gt;로 확인한 리덕스 스토어 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cffCCV/btqHJqv5eBs/D3rLpswcT22t2bEtkv0zik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cffCCV/btqHJqv5eBs/D3rLpswcT22t2bEtkv0zik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cffCCV/btqHJqv5eBs/D3rLpswcT22t2bEtkv0zik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcffCCV%2FbtqHJqv5eBs%2FD3rLpswcT22t2bEtkv0zik%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;  마무리하며..&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;이제 리덕스를 사용하여 &lt;code&gt;react-draft-wysiwyg&lt;/code&gt;과 &lt;code&gt;draft-js&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;더 자세히 보고싶은 분들은 &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin&quot;&gt;Github&lt;/a&gt;의 &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/blob/main/src/containers/write/WriteEditorContainer.jsx&quot;&gt;container부분&lt;/a&gt;, &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/blob/main/src/components/write/WriteEditor.jsx&quot;&gt;components 부분&lt;/a&gt;, &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/blob/main/src/reducers/groupSlice.js#L57&quot;&gt;redux 부분&lt;/a&gt; 확인해주시기 바랍니다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;다음은 &lt;code&gt;react-draft-wysiwyg&lt;/code&gt;에서 &lt;b&gt;이미지를 업로드하는 방법&lt;/b&gt;을 포스팅하겠습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;쓰다보니 길어졌는데 수정사항이나 개선사항이 있으면 댓글로 피드백 부탁드립니다.   감사합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>React</category>
      <category>draft-js</category>
      <category>editor</category>
      <category>react</category>
      <category>react-draft-wysiwyg</category>
      <category>react에디터</category>
      <category>리덕스</category>
      <category>리액트</category>
      <category>리액트드래프트위지윅</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/12</guid>
      <comments>https://haranglog.tistory.com/12#entry12comment</comments>
      <pubDate>Mon, 31 Aug 2020 23:32:28 +0900</pubDate>
    </item>
    <item>
      <title>2020 NAVER DEVELOPER OPEN CLASS 후기(정리)</title>
      <link>https://haranglog.tistory.com/11</link>
      <description>&lt;h2&gt;✔ NAVER DEVELOPER OPEN CLASS 후기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://campaign.naver.com/openclass2020/&quot;&gt;NAVER DEVELOPER OPEN CLASS&lt;/a&gt;의 목적은 네이버 신입 개발자 채용 설명회이다.&lt;/li&gt;
&lt;li&gt;작년에는 오프라인으로 진했다고 하는데 올해는 코로나 때문인지 온라인으로 매주마다 업데이트되면서 진행됐다.&lt;/li&gt;
&lt;li&gt;매주 금요일마다 업데이트 내용을 기다리는 재미?가 있었고, 생각보다 알차서 좋았던거 같고, 6주동안 모두 출석체크하면 소정의 상품인 네이버 립스탑 백 (RIPSTOP BAG)을 주는 이벤트도 진행하고 있었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  1. TUTORIAL&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;TUTORIAL은 시작인 만큼 가볍게 영상으로 시작됐다.&lt;/li&gt;
&lt;li&gt;네이버 웹툰작가 기안 84님이 네이버 사옥을 돌아다니며 소개하는 영상이였는데 네이버 참 좋아보이더라..&lt;/li&gt;
&lt;li&gt;가고싶다는 생각이 팍 들정도로 너무 좋아보였다.&lt;/li&gt;
&lt;li&gt;그리고 각 네이버 계열사들의(NAVER LABS, NBP, PaPago..등) CEO및 책임리더들이 간단히 소개를 해주고 지원할 때 작성한 질문들을 바탕으로 &lt;strong&gt;응답하라 네이버&lt;/strong&gt; 라는 이름과 함께 답변을 주는 형식이였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;❓ 질문과 내용을 요약&lt;/h3&gt;
&lt;h4&gt;  개발자의 지원서, 어떻게 작성하는 것이 좋을까요?&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;◾ 화려한 문체나 거창한 커리어보다는, &lt;strong&gt;개발에 대한 나만의 열정과 노력&lt;/strong&gt;들을 보여주시는 것이 중요합니다.&lt;br&gt;◾ 내가 무엇을 할 수 있는지가 명확히 드러나는 지원서를 선호합니다.&lt;br&gt;◾ 지원서에 개인 &lt;code&gt;GITHUB URL&lt;/code&gt;을 작성한 지원자의 경우, 꼭 들어가서 봅니다.&lt;br&gt;◾ &lt;strong&gt;GITHUB에 소스를 지속적으로 업로드하고 관리하고 공유하는 것&lt;/strong&gt;이 가장 중요합니다.&lt;br&gt;◾ 지원서에는 ‘잘했다, 못했다, 성공했다, 1등 받았다’ 와 같은 ‘결과’보다는 ‘무엇을 했는지’ 강조해 주세요.&lt;br&gt;◾ 협업에 대해서 이야기하고 싶다면 갈등, 조정, 협의, 합의 등에 이르게 된 과정을 설명해 주세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;  개발자로서 꼭 읽어야 할 책이 있다면 추천 부탁드립니다.&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;◾ 본인이 주로 쓰는 언어의 &lt;strong&gt;Effective 시리즈&lt;/strong&gt;를 추천합니다.&lt;br&gt;◾ 코드를 클린하게 작성하는 것에 대해서 다루고 있는 &lt;strong&gt;클린 코드&lt;/strong&gt;를 추천합니다.&lt;br&gt;◾ &lt;strong&gt;대학교 교과서&lt;/strong&gt;를 다시 읽어보는 것을 추천합니다. 저는 특히 &lt;strong&gt;자료구조 교과서&lt;/strong&gt;가 가장 큰 도움이 되었는데,&lt;br&gt;운영체제 등 기초 지식을 다지고, 코딩 테스트를 연습하는 데 도움이 되었습니다.&lt;br&gt;◾ &lt;strong&gt;Pro Git.&lt;/strong&gt; Git 관련 서적 한 권 추천합니다.&lt;br&gt;◾ 대학교에서 &lt;strong&gt;전산 기초 과목 학습할 때 읽은 책&lt;/strong&gt;들을 잘 보관하시고 다시 읽어 보는 것을 추천합니다.&lt;br&gt;&lt;strong&gt;운영체제, 데이터베이스, 네트워크, 자료구조, 프로그래밍 언어, 알고리즘 등 기초 과목&lt;/strong&gt;&lt;br&gt;◾ 책은 아니지만, 머신러닝/딥러닝에 관심 있는 분들에게 도움이 될 만한 &lt;a href=&quot;https://ko.coursera.org/courses?query=machine%20learning%20andrew%20ng&quot;&gt;&lt;strong&gt;온라인 강좌&lt;/strong&gt;&lt;/a&gt; 몇 가지 추천합니다. 여러 강좌 리스트 중 첫 번째, 두 번째 강좌를 신청해 들어보세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;  네이버가 계속 강조하는 기본기, 그 기본기가 도대체 무엇인가요?&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;◾ 우리가 하는 일에 대해 &lt;strong&gt;관심을 가지고, 업무를 하기 위해 필요한 것이 무엇인지 이해하며 꾸준히 노력&lt;/strong&gt;하는 것이요.&lt;br&gt;◾ &lt;strong&gt;학부 때 공부했던 OS, 네트워크, 알고리즘, 자료 구조 등을 착실하게 내 것으로 만들어야 더 좋은 코드를 만들 수 있고, 단순히 암기가 아니라 원리를 이해해야 실무에서 응용을 할 수 있다고 생각&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  2. 직원피셜 레알 네이버&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;2주차 CLASS도 같은 형식으로 진행되었고, 영상은 네이버에서 근무하는 직원분들을 대상으로 한 인터뷰가 진행되었다.&lt;/li&gt;
&lt;li&gt;그 내용으론 네이버의 복지와 관련된 내용이였다. 너무 좋아 보였다..&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;❓ 질문과 내용을 요약&lt;/h3&gt;
&lt;h4&gt;  비전공자도 네이버에서 개발 업무를 할 수 있나요? 비전공자로서 개발 업무를 수행하는 데에 강점과 약점은 무엇인가요?&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;◾ 할 수 있습니다. 전공은 중요하지 않다고 생각해요. 본인이 개발자가 되기 위해 &lt;strong&gt;어떤 노력들을 했는지, 왜 그런 노력을 했는지가 중요합니다.&lt;/strong&gt;&lt;br&gt;◾ 비전공자라면 주전공의 강점을 잘 살려보시는 것을 추천드립니다.&lt;br&gt;◾ 누가 전공자/비전공자인지 전혀 신경 쓰지 않고, 굳이 알려고 하지도 않는 분위기입니다. &lt;strong&gt;개발 실력이 우선입니다.&lt;/strong&gt;&lt;br&gt;◾ 채용 과정에서 비전공자이기 때문에 생기는 불이익은 없다고 생각합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;  개발을 할 때, 윈도우와 맥북 중 어떤 것을 주로 쓰시나요? 이유는 무엇인가요?&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;◾ 입사 전에는 윈도우만 사용을 했었는데 입사 후에는 &lt;strong&gt;맥북&lt;/strong&gt;을 사용하고 있어요. 무엇보다 터미널 환경이 쾌적해요.&lt;br&gt;◾ 윈도우와 맥북 둘 다 사용했었으나 현재는 윈도우를 쓰고있습니다. 맥OS가 개발 환경은 편했지만, 윈도우에서 당연시되던 화면분할이나 마우스 감도 조절 등 여러 편의 기능들을 전부 유료로 구매해야 하는 것이 불편했기 때문입니다.&lt;br&gt;◾ 네이버에서는 업무 기기를 직접 고를 수 있기 때문에 본인이 편하고 가장 잘 쓸 수 있는 장비로 쓰면 됩니다.&lt;br&gt;◾ 저는 백엔드 개발자인데요. 맥북을 사용합니다. 윈도우에서 활용하기 어려운 터미널을 활용할 수 있다는 장점도 있고, 요즘은 오피스도 맥 지원을 잘 해서 맥에서 문서 작업할 때도 큰 불편이 없어요.&lt;br&gt;◾ 맥북이 더 빌드 속도가 빠른 것 같아요. 빌드 속도가 개발 기간을 많이 줄여주죠.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;  네이버 제 2사옥은 언제쯤 완공이 될 예정인가요?&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;◾ 사람과 로봇이 공존하는 세계 최초의 로봇 친화형 빌딩, 네이버 제2사옥은 2021년 완공을 앞두고 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  3. 필살기보다 기본기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이번 영상은 2019/2020 신입 입사자분들에게 인터뷰를 한 영상이였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;❓ 질문과 내용을 요약&lt;/h3&gt;
&lt;h4&gt;  한가지 언어와 툴을 열심히 하는 것과 여러가지 언어와 툴을 시도해보는 것 중 어느 것이 더 중요하다고 보시나요?&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;◾ 한 가지 언어를 배웠든 여러 가지의 언어를 배웠든, &lt;strong&gt;어떻게 학습을 시작하는지, 공부의 깊이를 더하기 위해서 어떻게 경험을 쌓았는지가 더 중요하다고 생각합니다.&lt;/strong&gt;&lt;br&gt;◾ 한 가지, 여러 가지가 중요한 것이 아닙니다. &lt;strong&gt;튼실한 전산 기초를 가져 새로운 언어 학습의 러닝커브를 줄일 수 있느냐&lt;/strong&gt;가 중요합니다.&lt;br&gt;◾ &lt;strong&gt;여러 언어를 경험해보되 가장 관심 있는 한두 가지 언어에 대해서 전문성&lt;/strong&gt;을 가지는 방향을 제안하고 싶습니다.&lt;br&gt;◾ 오직 한 가지 언어만을 고수하는 것보다는, 여러 가지를 접해 보되 &lt;strong&gt;하나 정도는 깊이감 있게 공부하는 것&lt;/strong&gt;이 좋습니다.&lt;br&gt;◾ 특정 sw 언어로 개발하려는 것이 무엇인지 &lt;strong&gt;목적을 잃지 않는 것&lt;/strong&gt;, 또는 특정 목적을 이루려고 하는데 &lt;strong&gt;어떤 sw 언어가 효과적인지&lt;/strong&gt;를 판단하는 게 중요합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;  많은 기술 분야 중 적성에 맞는 분야를 어떻게 찾으셨나요?&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;◾ 무엇을 선택함에 있어 정답은 아니더라도 정답에 가까운 답안을 찾기 위해서는 &lt;strong&gt;후보군을 최대한 많이 준비하는 게 중요&lt;/strong&gt;하다고 생각합니다.&lt;br&gt;◾ &lt;strong&gt;업무&lt;/strong&gt;를 통해서, &lt;strong&gt;개인적인 토이 프로젝트&lt;/strong&gt;를 통해서, &lt;strong&gt;스터디/세미나&lt;/strong&gt;를 통해서 등등 다양한 분야를 직간접적으로 최대한 많이 경험해보시면 나에게 적성이 맞는 영역을 찾는 데 도움이 되지 않을까 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  4. Language? Communication!&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이번 영상은 협업에 대한 이야기였다. 커뮤니케이션이 얼마나 중요하지에 대해서 얘기를 하고 커뮤니케이션의 전반적인 내용이였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;❓ 질문과 내용을 요약&lt;/h3&gt;
&lt;h4&gt;  함께 일하고 싶은 동료는 어떤 사람인가요? 반대로, 함께 일하기 꺼려지는 동료는 어떤 사람인가요?&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;◾ &lt;strong&gt;진취적인 사람&lt;/strong&gt;하고 일하고 싶어요. 항상 열린 마인드를 가지고 있어야 더 좋은 개발 문화를 만들어 나갈 수 있다고 생각합니다.&lt;br&gt;◾ 업무를 하다 보면 가끔 &lt;strong&gt;쉬운 길이 생각이 안 나서 의도치 않게 돌아가는 경우&lt;/strong&gt;가 있는데 곁에서 그런 부분을 잘 캐치해 주는 동료가 있으면 굉장히 든든합니다.&lt;br&gt;◾ 반대로 &lt;strong&gt;아집이 강하고 나쁜 의미로 자기중심적인 분&lt;/strong&gt;들은 다른 사람이 반대 의견을 내기 어렵게 만들고, 더 좋은 해결 방법을 모색하는 데 안 좋은 영향을 끼치는 것 같아요.&lt;br&gt;◾ 본인이 &lt;strong&gt;알고 있는 지식을 잘 공유&lt;/strong&gt;하시는 분, 그리고 항상 겸손한 자세로 배워 나가시는 분들과 함께 일하고 싶습니다.&lt;br&gt;◾ &lt;strong&gt;긍정적인 생각&lt;/strong&gt;을 가지고, &lt;strong&gt;틀 안&lt;/strong&gt;에 갇혀 있지 않고 &lt;strong&gt;도전&lt;/strong&gt;을 두려워하지 않으며, 자기가 하는 일에 &lt;strong&gt;책임&lt;/strong&gt;을 지고 해내고, &lt;strong&gt;동료를 배려&lt;/strong&gt;하고 &lt;strong&gt;전체 멤버를 위해 노력해 줄 수 있는 분&lt;/strong&gt;과 함께 일하고 싶습니다.&lt;br&gt;◾ &lt;strong&gt;책임감이 없고 지나치게 개인적&lt;/strong&gt;이며, 문제에 대해 핑계와 변명을 늘어놓는 분과는 함께 일하기 힘듭니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;  타 부서와 협업할 때 의견이 달라 갈등하신 적이 있으신가요? 그 상황을 어떻게 해결하셨나요?&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;◾ &lt;strong&gt;타협할 때에는 갈등에 대해 다양하게 접근해보면서 서로의 차이를 수용하고 좁혀 나가야&lt;/strong&gt; 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  5. 클래스가 다른 성장&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이번 영상은 네이버에 들어오게되서 어떻게 성장하고 경험해 나가는지 대해서 다루웠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;❓ 질문과 내용을 요약&lt;/h3&gt;
&lt;h4&gt;  좋은 개발자가 되기 위해서는 회사 업무 외에도 개인 공부를 통해 빠르게 발전하는 기술들을 계속해서 터득해야 한다고 들었는데요, 이를 위해 어떤 방식으로 공부를 하고 계신지 궁금합니다.&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.apple.com/wwdc20/&quot;&gt;&lt;strong&gt;WWDC&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;와 같은 개발자 행사&lt;/strong&gt;에 관심이 많아요.&lt;br&gt;&lt;a href=&quot;http://daily-devblog.com/&quot;&gt;&lt;strong&gt;Daily DevBlog&lt;/strong&gt;&lt;/a&gt;와 같은 구독 서비스를 통해 여러 개발 블로그 글들을 참고하기도 하고, 사내 기술 공유 플랫폼에 올라오는 글을 보고 동향을 파악하기도 합니다.&lt;br&gt;&lt;a href=&quot;https://www.udemy.com/ko/&quot;&gt;&lt;strong&gt;Udemy 인강&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;을 듣고&lt;/strong&gt; 적용해 가는 방식으로 공부하고 있고, 대학 동기들과 &lt;strong&gt;스터디&lt;/strong&gt;를 하면서 각자 경험한 내용을 공유하고 있습니다.&lt;br&gt;코드 리뷰를 받고 나면 수많은 수정사항들이 나오는데, 수정사항들을 하나하나 꼼꼼히 메모해 놓습니다. 그리고 &lt;strong&gt;저는 책들을 통해 많은 것들을 배웁니다.&lt;/strong&gt;&lt;br&gt;저는 흥미가 가는 분야에 대해 &lt;a href=&quot;https://ko.coursera.org/&quot;&gt;&lt;strong&gt;coursera 강의&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;를 듣고&lt;/strong&gt; 있는데요, 관심 있는 주제에 대해 공부하고, 공부한 것을 적용해 &lt;strong&gt;직접 코드를 작성해보는 것&lt;/strong&gt;이 가장 좋은 것 같아요.&lt;br&gt;정보 공유 플랫폼인 &lt;a href=&quot;https://medium.com/&quot;&gt;&lt;strong&gt;Medium&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;을 구독하며 매일 메일&lt;/strong&gt;로 받아보고 있습니다.&lt;br&gt;시간 날 때마다 &lt;strong&gt;토이 프로젝트로 뭔가를 만들어 보면 좋습니다.&lt;/strong&gt;&lt;br&gt;&lt;a href=&quot;https://egghead.io/&quot;&gt;&lt;strong&gt;Egghead&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://www.pluralsight.com/&quot;&gt;&lt;strong&gt;pluralsight&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;등에 올라온 온라인 강의&lt;/strong&gt;들도 심심할 때마다 살피면서 따라 해 봅니다. 오픈소스 활동도 좋아요.&lt;br&gt;기술 동향 파악을 위해서는, &lt;strong&gt;기술 블로그를 구독&lt;/strong&gt;하고, &lt;strong&gt;개발 커뮤니티&lt;/strong&gt; 등을 통해 정보를 수집하고, &lt;strong&gt;온라인 교육 강좌&lt;/strong&gt;와 &lt;strong&gt;슬라이드 쉐어&lt;/strong&gt; 등을 통해 학습하기도 하고, 동료들과 &lt;strong&gt;스터디&lt;/strong&gt;하기도 합니다. 코딩 역량 향상을 위해서는, &lt;strong&gt;관심 있는 오픈 소스를 분석&lt;/strong&gt;하며 좋은 코드를 많이 봅니다. PR을 날리기도 하지요. 그리고 &lt;strong&gt;토이 프로젝트&lt;/strong&gt; 등을 통해 코드를 많이 짜 봅니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;  6. 전형의 모든 것&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;원래 6번째 CLASS는 라이브 톡으로 한다고 했지만 역시 코로나의 여파로 이 마저도 녹화한 영상으로 진행되었다.&lt;/li&gt;
&lt;li&gt;이번 영상에서는 공채 전형 담당자가 직접 올해 공채 접수기간 및 일정, 전형절차, 지원방법 등을 설명해주었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;❓ 영상 내용 요약&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;핵심 키워드는 &lt;strong&gt;기본기, 커뮤니케이션, 성장가능성, 개발열정&lt;/strong&gt;으로 이 키워드를 중심으로 평가한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;전형 절차는 &lt;strong&gt;지원서 작성, 온라인 코딩테스트 1차 기술면접(인성검사 실시), 2차 종합면접&lt;/strong&gt; 순으로 진행하지만 &lt;strong&gt;모든 전형을 온라인으로 진행&lt;/strong&gt;된다.&lt;/p&gt;
&lt;p&gt;◾ 지원서 작성: 9월 1~3주&lt;br&gt;◾ 온라인 코딩테스트: 9월 4주 주말&lt;br&gt;◾ 1차 기술면접: 10월 중순&lt;br&gt;◾ 2차 기술면접: 11월 중순(금/토/일)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;지원자격&lt;/p&gt;
&lt;p&gt;◾ 전공무관&lt;br&gt;◾ 2021년 2월 졸업 예정 학/석사 또는 경력 2년 미만의 기졸업자&lt;br&gt;◾ 2021년 1월 입사 후 정상근무 가능한 분&lt;br&gt;◾ 병역필 또는 면제자이며, 해외여행에 결격사유가 없는 분&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  작년 공채 지원서 문항&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;아래 중 특별히 관심있는 분양를 기재해주세요. 최소한의 관심 분야를 확인하기 위함입니다.&lt;/p&gt;
&lt;p&gt;Web FE, Android, iOS, Server, Machine Learning/Deep Learning/ Data Analysis&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;컴퓨터 공학 관리 이수 과목명과 해당 성적을 기재해주세요. (관련성이 높은 과목부터 최대 6개 작성)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;어려운 기술적 문제를 해결한 경험을 기술해주세요.&lt;/p&gt;
&lt;p&gt;학교수업, 프로젝트, 경진대회, 대회활동 등 (코드로 설명해주면 더욱 좋아요, 못 푼 문제도 좋아요, 러닝커브)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;본인이 가진 에너지를 최대한 집중해 끝까지 파고들었던 경험을 기재해주세요.&lt;/p&gt;
&lt;p&gt;공부, 개발, 색다른 경험, 취미등 어떤 분야의 경험도 무관&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;네이버 서비스 중 하나를 선택, 개발자로서 어떤 기능을 개선/개발하고 싶은지 관련된 기술 요소에 대해 구체적으로 작성해주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;어떤 개발 활동을 해 왔는지, 과정을 가장 잘 보여줄수 있는 GitHub과 같은 Git 기반의 url을 기재해주세요.(선택)&lt;/p&gt;
&lt;p&gt;오픈소스 컨트리뷰션, 프로젝트, 본인이 작성한 소스코드)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;  온라인 코딩테스트&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;코딩/ 알고리즘 활용 기본기 검증&lt;/p&gt;
&lt;p&gt;언어: #C, C++, JAVA, JavaScript, Python3, Swift, Kotlin중 선택&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;작년난이도는 총 세문제로 하 두문제와 중 한문제&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  1차 기술 면접&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;수리+CS 문제풀이 과정 검증&lt;/p&gt;
&lt;p&gt;지식의 양이 아닌 사고의 방법을 관찰, 기본기에 대한 심층 검증, 질의응답을 통한 커뮤니케이션 역량 파악&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  2차 기술 면접&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;기본기+커뮤니케이션 종합 리뷰&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>IT이것저것</category>
      <category>2020 NAVER DEVELOPER OPEN CLASS</category>
      <category>Naver</category>
      <category>NAVER DEVELOPER OPEN CLASS</category>
      <category>네이버 신입 개발자 채용 설명회</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/11</guid>
      <comments>https://haranglog.tistory.com/11#entry11comment</comments>
      <pubDate>Sat, 29 Aug 2020 11:22:41 +0900</pubDate>
    </item>
    <item>
      <title>[React] 리액트에서 react-moment, moment.js 사용하여 실시간으로 변경되는 시간 만들기</title>
      <link>https://haranglog.tistory.com/10</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/moment/moment&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Moment.js&lt;/a&gt;는 &amp;nbsp;현재 유지 관리 모드에 있는 레거시 프로젝트입니다. 다른 라이브러리를 사용하시는 것을 추천드립니다. (&lt;a href=&quot;https://momentjs.com/docs/#/-project-status/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고&lt;/a&gt;)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;a href=&quot;https://momentjs.com/docs/#/-project-status/recommendations/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;다른 대안 라이브러리&lt;/a&gt; (2023.02.25 수정)&lt;/b&gt;&lt;/blockquote&gt;
&lt;h1&gt;✔ moment.js&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/moment/moment&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;moment.js&lt;/a&gt;는 시간이 포함된 데이터를 받아 조작해야 할 경우 가장 많이 사용되는 편리한 라이브러리입니다.&lt;/li&gt;
&lt;li&gt;스터디를 모집하는 사이드 프로젝트를 만들 때 사용하였습니다. 현재 시간에 따라서 &lt;b&gt;스터디 신청 마감, 신청중인지&lt;/b&gt; 에 따라 실시간으로 변경이 되게 많드는 것이 목표였기 때문에 moment.js를 사용하였습니다.&lt;/li&gt;
&lt;li&gt;moment.js는 워낙 많이 사용되기 때문에 &lt;a href=&quot;https://momentjs.com/docs/&quot;&gt;&lt;b&gt;공식 문서&lt;/b&gt;&lt;/a&gt; 뿐만 아니라 잘 설명된 블로그들이 많이 존재하기 때문에 moment.js를 사용하는 자세한 방법은 생략하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  moment 라이브러리 설치&lt;/h4&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;$ npm i moment
// 또는
$ yarn add moment&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  moment 선언&lt;/h4&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;import moment from 'moment';
// 안써도 자동으로 한국 시간을 불러온다. 명확하게 하기 위해 import
import 'moment/locale/ko';&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  현재 시간 가져오기&lt;/h4&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;// format에 맞게 출력된다.
const nowTime = moment().format('YYYY-MM-DD HH:mm:ss');
console.log(nowTime);
// 출력 결과: 2020-08-23 12:54:30&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;code&gt;F5&lt;/code&gt;키를 눌렀을 때만 시간이 변경됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✌ 리액트 훅스 컴포넌트에서 setInterval 사용 시의 문제점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실시간으로 시간을 변경하기 위해 흔히 사용하는 setInterval을 사용해야 됩니다. 하지만 react Hooks에서 setInterval을 사용했을 때 예상대로 작동하지 않는 문제가 발생합니다.&lt;/li&gt;
&lt;li&gt;이 문제에 대한 설명과 그에 대한 해결방법이 자세하게 설명되어 있는 &lt;a href=&quot;https://overreacted.io/making-setinterval-declarative-with-react-hooks/&quot;&gt;&lt;b&gt;블로그&lt;/b&gt;&lt;/a&gt;를 참고하시기 바랍니다.&lt;/li&gt;
&lt;li&gt;이러한 이유와 성능상의 이슈 때문에 커스텀 Hook을 생성해서 &lt;code&gt;setInterval&lt;/code&gt;을 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// useInterval.js 작성
import { useRef, useEffect } from &quot;react&quot;;

export default function useInterval(callback, delay) {
    const savedCallback = useRef();

    useEffect(() =&amp;gt; {
      // useEffect에 매개변수로 받은 콜백을 현재 Ref로 선언해준다.
      savedCallback.current = callback;
    });

    useEffect(() =&amp;gt; {
      function tick() {
        savedCallback.current();
      }
        // useEffect에 Ref의 current를 setInterval를 delay 시간동안 해준다.
      let id = setInterval(tick, delay);
      // 언마운트되기전 clearInterval을 해준다.
      return () =&amp;gt; clearInterval(id);
    }, [delay]);
  }&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;a href=&quot;https://www.npmjs.com/package/react-use&quot;&gt;react-use&lt;/a&gt;의 &lt;a href=&quot;https://github.com/streamich/react-use/blob/HEAD/docs/useInterval.md&quot;&gt;useInterval&lt;/a&gt;를 사용하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import * as React from 'react';
import {useInterval} from 'react-use';

const Demo = () =&amp;gt; {
  const [count, setCount] = React.useState(0);
  const [delay, setDelay] = React.useState(1000);
  const [isRunning, toggleIsRunning] = useBoolean(true);

  useInterval(
    () =&amp;gt; {
      setCount(count + 1);
    },
    isRunning ? delay : null
  );

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div&amp;gt;
        delay: &amp;lt;input value={delay} onChange={event =&amp;gt; setDelay(Number(event.target.value))} /&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;h1&amp;gt;count: {count}&amp;lt;/h1&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;button onClick={toggleIsRunning}&amp;gt;{isRunning ? 'stop' : 'start'}&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};&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;  다른 useInterval 라이브러리입니다. 조금씩은 다르지만 기능적으로는 비슷합니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/react-useinterval&quot;&gt;&lt;b&gt;react-useinterval&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://www.npmjs.com/package/use-interval&quot;&gt;&lt;b&gt;use-interval&lt;/b&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  useInterval를 사용하여 실시간 시간 사용하기&lt;/h4&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React,{useState}  from 'react';

import moment from 'moment';
import 'moment/locale/ko';
import { useInterval } from 'react-use';

const LiveTimeContainer = () =&amp;gt; {
  const [realTime, setRealTime] = useState(Date.now());

  // useInterval
  useInterval(() =&amp;gt; {
    setRealTime(Date.now());
  }, 1000);

  return &amp;lt;div&amp;gt;{seconds}&amp;lt;/div&amp;gt;
}&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;code&gt;moment()&lt;/code&gt; 시간을 받아와 별다른 작업없이 시간이 실시간으로 1초마다 변하는 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;✔ react-moment&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/react-moment&quot;&gt;&lt;b&gt;react-moment&lt;/b&gt;&lt;/a&gt;는 react에서 moment를 편리하게 사용할 수 있게 만들어 놓은 라이브러리입니다.&lt;/li&gt;
&lt;li&gt;심지어 react-moment는 별다른 작업을 하지않아도 실시간으로 시간이 변하게 제공을 해줍니다.&lt;/li&gt;
&lt;li&gt;  react-moment의 자세한 설명은 &lt;a href=&quot;https://github.com/headzoo/react-moment&quot;&gt;&lt;b&gt;공식 문서&lt;/b&gt;&lt;/a&gt;를 참고해주세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  react-moment 라이브러리 설치&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;react-moment는 moment가 dependency되어 있기 때문에 &lt;code&gt;moment&lt;/code&gt; 부터 설치해주어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;$ npm i moment react-moment
// yarn
$ yarn add moment react-moment
// moment-timezone을 사용할려면
$ npm i moment-timezone&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;b&gt;접수중&lt;/b&gt;으로 상태가 변하고 대회도 마찬가지로 접수가 마감되고 대회시작의 상태를 알려주도록 구현하기 위해서 react-moment를 사용하였습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthContent&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lKhDb/btqG5MnfT8f/98kGWOvHKFm8nuNSV8c8aK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lKhDb/btqG5MnfT8f/98kGWOvHKFm8nuNSV8c8aK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lKhDb/btqG5MnfT8f/98kGWOvHKFm8nuNSV8c8aK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlKhDb%2FbtqG5MnfT8f%2F98kGWOvHKFm8nuNSV8c8aK%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;1039&quot; height=&quot;130&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  react-moment 선언하고 기본 익히기&lt;/h4&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React  from 'react';
import Moment from 'react-moment';

const MomentDateChage = () =&amp;gt; {
  const nowTime = Date.now();
  // Sun Aug 23 2020 15:43:49 GMT+0900
  return &amp;lt;Moment&amp;gt;{nowTime}&amp;lt;/Moment&amp;gt;;
}&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;기본적으로 react-moment는 60초(60000 milliseconds)로 &lt;code&gt;interval&lt;/code&gt;을 합니다. 하지만 &lt;code&gt;interval&lt;/code&gt;을 수정하여 사용할 수 있고 굳이 필요없는 곳에는 &lt;code&gt;interval&lt;/code&gt;을 &lt;code&gt;disable&lt;/code&gt;시킬 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React  from 'react';
import Moment from 'react-moment';

const MomentDateChage = () =&amp;gt; {
  const nowTime = Date.now();
  // interval 30초
  //return &amp;lt;Moment interval = { 30000 }&amp;gt;{nowTime}&amp;lt;/Moment&amp;gt;;
  // interval disable
  return &amp;lt;Moment interval = { 0 }&amp;gt;{nowTime}&amp;lt;/Moment&amp;gt;;
}&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;/li&gt;
&lt;li&gt;이렇게 적용해주면 5분 후 또는 몇초 후, 몇일 후로 나오게 되는데 만약 영어로 나올 경우는 아래와 같이 적용해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React  from 'react';
import Moment from 'react-moment';
import 'moment/locale/ko';

const MomentDateChage = () =&amp;gt; {
  const nowTime = Date.now(),
        startTime = new Date(&quot;2020-08-23T16:00&quot;);

  return &amp;lt;Moment fromNow&amp;gt;{startTime}&amp;lt;/Moment&amp;gt;;
}&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;a href=&quot;https://99geo.tistory.com/19&quot;&gt;&lt;b&gt;블로그&lt;/b&gt;&lt;/a&gt;를 참고하시기 바랍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✌ react-moment와 moment를 사용하여 응용해보기&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;&lt;code&gt;moment&lt;/code&gt;와 &lt;code&gt;useInterval&lt;/code&gt;를 사용하여 현재 시간을 불러오고, react-moment로 자동으로 시간이 변경되게 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthContent&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;103&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WHek5/btqG4aIHdBy/3EdGRAPlTOSC2BI98IhEsK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WHek5/btqG4aIHdBy/3EdGRAPlTOSC2BI98IhEsK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WHek5/btqG4aIHdBy/3EdGRAPlTOSC2BI98IhEsK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/WHek5/btqG4aIHdBy/3EdGRAPlTOSC2BI98IhEsK/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;103&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;103&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;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React,{useState}  from 'react';

import moment from 'moment';
import Moment from 'react-moment';
import 'moment/locale/ko';

import { useInterval } from 'react-use';

const LiveTimeContainer = () =&amp;gt; {
  const [seconds, setSeconds] = useState(Date.now());

  // useInterval
  useInterval(() =&amp;gt; {
    setSeconds(Date.now());
  }, 1000);

  const startTime = new Date('2020-08-23T16:09'),
        nowTimeFormat = new Date(seconds);

  return (
      &amp;lt;&amp;gt;
    {startTime - nowTimeFormat &amp;gt; 0 ? 
     (&amp;lt;&amp;gt;&amp;lt;Moment fromNow&amp;gt;{startTime}&amp;lt;/Moment&amp;gt;&amp;amp;nbsp;접수 시작&amp;lt;/&amp;gt;) : (&amp;lt;div&amp;gt;{'접수 중'}&amp;lt;/div&amp;gt;)
    }  
    &amp;lt;/&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  스터디 모집 사이트 소스를 확인해보고 싶으면 &lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin&quot;&gt;&lt;b&gt;Github&lt;/b&gt;&lt;/a&gt;를 확인하시기 바랍니다.&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/blob/main/src/containers/groups/StudyGroupsContainer.jsx#L25-L27&quot;&gt;StudyGroupsContainer.jsx&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/blob/main/src/components/common/DateTimeChange.jsx&quot;&gt;DateTimeChange.jsx&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/CodeSoom/project-react-2-saseungmin/blob/main/src/components/introduce/IntroduceForm.jsx#L104&quot;&gt;IntroduceForm.jsx&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  또한, 수정해야할 정보나 부분이 있으면 댓글로 피드백 부탁드립니다.  &lt;/h4&gt;</description>
      <category>React</category>
      <category>moment</category>
      <category>react</category>
      <category>react-moment</category>
      <category>useInterval</category>
      <category>리액트</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/10</guid>
      <comments>https://haranglog.tistory.com/10#entry10comment</comments>
      <pubDate>Sun, 23 Aug 2020 16:30:24 +0900</pubDate>
    </item>
    <item>
      <title>  2020 FrontEnd 개발자가 되기 위한 10가지 방법</title>
      <link>https://haranglog.tistory.com/9</link>
      <description>&lt;blockquote&gt;
&lt;p&gt;이 글은 2020년 07월 15일에 있었던 7월 우아한세미나(주니어개발자로 성장하는 우아한 방법들)를 듣고 요약한 것입니다.&lt;br /&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Qtg5xe6B_vA&quot;&gt;https://www.youtube.com/watch?v=Qtg5xe6B_vA&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=Qtg5xe6B_vA&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bqYf91/hyHmQOUuD7/DMsNkhzdsUFQNoxdEfWF10/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/Qtg5xe6B_vA&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;  세상은...&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;웹 프론트엔드 기술이 활용되는 서비스 범위가 매우 넓어지고 웹의 기술 역시 다양하고 복잡하게 발전하고 있다.&lt;/li&gt;
&lt;li&gt;또한, 서비스와 제품의 기술적 요구사항도 다양해지는 중이다.&lt;/li&gt;
&lt;li&gt;이런 세상에서 우리가 어떻게 대응할 것인가를 10가지로 알아본다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✌ 1. 현장 이해하고 대응하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;첫 번째 주제인 현장을 이해하고 대응하는 것이 사실상 결론이고 가장 중요한 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  현장의 세가지 특징&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;함께 일하기&lt;/li&gt;
&lt;li&gt;분석, 개발, 테스트, 배포, 피드백, 개선&lt;/li&gt;
&lt;li&gt;제품과 서비스의 &lt;b&gt;지속적인&lt;/b&gt; 운영&lt;/li&gt;
&lt;li&gt;product를 만들고 서비스를 만들어도 끊임없이 유지하는 것이 현장의 대표적인 특징.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  여기서 발생하는 문제들&lt;/h4&gt;
&lt;ol&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;li&gt;테스팅의 어려움(품질과 관련되어 있고 현장에서의 어려움)&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;❗ 요즘 채용 공고를 보면 기업에서 원하는 기술과 지식이 많고 복잡하다.&lt;/h4&gt;
&lt;h4&gt;  이러한 상황에서의 우리의 대응법&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;  연습은 실전처럼하는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;기초 프로그래밍 연습 -&amp;gt; 미니 프로젝트 -&amp;gt; 좀더 큰 프로젝트 -&amp;gt; 같이 할 수 있는 프로젝트&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 프로젝트를 하면서 모르는 부분을 그때 그때 찾아보자.&lt;br /&gt;인턴경험, 알바 경험 등등&lt;br /&gt;현장이 원하는 인재는 경력같은 주니어..&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;  문제 해결 능력 향상
&lt;ul&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&gt;✌ 2. 프로젝트를 통해서 JS 익히기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;JavaScript를 익히는 것이 가장 중요하다❗&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;책과 프로젝트의 거리감을 줄이고 두 개를 병행하되, 중심은 프로젝트로 하는 것이 좋다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;프로젝트를 하면서 언어 책을 함께 공부하는 것도 좋은 방법이다.&lt;/p&gt;
&lt;p&gt;예를 들어 이런게 prototype이구나&lt;br /&gt;closure를 활용해서 이렇게 구현할 수 있구나.&lt;br /&gt;let, const의 차이가 이런거 구나.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;  면접에서 이론을 많이 묻는 이유는 기초지식을 충분히 알고 대응하는 개발자인지를 확인하기 위해서이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✌ 3. 향상된 UX를 목표로 개발하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;FE 개발자는 사용자와 접점이 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;FE 개발자의 많은 시간은 완벽한 코드보다, 사용성 향상작업이다.(성능, 매끄러움 등)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;사용자가 쓰기에 불편함이 없는 인터랙션 개발를 해야한다.&lt;/p&gt;
&lt;p&gt;애니메이션&lt;br /&gt;좀더 빠르고 좀더 직관적인 UX 고민, 개선&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CSS3, DOM, Event, Animation 제어, 비동기에 대한 이해&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;디바운스 쓰로틀링&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✌ 4. 함께 자라기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;현업은 함께 익히고 함께 고민하고 함께 만들어 간다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;프로젝트를 할 수준이 아니더라도 함께 학습하고, 간단한 프로젝트를 함께 만들어보고 그에 대한 피드백을 받는 것이 중요하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;함께 맞춰보는 것은 피곤하지만 맞춰보는 것이 중요하다.&lt;/p&gt;
&lt;p&gt;업무를 분석하고 github issue/project로 일감 만들어서 관리하기&lt;br /&gt;PR 보내기&lt;br /&gt;코드리뷰 하기&lt;br /&gt;git branch 전략세워보기&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;주변에 같이 할 사람이 없으면 커뮤니티를 활용해서 사람들과 함께 해본다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cafe.naver.com/hacosa&quot;&gt;하드코딩 하는 사람들&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;협업도구 슬랙과 트렐로&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✌ 5. 기초는 튼튼하게&lt;/h2&gt;
&lt;h3&gt;✨ 기초에서 좀더 중요한 것들은?&lt;/h3&gt;
&lt;h4&gt;1️⃣ 자료구조와 알고리즘&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;채용단게를 뚫기 위해서는 반드시 필요 ❗&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;문제를 좀더 다양한 방법으로 풀 수 있는 능력이 생기고, 효율성을 고려하면서 구현할 수 있는 능력이 생김.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;꾸준히 연습을 하고 지속가능한 방법을 찾는 것이 중요하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;연습은 실전처럼&lt;/p&gt;
&lt;p&gt;◾ 시간 재기&lt;br /&gt;◾ 쉬운 문제 많이 풀어보기&lt;br /&gt;◾ 같은 문제 다시 풀어보기&lt;br /&gt;◾ 다른 사람 코드 보기&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2️⃣ 네트워크&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;처음부터 다 보는 것보다도 HTTP 중심으로 보는 것이 중요하다.(TCP, IP를 포함해서)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;크롬의 개발자도구의 Network 탭에 있는 항목들을 모두 다 아는 것이 좋다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;HTTP 관련 책(추천 책은 HTTP 완벽 가이드/ 간단하게 볼려면 그림으로보는 HTTP&amp;amp;Network Basic) 읽기&lt;/p&gt;
&lt;p&gt;HTTP는 고전 책을 봐도 좋다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;개발하면서 알게 되는 용어를 찾아서 공부해보기.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;네트워크 웹 보안(XSS,CSRF,OWASP)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3️⃣ 백엔드 접해보기&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;우리가 어떤 일을 어떻게 접할지 모른다.&lt;/p&gt;
&lt;p&gt;SSR(server-side rendering)을 위한 서버를 운영해야 한다면?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Node.js 중심으로 웹 애플리케이션 서버와 연동해보기&lt;/p&gt;
&lt;p&gt;내가 로그인을 만들면 HTTP요청을 보내고 Request를 보내면 서버에서 어떻게 받는지, Response는 어떻게 하는지 등에 대한 이해&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Database, OS, 클라우드(AWS, 그래도 우선순위가 낮다.) 등의 지식도 중요하다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;  종합적으로 프론트앤드 개발자보다는 좋은 프로그래머로 성장하는 것이 핵심이기 때문에 기초는 꾸준히 해야한다.&lt;/b&gt;&lt;/p&gt;
&lt;h2&gt;✌ 6. HTML, CSS에서 FE 엔지니어로!&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Component 기반(SPA) 개발이 표준이 된 세상&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그렇기 때문에 HTML, CSS, JS를 따로 개발하기보다 함께 개발해야 하는 시대&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만, React, Vue 를 할 줄 안다고 FE 개발자가 되기 어렵다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;가장 중요하고 가장 먼저 해야되는 것은 &lt;b&gt;바닐라 자바스크립트가 중요하다!!!!&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;꾸미는 것을 좋아하지 않는 분들은 FE 개발자가 되는 것은 좋지 않다고 생각한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;프로그래밍 사고 늘리기가 최우선이다. ❗❗❗&lt;/p&gt;
&lt;p&gt;◾ JS책만 보지 말고, 무언가 만들어보기&lt;br /&gt;◾ 조건, 반복, 함수를 활용한 호출관계, 모듈을 만드는 방법 등&lt;br /&gt;◾ 알고리즘 문제, 쉬운 문제를 많이 그리고 꾸준히 푸는 연습이 필요하다.&lt;br /&gt;◾ 자바스크립트 프로그래밍 연습&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✌ 7. 프론트..원래 이럼?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;  &lt;a href=&quot;https://github.com/devJang/developer-roadmap#%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%A1%9C%EB%93%9C%EB%A7%B5&quot;&gt;프론트앤드 로드맵&lt;/a&gt;을 보면 배워야할 것들이 너무 많다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;전부 다 잘하기에는 힘들기 때문에 양보다는 질이 중요하다. 그렇기 때문에 무언가를 깊이 있게 다룰 수 있는 것이 중요하다.&lt;/p&gt;
&lt;p&gt;예를 들어 공부하다 모르는 부분을 만나서 그 문제에 대해서 어느정도로 깊게 파고 들었나&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;내가 하고 있는 분야를 깊게 파고 있는가?&lt;/p&gt;
&lt;p&gt;◾ 피드백받고 개선하기.&lt;br /&gt;◾ 리팩토링을 시도하고 실험했는가&lt;br /&gt;◾ 좀더 사용성을 올리기 위해서 다양한 방법을 찾아보고 시도했는가?&lt;br /&gt;◾ 내가 얻어낸 코드를 공유하고, 노하우를 알리거나, 오픈소스화했는가?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✌ 8. React, Vue, Angular 익히기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;React, Vue, Angular같은 프레임워크는 추상화 되어있고 편리한데 internal logic(내부 논리)을 알기 어렵다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;모두 다 할 필요는 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;바닐라로 훈련이 충분히 된 상태에서 접근하는 것이 좋다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이미 프레임워크를 다루었다면 바닐라로 프레임워크 흉내내보기&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하나만 깊이 있게 하는 것이 중요하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;특정 프레임워크 비의존적인 부분에 대한 학습이 중요하다.&lt;/p&gt;
&lt;p&gt;◾ 각 프레임워크의 동작 방식&lt;br /&gt;◾ 렌더링 원리&lt;br /&gt;◾ 상태 관리&lt;br /&gt;◾ 모듈 관리&lt;br /&gt;◾ 성능에 유리한 코딩방식&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✌ 9. 내가 선택한 길이 맞을까?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;열심히 그리고 즐겁게 개발하고 있다면 틀린 길은 없다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;나를 믿고 전진&lt;/b&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;h2&gt;✌ 10. 나를 알아봅시다.&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이력서 써보기&lt;/li&gt;
&lt;li&gt;면접 기회 얻어보기 (부족한 것을 피드백 받기)&lt;/li&gt;
&lt;li&gt;면접 기회가 없으면 면접 스터디 같은거라도..&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  좋은 회사를 알아볼 수 있는 tip&lt;/h4&gt;
&lt;ul&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;h4&gt;  &lt;a href=&quot;https://www.notion.so/44da2a336b864d35bd642bc62a300708&quot;&gt;개발자 지망생을 위한 주관적 추천도서 목록&lt;/a&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1598792031125&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;개발자 지망생을 위한 주관적 추천도서&quot; data-og-description=&quot;최종수정일: 2020년 7월 28일&quot; data-og-host=&quot;www.notion.so&quot; data-og-source-url=&quot;https://www.notion.so/44da2a336b864d35bd642bc62a300708&quot; data-og-url=&quot;https://www.notion.so/44da2a336b864d35bd642bc62a300708&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bGSCd9/hyHjTerFIg/yXTjLk3QVXdvwN3cLMuoo0/img.jpg?width=1500&amp;amp;height=1500&amp;amp;face=0_0_1500_1500,https://scrap.kakaocdn.net/dn/TtK48/hyHbMGzutV/YTJVS3gKvKrkjzbhdPxYY1/img.jpg?width=1500&amp;amp;height=1500&amp;amp;face=0_0_1500_1500&quot;&gt;&lt;a href=&quot;https://www.notion.so/44da2a336b864d35bd642bc62a300708&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.notion.so/44da2a336b864d35bd642bc62a300708&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bGSCd9/hyHjTerFIg/yXTjLk3QVXdvwN3cLMuoo0/img.jpg?width=1500&amp;amp;height=1500&amp;amp;face=0_0_1500_1500,https://scrap.kakaocdn.net/dn/TtK48/hyHbMGzutV/YTJVS3gKvKrkjzbhdPxYY1/img.jpg?width=1500&amp;amp;height=1500&amp;amp;face=0_0_1500_1500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;개발자 지망생을 위한 주관적 추천도서&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;최종수정일: 2020년 7월 28일&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.notion.so&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT이것저것</category>
      <category>FE 개발자</category>
      <category>우아한 세미나</category>
      <category>주니어 개발자</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/9</guid>
      <comments>https://haranglog.tistory.com/9#entry9comment</comments>
      <pubDate>Sun, 26 Jul 2020 20:53:11 +0900</pubDate>
    </item>
    <item>
      <title>  SPA(싱글 페이지 애플리케이션)</title>
      <link>https://haranglog.tistory.com/8</link>
      <description>&lt;h2&gt;✌ 라운드 트립 애플리케이션(round-trip)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;오랫동안 웹 앱은 라운드 트립 모델을 따랐다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;먼저 브라우저는 첫 HTML 페이지를 서버에 요청하고 수신하고 링크 클릭이나 폼 제출 등 사용자의 행위가 일어나면, 브라우저는 &lt;b&gt;다시 완전히 새로운 HTML 콘텐츠를 요청하고 수신한다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이런 방식은 애플리케이션에서 브라우저는 본질적으로 HTML 콘텐츠의 렌더링 엔진일 뿐이며, &lt;b&gt;모든 애플리케이션 로직과 데이터는 서버에 존재한다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;브라우저는 일련의 stateless(무상태) HTTP 요청을 보내고 서버는 그에 따라 HTML 문서를 동적으로 생성해 처리하는 식이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;웹 브라우저의 사용률이 낮은 대신 다양한 종류의 클라이언트&lt;/b&gt;를 지원할 때 라운드 트립 방식의 웹 개발을 많이 채택한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;❗ 라운드 트립 애플리케이션의 단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;사용자가 다음 HTML 문서가 요청되고 로딩될 때까지 기다려야 한다.&lt;/li&gt;
&lt;li&gt;모든 요청을 처리하고 모든 애플리케이션 상태를 관리하기 위해 서버 측에 대형 인프라가 필요하다.&lt;/li&gt;
&lt;li&gt;각 HTML 문서가 독립적으로 동일한 콘텐츠를 각자 포함하므로 더 넓은 대역폭이 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  이러한 이유로 리액트는 라운드 트립 애플리케이션에 적합하지 않은데, &lt;b&gt;서버로부터 수신한 새로운 HTML 문서마다 브라우저가 초기 준비 작업을 수행해야 하기 때문이다.&lt;/b&gt;&lt;/h4&gt;
&lt;h2&gt;✌ 싱글 페이지 애플리케이션(SPA, single-page application)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;라운드 트립 애플리케이션과 다른 접근법을 사용한는데 첫 HTML 문서의 로딩은 동일하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그 다음 사용자의 행위는 HTML의 작은 조각이나 데이터 집합에 &lt;b&gt;삽입할 일부 데이터만&lt;/b&gt;을 위한 HTTP 요청을 발생시킨다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;따라서, &lt;b&gt;초기 HTML 문서는 다시 로딩되거나 대체되지 않으며&lt;/b&gt;, HTTP 요청이 비동기 방식으로 수행되는 동안 사용자는 &lt;b&gt;계속 현재의 HTML과 상호작용할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;리액트는 SPA가 잘 어을리는데 애플리케이션을 초기화하는 브라우저 작업은 &lt;b&gt;처음 한 번만 수행&lt;/b&gt;되며, 애플리케이션이 실행된 후에는 데이터나 콘텐츠에 대한 요청과 사용자 행위에 대한 응답이 모두 &lt;b&gt;백그라운드에서 수행되기 때문이다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SPA는 때문에 말 그래도 단일 페이지로 구성되며 기존의 라운드 트립 애플리케이션과 비교할 때, 배포가 간단하며 네이티브 앱과 유사한 &lt;b&gt;사용자 경험(UX)을 제공할 수 있는 장점&lt;/b&gt;을 가진다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이러한 장점으로 인해 부가적으로 애플리케이션의 속도의 향상도 기대할 수 있어서 모바일 퍼스트 전략에 부합한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;❗ 싱글 페이지 애플리케이션의 단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;초기 구동 속도
&lt;ul&gt;
&lt;li&gt;SPA는 웹 애플리케이션에 필요한 정적 리소스를 최초에 한번 다운로드하기 때문에 초기 구동 속도가 상대적으로 느리다.&lt;/li&gt;
&lt;li&gt;하지만, 애플리케이션에 적합한 기술이므로 트래픽의 감소와 속도, 사용성, 반응성의 향상 등의 장점의 생각한다면 결정적인 단점이라고 할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;앱 규모가 커지면 자바스크립트 파일이 너무 커진다.
&lt;ul&gt;
&lt;li&gt;페이지 로딩 시 사용자가 실제로 방문하지 않을 수도 있는 페이지의 스크립트도 불러오기 때문이다.&lt;/li&gt;
&lt;li&gt;하지만, 코드 스플리팅(code splitting)을 사용해 라우트 별로 파일들을 나누어서 트래픽과 로딩 속도를 개선할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SEO(검색엔지 최적화) 문제
&lt;ul&gt;
&lt;li&gt;자바스크립트를 사용하여 라우팅을 관리하는 것은 자바스크립트를 실행하지 않는 일반 크롤러에서는 페이지의 정보를 제대로 수집해가지 못한다는 잠재적인 단점이 따른다. (서버 렌더링 방식이 아닌 자바스크립트 기반 비동기 모델이기 때문.)&lt;/li&gt;
&lt;li&gt;그렇기 때문에 검색 엔진의 검색 결과에 페이지가 잘 나타나지 않을 수도 있다.&lt;/li&gt;
&lt;li&gt;하지만 리액트에서는 &lt;a href=&quot;https://velopert.com/3425&quot;&gt;서버 사이드 렌더링(server-side rendering)&lt;/a&gt;을 통해 해결할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1595757772384&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;react-router :: 3장. 서버사이드 렌더링 | VELOPERT.LOG&quot; data-og-description=&quot;이 튜토리얼은 3개의 포스트로 나뉘어진 이어지는 강좌입니다. 목차를 확인하시려면 여기를 참고하세요. 3장. 서버사이드 렌더링 이번 장에서는 리액트 어플리케이션을 서버사이드 렌더링 하는&quot; data-og-host=&quot;velopert.com&quot; data-og-source-url=&quot;https://velopert.com/3425&quot; data-og-url=&quot;https://velopert.com/3425&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cLgjBA/hyGTs9nLew/Zq7WmASBO0cppW6CbJMBZK/img.png?width=950&amp;amp;height=499&amp;amp;face=0_0_950_499,https://scrap.kakaocdn.net/dn/yqs3a/hyGUOXioJJ/pvnZcZnVkibLkISidZuhWK/img.png?width=950&amp;amp;height=499&amp;amp;face=0_0_950_499,https://scrap.kakaocdn.net/dn/buE0wv/hyGUJIrKiM/0K4R25LjrBMPQvwtwhqOD1/img.png?width=946&amp;amp;height=789&amp;amp;face=0_0_946_789&quot;&gt;&lt;a href=&quot;https://velopert.com/3425&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velopert.com/3425&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cLgjBA/hyGTs9nLew/Zq7WmASBO0cppW6CbJMBZK/img.png?width=950&amp;amp;height=499&amp;amp;face=0_0_950_499,https://scrap.kakaocdn.net/dn/yqs3a/hyGUOXioJJ/pvnZcZnVkibLkISidZuhWK/img.png?width=950&amp;amp;height=499&amp;amp;face=0_0_950_499,https://scrap.kakaocdn.net/dn/buE0wv/hyGUJIrKiM/0K4R25LjrBMPQvwtwhqOD1/img.png?width=946&amp;amp;height=789&amp;amp;face=0_0_946_789');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;react-router :: 3장. 서버사이드 렌더링 | VELOPERT.LOG&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;이 튜토리얼은 3개의 포스트로 나뉘어진 이어지는 강좌입니다. 목차를 확인하시려면 여기를 참고하세요. 3장. 서버사이드 렌더링 이번 장에서는 리액트 어플리케이션을 서버사이드 렌더링 하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;velopert.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React</category>
      <category>react</category>
      <category>spa</category>
      <category>라운드 트립</category>
      <category>싱글 페이지 애플리케이션</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/8</guid>
      <comments>https://haranglog.tistory.com/8#entry8comment</comments>
      <pubDate>Sun, 26 Jul 2020 19:03:33 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 정규표현식(regular expression)</title>
      <link>https://haranglog.tistory.com/7</link>
      <description>&lt;h2&gt;  정규 표현식 정의&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;자바스크립트에서 정규 표현식은 RegExp 객체로 표현된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;정규 표현식 리터럴은 한 쌍의 슬래시(/) 문자 사이에 위치한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// s로 끝나는 모든 문자열과 매치된다.
const pattern = /s$/; // 문자열 리터럴
const pattern = new RegExp('s$'); // RegExp() 생성자를 사용하여 정의&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  리터럴 문자&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;모든 영문자와 숫자는 그 문자 그대로 정규 표현식에서 매치된다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;특정한 비-알파벳 문자들은 역슬래시(\)로 시작하는 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9D%B4%EC%8A%A4%EC%BC%80%EC%9D%B4%ED%94%84_%EB%AC%B8%EC%9E%90&quot;&gt;이스케이프 문자열&lt;/a&gt;을 통해 지원한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;구두점 문자(?,!,.,등등)를 일반 문자로 취급할려면 &lt;b&gt;반드시 \를 문자 앞에 붙여야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 역슬래시를 포함하는 모든 문자열과 매치된다.
const pattern = /\\/;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1594472801471&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;이스케이프 문자 - 위키백과, 우리 모두의 백과사전&quot; data-og-description=&quot;위키백과, 우리 모두의 백과사전. 이스케이프 문자는 이스케이프 시퀀스를 따르는 문자들로서 다음 문자가 특수 문자임을 알리는 백슬래시(\)를 사용한다. 일부 제어 시퀀스인 이스케이프 문자�&quot; data-og-host=&quot;ko.wikipedia.org&quot; data-og-source-url=&quot;https://ko.wikipedia.org/wiki/%EC%9D%B4%EC%8A%A4%EC%BC%80%EC%9D%B4%ED%94%84_%EB%AC%B8%EC%9E%90&quot; data-og-url=&quot;https://ko.wikipedia.org/wiki/%EC%9D%B4%EC%8A%A4%EC%BC%80%EC%9D%B4%ED%94%84_%EB%AC%B8%EC%9E%90&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9D%B4%EC%8A%A4%EC%BC%80%EC%9D%B4%ED%94%84_%EB%AC%B8%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.wikipedia.org/wiki/%EC%9D%B4%EC%8A%A4%EC%BC%80%EC%9D%B4%ED%94%84_%EB%AC%B8%EC%9E%90&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;이스케이프 문자 - 위키백과, 우리 모두의 백과사전&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;위키백과, 우리 모두의 백과사전. 이스케이프 문자는 이스케이프 시퀀스를 따르는 문자들로서 다음 문자가 특수 문자임을 알리는 백슬래시(\)를 사용한다. 일부 제어 시퀀스인 이스케이프 문자�&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;ko.wikipedia.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3&gt;  문자 클래스&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;개별 리터럴 문자들은 그 문자들을 &lt;b&gt;대괄호([])로 묶어서 문자 클래스&lt;/b&gt;로 다룰 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;문자 클래스는 해당 클래스 내의 모든 문자에 매치된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// a, b, c 중 아무 글자에나 매치된다.
const pattern = /[abc]/;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;부정 문자 클래스는 대괄호 안에 있는 무자들을 제외한 모든 문자와 매치된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;대괄호안에 &lt;b&gt;캐럿(^)&lt;/b&gt;을 제일 앞에 두면 부정 문자 클래스를 정의할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// a, b, c를 제외한 모든 문자와 매치된다.
const pattern = /[^abc]/;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하이픈(-)을 사용하여 &lt;b&gt;문자의 범위&lt;/b&gt;를 지정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 소문자 a부터 z까지와 매치된다.
const pattern = /[a-z]/;
// 모든 알파벳 글자와 숫자와 매치된다.
const pattern = /[a-zA-Z0-9]/;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;자바 스크립트 정규 표현식 문법에는 자주 사용되는 문자 클래스를 나타내는 특수 문자와 이스케이프 문자열이 정의 되어 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이스케이프된 특수 문자 클래스를 대괄호 내에서 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 공백 문자 하나 또는 숫자 문자 하나와 매치된다.
const pattern = /[\s\d]/;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;\b는 별도의 특별한 의미를 지니고 있지만, 문자 클래스 내에서 사용되면 &lt;b&gt;백스페이스 문자&lt;/b&gt;를 나타낸다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;정규 표현식에서 백스페이스 문자 자체를 표현하려면 &lt;b&gt;하나의 요소만&lt;/b&gt; 있는 문자 클래스를 사용해야 한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 백스페이스 리터럴
const pattern = /[\b]/;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  정규 표현식 문자 클래스&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;문자&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[...]&lt;/td&gt;
&lt;td&gt;대괄호 사이에 있는 한 문자.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[^...]&lt;/td&gt;
&lt;td&gt;대괄호 사이에 없는 한 문자.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.&lt;/td&gt;
&lt;td&gt;줄바꿈 문자나 유니코드 라인 종료 문자를 제외한 모든 문자.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\w&lt;/td&gt;
&lt;td&gt;ASCII 워드 문자. [a-zA-Z0-9]와 같다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\W&lt;/td&gt;
&lt;td&gt;ASCII 워드 문자가 아닌 모든 문자. [^a-zA-Z0-9]와 같다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\s&lt;/td&gt;
&lt;td&gt;모든 유니코드 공백 문자.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\S&lt;/td&gt;
&lt;td&gt;유니코드 공백 문자가 아닌 모든 문자. (but! \w와 \S는 같지 않다.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\d&lt;/td&gt;
&lt;td&gt;모든 ASCII 숫자. [0-9]와 같다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\D&lt;/td&gt;
&lt;td&gt;ASCII 숫자가 아닌 모든 문자. [^0-9]와 같다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[\b]&lt;/td&gt;
&lt;td&gt;백스페이스 리터럴(특수한 경우)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  반복&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;반복을 지정하는 문자는 항상 &lt;b&gt;반복을 지정할 패턴 뒤&lt;/b&gt;에 나온다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const pattern = /\d{2,4}/; // 두 자리에서 네 자리의 숫자와 매치된다.
const pattern = /\w{3}\d?/; // 3개의 문자와 생략 가능한 숫자와 매치된다.
const pattern = /\s+java\s+/; // java와 앞 뒤로 하나 이상의 공백 문자를 포함한 문자열과 매치된다.
const pattern = /[^(]*/; // 시작 괄호가 아닌 0개 이상의 문자와 매치된다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;  정규 표현식 반복 문자&lt;/h4&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;문자&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;{n,m}&lt;/td&gt;
&lt;td&gt;앞의 항목이 적어도 n번 이상, m번 이하로 나타난다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{n,}&lt;/td&gt;
&lt;td&gt;앞의 항목이 n번 이상 나타난다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{n}&lt;/td&gt;
&lt;td&gt;앞의 항목이 정확하게 n번 나타난다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;앞의 항목이 0 또는 한 번 나타난다. 즉, 안나올수도 있다.{0,1}과 같다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;앞의 항목이 한 번 이상 나타난다. {1,}과 같다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*&lt;/td&gt;
&lt;td&gt;앞의 항목이 0번 또는 그 이상 나타난다. {0,}과 같다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;* 와 ? 반복 문자는 앞에 오는 문자가 0번 매치된다고 간주하기 떄문에 반복 문자열 앞의 문자가 매치되지 않는 상황에서도 허용한다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;const pattern = /a*/;
&quot;bbbb&quot;.match(pattern);
// [&quot;&quot;, index: 0, input: &quot;bbbb&quot;, groups: undefined]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  대체, 그룹화, 참조&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;정규 표현식 문법은 &lt;b&gt;대체 표현식(alternative)&lt;/b&gt;, &lt;b&gt;부분 표현식 그룹화(grouping subexpression)&lt;/b&gt;, &lt;b&gt;이전 부분 표현식&lt;/b&gt;을 참조하는 특별한 문자를 포함하고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  대체 표현식(alternative)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;파이프 문자( | )는 대체 표현식을 구분한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;// 문자열 ab 또는 cd 또는 ef와 매치된다.
const pattern = /ab|cd|ef/;
// 세 자리의 숫자문자 또는 네 개의 소문자로 이루어진 문자열과 매치된다.
const pattern = /\d{3}|[a-z]{4}/;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;대체 표현식은 매치를 발견할 때까지 왼쪽에서 오른쪽으로 수행된다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;// ab에 적용하면, 오직 첫 번째 글자 a만 매치된다.
const pattern = /a|ab/;
&quot;ab&quot;.match(pattern);
// [&quot;a&quot;, index: 0, input: &quot;ab&quot;, groups: undefined]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  부분 표현식 그룹화(grouping subexpression)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;여러 항목을 하나의 부분 표현식으로 묶고(그룹화), 이렇게 묶인 항목들을 |,* ,+,? 등이 하나의 단위로 취급할 수 있도록 하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// java와 그다음에 생략 가능한 script가 오는 문자열과 매치된다.
const pattern = /java(script)?/;
// ab 또는 cd가 한 번 이상 반복되는 문자열 혹은 무자열 ef와 매치된다.
const pattern = /(ab|cd)+|ef/;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  이전 부분 표현식&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;전체 패턴 안에 부분 패턴을 정의하는 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;정규 표현식이 대상 문자열에 매치되면 괄호로 둘러싸인 특정 패턴과 매치되는 부분 문자열을 추출할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// 만약 각 매치에 대해 끝에 있는 숫자를 뽑는다면, 괄호 안에 두고 찾아낸 매치 결과에서 숫자를 추출할 수 있다.
const pattern = /[a-z]+(\d+)/;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;괄호로 둘러싸인 부분 표현식을 사용하면 같은 정규 표현식 내에서 &lt;b&gt;해당 부분을 다시 참조&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예를 들어 \1은 첫 번째 부분 표현식을 참조, \3은 세번째 부분 표현식을 참조한다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;// 중첩된 부분 표현 ([Ss]cript)는 \2로 참조할 수 있다.
const pattern = /([Jj]ava([Ss]cript?)\sis\s(fun\w*)/;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;문자&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;파이프 문자&lt;/td&gt;
&lt;td&gt;대체. 왼쪽에 있ㄴ는 부분 표현식이나 오른쪽에 있는 부분 표현식 중 하나를 매치한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(...)&lt;/td&gt;
&lt;td&gt;그룹화. 하나의 단위로 그룹화된 아이템으로 * ,+,?,파이프 문자등에 의해 사용될 수 있다. &lt;br /&gt;나중에 이 그룹을 참조할 수 있도록 이 그룹과 매치된 문자를 기억한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(?:...)&lt;/td&gt;
&lt;td&gt;그룹화만 한다. 그룹의 항목들은 하나의 단위로 취급되지만, 이 그룹에 매치되는 문자들은 기억하지는 않는다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\n&lt;/td&gt;
&lt;td&gt;n번째 그룹이 처음 매치했던 문자와 같은 문자를 매치한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  정규 표현식 앵커(anchor)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;매치에 사용할 문자를 지정하지 않고 매치가 일어날 수 있는 적절한 위치를 지정한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;시작 부분에 정규식을 고정하는 ^와 문자열의 마지막에 정규식을 고정하는 $가 많이 사용된다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;// JavaScript 문자열 자체가 매치된다.
const pattern = /^JavaScript$/;
// 이 경우 불필요한 공배까지 들어간다.
const pattern2 = /\sJava\s/;
&quot;my Java Script&quot;.match(pattern2);
// [&quot; Java &quot;, index: 3, input: &quot; my Java Script &quot;, groups: undefined]
// 단어 경계에 매치된다. 불필요한 공백이 없다.
const pattern3 = /\bJava\b/;
&quot;my Java Script&quot;.match(pattern3);
// [&quot;Java&quot;, index: 3, input: &quot;my Java Script&quot;, groups: undefined]&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(?= 와 ) 사이에 표현식이 있으면 이는 &lt;b&gt;긍정형 전방 탐색 선언문&lt;/b&gt;이고, 이는 사이에 있는 &lt;b&gt;문자들이 반드시 매치&lt;/b&gt;되어야 한다는 의미지만, &lt;b&gt;매치된 문자열이 결과에 포함되지 않는다&lt;/b&gt;는 뜻이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(?!를 사용하여 선언문을 만들면 &lt;b&gt;부정형 전방 탐색&lt;/b&gt;이고, 그다음에 나오는 문자열들이 매치되면 안 된다는 뜻이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;✨ 더 자세한 내용은 &lt;a href=&quot;https://blog.hexabrain.net/205&quot;&gt;참고&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1594472811866&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;정규 표현식(Regex) 강좌 9편. 전후방탐색(lookaround)&quot; data-og-description=&quot;1. 전방 탐색(lookahead) 전방 탐색(lookahead)이란 작성한 패턴에 일치하는 영역이 존재하여도 그 값이 제외되어서 나오는 패턴입니다. 전방 탐색 기호는 ?= 이며, = 다음에 오는 문자가 일치하는 영역�&quot; data-og-host=&quot;blog.hexabrain.net&quot; data-og-source-url=&quot;https://blog.hexabrain.net/205&quot; data-og-url=&quot;https://blog.hexabrain.net/205&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MQ8uF/hyGIy9ME4U/6BuCwyS82K0aaEfM34kdkk/img.jpg?width=620&amp;amp;height=413&amp;amp;face=0_0_620_413,https://scrap.kakaocdn.net/dn/b39f1D/hyGIuM6A19/gvhRJmVnLgkun4d9JtePo0/img.jpg?width=620&amp;amp;height=413&amp;amp;face=0_0_620_413,https://scrap.kakaocdn.net/dn/3JDCa/hyGIsPg5j2/FBGSrvK6MKkksjaur561J1/img.jpg?width=620&amp;amp;height=413&amp;amp;face=0_0_620_413&quot;&gt;&lt;a href=&quot;https://blog.hexabrain.net/205&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.hexabrain.net/205&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MQ8uF/hyGIy9ME4U/6BuCwyS82K0aaEfM34kdkk/img.jpg?width=620&amp;amp;height=413&amp;amp;face=0_0_620_413,https://scrap.kakaocdn.net/dn/b39f1D/hyGIuM6A19/gvhRJmVnLgkun4d9JtePo0/img.jpg?width=620&amp;amp;height=413&amp;amp;face=0_0_620_413,https://scrap.kakaocdn.net/dn/3JDCa/hyGIsPg5j2/FBGSrvK6MKkksjaur561J1/img.jpg?width=620&amp;amp;height=413&amp;amp;face=0_0_620_413');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;정규 표현식(Regex) 강좌 9편. 전후방탐색(lookaround)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;1. 전방 탐색(lookahead) 전방 탐색(lookahead)이란 작성한 패턴에 일치하는 영역이 존재하여도 그 값이 제외되어서 나오는 패턴입니다. 전방 탐색 기호는 ?= 이며, = 다음에 오는 문자가 일치하는 영역�&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;blog.hexabrain.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;문자&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;^&lt;/td&gt;
&lt;td&gt;문자열의 시작 부분, 정규식이 어려 줄을 대상으로 하는 경우 한 줄의 시작과 일치한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$&lt;/td&gt;
&lt;td&gt;문자열의 끝을 나타낸다. 여러 줄일 경우 한 줄의 마지막을 나타낸다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\b&lt;/td&gt;
&lt;td&gt;단어 경계를 나타낸다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\B&lt;/td&gt;
&lt;td&gt;단어 경계가 아닌 곳의 위치를 나타낸다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(?=p)&lt;/td&gt;
&lt;td&gt;전방 탐색 선언으로 패턴 p와 매치되어야 하지만, 매치 결과에는 포함되지 않는다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(?!p&lt;/td&gt;
&lt;td&gt;부정형 전방 탐색 선언으로 '(?!' 다음에 나오는 문자들은 패턴 p와 매치되지 않아야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  플래그(flag)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;다른 정규 표현식 문법과는 달리 &lt;b&gt;플래그는 / 문자 쌍 바깥&lt;/b&gt;에, 즉 두 번째 슬래시 다음에 쓴다.&lt;/li&gt;
&lt;li&gt;i 플래그는 &lt;b&gt;대소문자를 구별하지 않고&lt;/b&gt; 패턴 매칭이 되어야 한다.&lt;/li&gt;
&lt;li&gt;g 플래그는 전역 패턴 매칭을 지정하는데, 즉 검색할 문자열내에서 패턴의 첫 번째 매칭 결과뿐 아니라 &lt;b&gt;모든 매칭 결과를 얻는다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;m 플래그는 여러 줄에 걸쳐 패턴 매칭을 수행하는 것으로 m 플래그를 지정한 상태에서 검색될 문자열이 &lt;b&gt;줄바꿈 문자를 포함하고 있으면,&lt;/b&gt; ^와 $ 앵커는 문자열의 시작과 끝을 나타낼 뿐만 아니라 각 줄의 시작과 끝을 나타내기도 한다.
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// java 뿐만 아니라 Java\nis fun 에도 매치된다.
const pattern = /java$/im/;&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;  정규 표현식 플래그&lt;/h4&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;문자&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;i&lt;/td&gt;
&lt;td&gt;대소문자를 구별하지 않는 매칭을 수행한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;g&lt;/td&gt;
&lt;td&gt;전역 매칭을 수행한다. 즉, 처음 매치에서 끝내지 않고 모든 매치를 찾는다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;m&lt;/td&gt;
&lt;td&gt;여러 줄모드로 ^은 줄의 시작이나 문자열의 시작과 일치하고, $는 줄의 끝이나 문자열의 끝과 일치한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  참고 문서&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&amp;nbsp;MDN 공식 문서 : &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/%EC%A0%95%EA%B7%9C%EC%8B%9D&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/%EC%A0%95%EA%B7%9C%EC%8B%9D&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1594472819167&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;정규 표현식&quot; data-og-description=&quot;정규 표현식은 문자열에 나타는 특정 문자 조합과 대응시키기 위해&amp;nbsp;사용되는 패턴입니다. 자바스크립트에서, 정규 표현식 또한 객체입니다.&amp;nbsp; 이 패턴들은&amp;nbsp;RegExp의 exec 메소드와 test 메소드&amp;nbsp; ,��&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/%EC%A0%95%EA%B7%9C%EC%8B%9D&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/%EC%A0%95%EA%B7%9C%EC%8B%9D&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cJOKuN/hyGJT5IKDf/9JpWV5YMAMa4ZKKG61OeKK/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/Do2Ju/hyGJUjgHD2/DtFKVuem5hXnkeSjciYhpk/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/%EC%A0%95%EA%B7%9C%EC%8B%9D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/%EC%A0%95%EA%B7%9C%EC%8B%9D&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cJOKuN/hyGJT5IKDf/9JpWV5YMAMa4ZKKG61OeKK/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/Do2Ju/hyGJUjgHD2/DtFKVuem5hXnkeSjciYhpk/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;정규 표현식&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;정규 표현식은 문자열에 나타는 특정 문자 조합과 대응시키기 위해&amp;nbsp;사용되는 패턴입니다. 자바스크립트에서, 정규 표현식 또한 객체입니다.&amp;nbsp; 이 패턴들은&amp;nbsp;RegExp의 exec 메소드와 test 메소드&amp;nbsp; ,��&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JavaScript</category>
      <category>JavaScript</category>
      <category>Regular Expression</category>
      <category>정규식</category>
      <category>정규표현식</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/7</guid>
      <comments>https://haranglog.tistory.com/7#entry7comment</comments>
      <pubDate>Sat, 11 Jul 2020 16:28:53 +0900</pubDate>
    </item>
    <item>
      <title>[React] 함수형 컴포넌트의 React hooks</title>
      <link>https://haranglog.tistory.com/6</link>
      <description>&lt;h2&gt;  Hook의 개요&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Hook가 React 버전 16.8에 새롭게 추가되었다. Hook를 이용하여 Class를 작성할 필요 없이 상태 값과 여러 React의 기능을 사용할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;  Hook를 만들게 된 동기&lt;/h4&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;컴포넌트 사이에서 상태와 관련된 로직을 재사용하기 어렵다.&lt;/li&gt;
&lt;li&gt;복잡한 컴포넌트들은 이해하기 어렵다.&lt;/li&gt;
&lt;li&gt;Class은 사람과 기계를 혼동시킨다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;  &lt;a href=&quot;https://ko.reactjs.org/docs/hooks-intro.html&quot;&gt;자세한 내용 참조&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  Hook의 규칙&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Hook을 사용할 때는 두 가지 규칙을 준수해야 한다.&lt;br /&gt;이러한 규칙을 자동으로 지켜주는 &lt;a href=&quot;https://www.npmjs.com/package/eslint-plugin-react-hooks&quot;&gt;linter 플러그인&lt;/a&gt;이 있다.&lt;br /&gt;위 ESLint 플러그인은 create react app에 기본적으로 포함하고 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;1. 최상위에서만 Hook을 호출해야 한다.&lt;/b&gt;&lt;br /&gt;반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;2. 오직 React 함수 내에서 Hook을 호출해야 한다.&lt;/b&gt;&lt;br /&gt;Hook을 일반적인 javascript 함수에서 호출하지 말아야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;  useState&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;가장 기본적인 Hook로 함수형 컴포넌트에서 가변적인 상태를 지닐 수 있게 해 준다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useState&lt;/code&gt; 함수의 파라미터에는 상태의 기본값을 넣어주는 것으로 초기 렌더링 시에 사용하는 값이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그 이후의 렌더링 시에는 이 값은 무시된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const [value,setValue] = useState(0);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useState&lt;/code&gt; 함수는 호출되면 배열을 반환하는데 배열의 첫 번째 원소는 상태 값, 두 번째 원소는 상태를 설정하는 함수이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;setValue&lt;/code&gt; 함수는 &lt;code&gt;value&lt;/code&gt;를 갱신할 때 사용한다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;setValue(newValue);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;클래스 컴포넌트&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;   class Example extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          value: 0
        };
      }
      render() {
        return (
          &amp;lt;div&amp;gt;
            &amp;lt;p&amp;gt; 카운터 값 {this.state.value} 입니다. &amp;lt;/p&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; this.setState({ count: this.state.value + 1 })}&amp;gt;+1&amp;lt;/button&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; this.setState({ count: this.state.value - 1 })}&amp;gt;-1&amp;lt;/button&amp;gt;
          &amp;lt;/div&amp;gt;
        );
      }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;함수형 컴포넌트&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;  const Counter = () =&amp;gt; {
    const [value,setValue] = useState(0);

    return(
        &amp;lt;div&amp;gt;
            &amp;lt;p&amp;gt; 카운터 값은 &amp;lt;b&amp;gt;{value}&amp;lt;/b&amp;gt;입니다. &amp;lt;/p&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; setValue(value+1)}&amp;gt;+1&amp;lt;/button&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; setValue(value-1)}&amp;gt;-1&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    )&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;  state 갱신의 취소&lt;br /&gt;State Hook을 현재의 state와 동일한 값으로 갱신하는 경우 React는 자식을 렌더링 한다거나 무엇을 실행하는 것을 회피하고 그 처리를 종료한다.(React는 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description&quot;&gt;Object.is 비교 알고리즘&lt;/a&gt;을 사용한다.)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592911008155&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Object.is()&quot; data-og-description=&quot;Object.is() 메서드는 두 값이 같은 값인지 결정합니다.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/is&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cMN4zO/hyGvzHVSpE/WvHjKKEyyTxtEzkzwKfzYK/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/cfvwDi/hyGvtVfjE0/xvVEsvCyiwZsSsakKKMOJ1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cMN4zO/hyGvzHVSpE/WvHjKKEyyTxtEzkzwKfzYK/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/cfvwDi/hyGvtVfjE0/xvVEsvCyiwZsSsakKKMOJ1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Object.is()&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Object.is() 메서드는 두 값이 같은 값인지 결정합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2&gt;  useEffect&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useEffect&lt;/code&gt;에 전달된 함수는 화면에 렌더링이 완료된 후에 수행되게 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;클래스형 컴포넌트의 &lt;code&gt;componentDidMount&lt;/code&gt;와 &lt;code&gt;componentDidUpdate&lt;/code&gt;를 합친 형태랑 같다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useEffect()&lt;/code&gt;는 값이 업데이트될 때마다 실행되는데 화면에 맨 처음 렌더링 될 때만 실행하고, 업데이트될 때는 실행되지 않게 하려면 함수의 &lt;b&gt;두 번째 파라미터로 비어 있는 배열을 넣어 주면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이 배열은 effect가 종속되어 있는 값의 배열이지만, 빈 배열 시에 어떤 값에도 의존하지 않으므로, 다시 실행할 필요가 전혀 없어진다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;  useEffect(() =&amp;gt; {
      console.log({ name, nickName })
  },[])&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;특정 값이 업데이트될 때만 실행하고 싶으면 &lt;b&gt;두 번째 파리미터로 전달되는 배열 안에 검사하고 싶은 값을 넣어 주면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
console.log(name);
},[name])&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  effect 정리&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떤 작업을 수행하고 싶다면 &lt;code&gt;useEffect&lt;/code&gt;에서 정리(clean-up) 함수를 반환해 주어야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;정리 함수는 메모리 누수 방지를 위해 UI에서 컴포넌트를 제거하기 전에 수행된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;만약 컴포넌트가 여러 번 렌더링 된다면 다음 effect가 수행되기 전에 이전 effect는 정리된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;  useEffect(() =&amp;gt; {
      console.log(&quot;effect&quot;);
      console.log({
          name, nickName
      })
      return () =&amp;gt;{
          console.log('cleanUp');
          console.log(name);
      }
  },[])&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;정리 함수가 호출될 때는 &lt;b&gt;업데이트되기 직전 값을 보여준다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  useContext&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;  &lt;a href=&quot;https://ko.reactjs.org/docs/context.html&quot;&gt;React context 참고&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;const value = useContext(MyContext);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;context 객체(React.createContext에서 반환된 값)를 받아 그 context의 현재 값을 반환한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;컴포넌트에서 가장 가까운 &lt;code&gt;&amp;lt;MyContext.Provider&amp;gt;&lt;/code&gt;가 갱신되면 이 Hook은 그 MyContext.Provider에게 전달된 가장 최신의 context value를 사용하여 렌더러를 트리거한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useContext&lt;/code&gt;로 전달된 인자는 &lt;b&gt;context 객체 그 자체이어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useContext&lt;/code&gt;를 호출한 컴포넌트는 &lt;b&gt;context 값이 변경되면 항상 리렌더링 된다.&lt;/b&gt; 만약 &lt;a href=&quot;https://github.com/facebook/react/issues/15156#issuecomment-474590693&quot;&gt;컴포넌트를 리렌더링 하는 것에 복잡해진다면, 메모이제이션을 사용하여 최적화할 수 있다.&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;  const themes = {
    light: {
      foreground: &quot;#000000&quot;,
      background: &quot;#eeeeee&quot;
    },
    dark: {
      foreground: &quot;#ffffff&quot;,
      background: &quot;#222222&quot;
    }
  };

  const ThemeContext = React.createContext(themes.light);

  function App() {
    return (
    // Provider로 prop을 받아서 이 값을 하위에 있는 컴포넌트에게 전달한다. (context의 변화를 알린다.)
      &amp;lt;ThemeContext.Provider value={themes.dark}&amp;gt;
        &amp;lt;Toolbar /&amp;gt;
      &amp;lt;/ThemeContext.Provider&amp;gt;
    );
  }
  function Toolbar(props) {
    return (
      &amp;lt;div&amp;gt;
        &amp;lt;ThemedButton /&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
  function ThemedButton() {
    const theme = useContext(ThemeContext);
    return (
      &amp;lt;button style={{ background: theme.background, color: theme.foreground }}&amp;gt;test&amp;lt;/button&amp;gt;
    );
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  useReducer&lt;/h2&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;const [state, dispatch] = useReducer(reducer, initialArg, init);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useState&lt;/code&gt;의 대체 함수로 (state, action) =&amp;gt; newState의 형태로 &lt;b&gt;reducer를 받고 dispatch 메서드와 짝의 형태로 현재 state를 반환한다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;리듀서는 현재 상태, 그리고 업데이트를 위해 필요한 정보를 담은 액션 값을 전달받아 새로운 상태를 반환하는 함수이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;리듀서 함수에서 새로운 상태를 만들 때는 &lt;b&gt;불변성을 지켜야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;useReducer는 콜백 대신 &lt;b&gt;&lt;code&gt;dispatch&lt;/code&gt;를 전달할 수 있기 때문에&lt;/b&gt; 업데이트를 하는 컴포넌트의 성능을 최적화할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;  const initialState = {value : 0};
  function reducer(state,action){
    switch (action.type){
        case 'INCREMENT':
            return {value : state.value + 1};
        case 'DECREMENT':
            return {value : state.value - 1};
        default :
        //아무것도 해당되지 않을 때 기존 상태 반환
            return state;
    }
  }
  const Counter = () =&amp;gt; {
      // useReducer(리듀서 함수, 초기값)
    const [state, dispatch] = useReducer(reducer,initialState);
    return (
        &amp;lt;div&amp;gt;
            &amp;lt;p&amp;gt;
                현재 카운터 값은 &amp;lt;b&amp;gt;{state.value}&amp;lt;/b&amp;gt;입니다.
            &amp;lt;/p&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; dispatch({type : 'INCREMENT'})}&amp;gt;+1&amp;lt;/button&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; dispatch({type : 'DECREMENT'})}&amp;gt;-1&amp;lt;/button&amp;gt;

        &amp;lt;/div&amp;gt;
    );
  };&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;state&lt;/code&gt;값과 &lt;code&gt;dispatch&lt;/code&gt; 함수를 받아오는데 &lt;code&gt;state&lt;/code&gt;는 현재 가리키고 있는 상태이고, &lt;code&gt;dispatch&lt;/code&gt;는 액션을 발생 키기는 함수이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useReducer&lt;/code&gt;를 사용했을 때 가장 큰 장점은 &lt;b&gt;컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다는 점이다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  초기화 지연&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;초기화 state를 지연시켜서 생성할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;useReducer(리듀서 함수, 초기값, init 함수) 세 번째 인자로 전달한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예를 들어 버튼 클릭 시에 state를 재설정시킨다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;function init(initialCount) {
return {count: initialCount};
}

function reducer(state, action) {
switch (action.type) {
  case 'INCREMENT':
    return {count: state.count + 1};
  case 'DECREMENT':
    return {count: state.count - 1};
  case 'reset':
    return init(action.payload);
  default:
       state;
}
}

function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
  &amp;lt;&amp;gt;
    Count: {state.count}
    &amp;lt;button
      onClick={() =&amp;gt; dispatch({type: 'reset', payload: initialCount})}&amp;gt;
      Reset
    &amp;lt;/button&amp;gt;
    &amp;lt;button onClick={() =&amp;gt; dispatch({type: 'DECREMENT'})}&amp;gt;-&amp;lt;/button&amp;gt;
    &amp;lt;button onClick={() =&amp;gt; dispatch({type: 'INCREMENT'})}&amp;gt;+&amp;lt;/button&amp;gt;
  &amp;lt;/&amp;gt;
);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  useMemo&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useMemo&lt;/code&gt;를 사용하면 함수형 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98&quot;&gt;메모이제이션&lt;/a&gt;된 값을 반환한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;렌더링 하는 과정에서 &lt;b&gt;특정 값이 바뀌었을 때만 연산을 실행&lt;/b&gt;하고, 원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용하는 방식이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;useMemo로 전달된 함수는 &lt;b&gt;렌더링 중&lt;/b&gt;에 실행된다. 때문에 렌더링 중에 실행되지 않는 것은 사용하면 안 된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;  const getAverage = numbers =&amp;gt; {
      console.log(&quot;평균값 계산&quot;);
      if(numbers.length === 0)return 0;
      const sum = numbers.reduce((a,b) =&amp;gt; a+b);
      return sum / numbers.length;
  }
  // 생략..
  const avg = useMemo(() =&amp;gt; getAverage(list), [list]); // list 값이 변경될때만 실행
  // 생략..&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  useCallback&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useMemo&lt;/code&gt;와 비슷하고 렌더링 성능을 최적화해야 하는 상황에서 사용된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98&quot;&gt;메모이제이션&lt;/a&gt;된 콜백을 반환한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이벤트 핸들러 함수를 필요할 때만 생성할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;useCallback(fn, deps)은 useMemo(() =&amp;gt; fn, deps)와 같다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt; const onChange = useCallback(e =&amp;gt; {
   setNumber(e.target.value);
 },[]); //처음 랜더링될 때만 함수 생성
 const onInsert = useCallback(e =&amp;gt; {
   const nextList = list.concat(parseInt(number));
   setList(nextList);
   setNumber('');
 },[number,list]); //number, list 가 바뀌었을 떄만 함수 생성&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useCallback&lt;/code&gt;의 첫 번째 파라미터에는 생성하고 싶은 함수를 넣고, 두 번째 파라미터에는 어떤 값이 바뀌었을 때 함수를 새로 생성해야 하는지를 명시하는 배열을 넣는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;빈 배열을 넣게 되면 컴포넌트가 렌더링 될 때 단 한 번만 함수가 생성되고, 배열 안에 값을 넣게 되면 내용일 바뀌거나 새로운 항목이 추가될 때마다 함수가 생성된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  useRef&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;함수형 컴포넌트에서 &lt;code&gt;ref&lt;/code&gt;를 쉽게 사용할 수 있도록 해준다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function TextInputWithFocusButton() {
  const inputEl = useRef(null); //ref 값 기본값 설정
  const onButtonClick = () =&amp;gt; {
    inputEl.current.focus(); // 등록버튼 누른뒤 input 태그 focus
  };
  return (
    &amp;lt;&amp;gt;
      &amp;lt;input ref={inputEl} type=&quot;text&quot; /&amp;gt;
      &amp;lt;button onClick={onButtonClick}&amp;gt;클릭&amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;useRef&lt;/code&gt;를 사용하여 &lt;code&gt;ref&lt;/code&gt;를 설정하면 &lt;code&gt;useRef&lt;/code&gt;를 통해 만든 객체 안의 &lt;code&gt;current&lt;/code&gt; 값이 실제 엘리먼트를 가리킨다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ref&lt;/code&gt; 안의 값이 바뀌어도 컴포넌트가 &lt;b&gt;렌더링 되지 않는다.&lt;/b&gt;(렌더링과 관련되지 않은 값을 관리할 때만 사용)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  useImperativeHandle&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;useImperativeHandle(ref, createHandle, [deps])&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;useImperativeHandle은 ref를 사용할 때 부모 컴포넌트에 노출되는 인스턴스 값을 사용자 화한다.&lt;/li&gt;
&lt;li&gt;useImperativeHandle는 forwardRef와 같이 사용한다.
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () =&amp;gt; ({
    focus: () =&amp;gt; {
      inputRef.current.focus();
    }
  }));
  return &amp;lt;input ref={inputRef} ... /&amp;gt;;
}
FancyInput = forwardRef(FancyInput);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;FancyInput ref={inputRef} /&amp;gt;&lt;/code&gt;를 렌더링 한 부모 컴포넌트는 &lt;code&gt;inputRef.current.focus();&lt;/code&gt;를 호출할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  useLayoutEffect&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이 함수는 모든 DOM 변경 후에 동기적으로 발생하고 DOM에서 레이아웃을 읽고 동기적으로 리렌더링하는 경우에 사용한다.&lt;/li&gt;
&lt;li&gt;useLayoutEffect의 내부에 예정된 갱신은 브라우저가 &lt;b&gt;화면을 그리기 이전 시점&lt;/b&gt;에 동기적으로 수행된다.&lt;/li&gt;
&lt;li&gt;useLayoutEffect는 componentDidMount나 componentDidUpdate와 동일한 단계를 실행하긴 하지만 먼저 useEffect를 사용하고 문제가 있으면 useLayoutEffect을 사용해본다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  useDebugValue&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;useDebugValue는 React 개발자 도구에서 사용자 Hook 레이블을 표시하는 데에 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;  function useFriendStatus(friendID) {
    const [isOnline, setIsOnline] = useState(null);
    useDebugValue(isOnline ? 'Online' : 'Offline');

    return isOnline;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;  다른 Hooks&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;  참고 링크 : &lt;a href=&quot;https://ko.reactjs.org/docs/hooks-reference.html&quot;&gt;React-Hooks 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;다른 개발자가 만든 Hooks도 라이브러리로 설치하여 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;  참고 링크 : &lt;a href=&quot;https://nikgraf.github.io/react-hooks&quot;&gt;https://nikgraf.github.io/react-hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;  참고 링크 : &lt;a href=&quot;https://github.com/rehooks/awesome-react-hooks&quot;&gt;https://github.com/rehooks/awesome-react-hooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592911040715&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Hooks API Reference &amp;ndash; React&quot; data-og-description=&quot;A JavaScript library for building user interfaces&quot; data-og-host=&quot;ko.reactjs.org&quot; data-og-source-url=&quot;https://ko.reactjs.org/docs/hooks-reference.html&quot; data-og-url=&quot;https://ko.reactjs.org/docs/hooks-reference.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cOqhjt/hyGxbFrkLP/xv1VZtl3UJaJsxpryTgidk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/hooks-reference.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.reactjs.org/docs/hooks-reference.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cOqhjt/hyGxbFrkLP/xv1VZtl3UJaJsxpryTgidk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Hooks API Reference &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;A JavaScript library for building user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;ko.reactjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/6</guid>
      <comments>https://haranglog.tistory.com/6#entry6comment</comments>
      <pubDate>Tue, 23 Jun 2020 20:17:57 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript ES6/ ecmascript 2015] 문법 정리</title>
      <link>https://haranglog.tistory.com/5</link>
      <description>&lt;h1&gt;✔ ES2015+&lt;/h1&gt;
&lt;h2&gt;✒ const, let&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;const와 let은 블록 스코프이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;if(true){
  var x = 3;
}
console.log(x); // 3
if(true){
  const y = 3;
}
console.log(y) // Uncaught ReferenceError : y is not defined
if(true){
  let z = 3;
}
console.log(z); // Uncaught ReferenceError: z is not defined
for(var i=0; i&amp;lt;5; i++){
  // do something
}
console.log(i); // 5 &lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;var&lt;/code&gt;은 &lt;b&gt;함수 스코프&lt;/b&gt;를 가지므로 &lt;code&gt;if&lt;/code&gt;문의 블록과 관계없이 접근할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;const&lt;/code&gt;와 &lt;code&gt;let&lt;/code&gt;은 블록 스코프를 가지고 있어서 블록 밖에서는 변수에 접근할 수 없다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;블록 범위는 &lt;code&gt;if, while, for, function 등이&lt;/code&gt; 중괄호에 해당한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;블록 스코프를 사용함으로써 &lt;a href=&quot;https://yuddomack.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85Hoisting#recentComments&quot;&gt;호이스팅&lt;/a&gt; 같은 문제가 해결되고 코드의 관리가 수월해졌다는 장점이 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;const&lt;/code&gt;와 &lt;code&gt;let&lt;/code&gt;의 차이점은 &lt;code&gt;const&lt;/code&gt;는 한 번 대입하면 다른 값을 대입할 수 없다. 또 한, 초기화 시 값을 대입하지 않으면 에러가 발생한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592549193961&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;자바스크립트 호이스팅(Hoisting)&quot; data-og-description=&quot;(안내) 호이스팅의 원리에 대한 글을 추가로 포스팅했습니다. (19.10.13) 자바스크립트의 변수는 다른 언어들과 조금 다르게 동작합니다. 이는 때때로 개발자로 하여금 의도하지 않은 결과를 발생�&quot; data-og-host=&quot;yuddomack.tistory.com&quot; data-og-source-url=&quot;https://yuddomack.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85Hoisting#recentComments&quot; data-og-url=&quot;https://yuddomack.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85Hoisting&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tCa5j/hyGsHlbLqf/qfMDROXe3qnzg7birmf930/img.jpg?width=800&amp;amp;height=256&amp;amp;face=0_0_800_256,https://scrap.kakaocdn.net/dn/nUo0A/hyGtPhBPUK/iZMpBAf8ZkydxGpHMNHuT1/img.jpg?width=800&amp;amp;height=256&amp;amp;face=0_0_800_256,https://scrap.kakaocdn.net/dn/gP2Uu/hyGszHtSgW/xkMadtUUnq7PFMFRnQYv00/img.jpg?width=960&amp;amp;height=640&amp;amp;face=0_0_960_640&quot;&gt;&lt;a href=&quot;https://yuddomack.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85Hoisting#recentComments&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yuddomack.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85Hoisting#recentComments&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tCa5j/hyGsHlbLqf/qfMDROXe3qnzg7birmf930/img.jpg?width=800&amp;amp;height=256&amp;amp;face=0_0_800_256,https://scrap.kakaocdn.net/dn/nUo0A/hyGtPhBPUK/iZMpBAf8ZkydxGpHMNHuT1/img.jpg?width=800&amp;amp;height=256&amp;amp;face=0_0_800_256,https://scrap.kakaocdn.net/dn/gP2Uu/hyGszHtSgW/xkMadtUUnq7PFMFRnQYv00/img.jpg?width=960&amp;amp;height=640&amp;amp;face=0_0_960_640');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;자바스크립트 호이스팅(Hoisting)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;(안내) 호이스팅의 원리에 대한 글을 추가로 포스팅했습니다. (19.10.13) 자바스크립트의 변수는 다른 언어들과 조금 다르게 동작합니다. 이는 때때로 개발자로 하여금 의도하지 않은 결과를 발생�&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;yuddomack.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2&gt;✒ 템플릿 문자열 (Template Strings)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;큰따옴표나 작은따옴표로 감싸는 기존 문자열과는 다르게 백틱(`)으로 감싼다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이것은 문자열 안에 변수를 넣을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const num1 = 1;
const num2 = 2;
const result = 3;
const sum = `${num1} 더하기 ${num2}는 ${result}`;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✒ 객체 리터럴&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;IdCheck 같은 객체의 메서드에 함수를 연결할 때 콜론과 &lt;code&gt;function&lt;/code&gt;을 붙이지 않아도 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;memName : memName 처럼 속성명과 변수명이 겹치는 경우에는 한 번만 쓸 수 있게 되어있다. (코드의 중복을 피할 수 있다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예전 문법에서는 ES6라는 속성명을 만들려면 객체 리터럴 바깥에서 [es + 6]를 해야 했었지만, 객체 리터럴 안에서 사용 가능하다.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;const myObject = {
  IdCheck(){
      console.log('check!');
  },
  memName,
  [es+6] : 'wow!',
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✒ Arrow Functions(화살표 함수)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;return 문을 줄일 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;아래의 세 함수는 같은 함수이다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;  function add1(x,y){
    return x+y;
  }
  const add2 = (x,y) =&amp;gt; {
  return x + y;
  }
  const add3 = (x,y) =&amp;gt; x + y;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;  &lt;code&gt;this&lt;/code&gt; 바인드 방식&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;일반 함수의 자신을 호출하는 객체를 가리키는 &lt;code&gt;dynamic this&lt;/code&gt;였다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;  var relationship1 = {
    name : 'seungmin',
    friends : ['min', 'seung', 'sa'],
    logFriends: function(){
        var that = this; // relationship1을 가리키는 this를 that에 저장
        this.friends.forEach(function(friend){
            console.log(that.name, friend);
        })
    }
  }
  relationship1.logFriends();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Arrow Functions는 코드의 &lt;b&gt;상위 스코프&lt;/b&gt;를 가리키는 lexical this를 갖는다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;  const relationship2 = {
    name : 'seungmin',
    friends : ['min', 'seung', 'sa'],
    logFriends(){
        this.friends.forEach(friend =&amp;gt; {
            console.log(this.name, friend);
        })
    }
  }
  relationship2.logFriends();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;this를 그대로 사용할 수 있다. (상위 스코프의 this를 그래도 물려받는다.)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✒ 비구조화 할당&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;객체 안의 속성을 찾아서 변수와 매칭 해준다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;const ob = {
  status : {
      name : 'seung',
      count : 5,
  },
  getOb(){
      this.status.count--;
      return this.status.count;
  }
};
const {getOb, status: {count}} = ob;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✒ Modules&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Export, Import를 이용해 function이나 variables 들을 다른 곳에서 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;중괄호({})와 중괄호가 없는 것의 차이는 default로 설정되어있느냐 차이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export default ColorBox;
export const ColorProvider = ({children}) =&amp;gt; {
// ...
}
import ColorBox from './components/ColorBox';
import {ColorProvider} from './contexts/color';&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;  &lt;a href=&quot;https://medium.com/@chullino/require-exports-module-exports-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-1d024ec5aca3&quot;&gt;참고 문서&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592549200225&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;require(), exports, module.exports 공식문서로 이해하기&quot; data-og-description=&quot;.&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@chullino/require-exports-module-exports-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-1d024ec5aca3&quot; data-og-url=&quot;https://medium.com/@chullino/require-exports-module-exports-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-1d024ec5aca3&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://medium.com/@chullino/require-exports-module-exports-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-1d024ec5aca3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@chullino/require-exports-module-exports-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-1d024ec5aca3&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;require(), exports, module.exports 공식문서로 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2&gt;✒ 프로미스(Promise)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;비동기 프로그래밍에서도 특히 이벤트 주도 방식 때문에 콜백 함수를 자주 사용하는데 콜백 대신 프로미스 기반으로 재구성된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CallBack Hell에서 벗어나 가독성이 좋으며 중첩된 콜백의 단점을 완화시킨다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;  const condition = true;
  const promise = new Promise((resolve, reject) =&amp;gt; {
    if(condition){
        resolve('성공');
    }else{
        reject('실패');
    }
  });

  promise
  .then((message) =&amp;gt; {
  console.log(message); // 성공한 경우(resolve)
  })
  .catch((error) =&amp;gt; {
  console.log(error); // 실패한 경우(reject)
  })
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;프로미스 내부에서 &lt;code&gt;resolve&lt;/code&gt;가 호출되면 &lt;code&gt;then&lt;/code&gt;이 실행되고, &lt;code&gt;reject&lt;/code&gt;가 호출되면 &lt;code&gt;catch&lt;/code&gt;가 실행된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;then&lt;/code&gt;이나 &lt;code&gt;catch&lt;/code&gt;에서 다시 다른 &lt;code&gt;then&lt;/code&gt;이나 &lt;code&gt;catch&lt;/code&gt;를 붙일 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이전 &lt;code&gt;then&lt;/code&gt;의 &lt;code&gt;return&lt;/code&gt;값을 다음 &lt;code&gt;then&lt;/code&gt; 매개변수로 넘긴다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;프로미스를 &lt;code&gt;return 한&lt;/code&gt; 경우 프로미스가 수행된 후 다음 &lt;code&gt;then&lt;/code&gt;이나 &lt;code&gt;catch&lt;/code&gt;가 호출된다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;promise
  .then((message) =&amp;gt; {
      return new Promise((resolve, reject) =&amp;gt; {
          resolve(message);
      });
  })
  .then((message2) =&amp;gt; {
      return new Promise((resolve, reject) =&amp;gt; {
          resolve(message2);
      });
  })
  .then((message3) =&amp;gt; {
      console.log(message3);
  })
  .catch((error) =&amp;gt; {
      console.error(error);
  })&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;모든 콜백 함수를 바꿀 수 있지는 않다. 메서드가 프로미스 방식을 지원해야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all&quot;&gt;Promise.all&lt;/a&gt;은 여러 프로미스의 결과를 집계할 때 유용하게 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Promise.resolve&lt;/code&gt;는 즉시 &lt;code&gt;resolve 하는&lt;/code&gt; 프로미스를 만든다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Promise.reject&lt;/code&gt;는 &lt;code&gt;reject 하는&lt;/code&gt; 프로미스를 만든다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592549205694&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Promise.all()&quot; data-og-description=&quot;Promise.all() 메서드는 순회 가능한 객체에 주어진 모든 프로미스가 이행한 후, 혹은 프로미스가 주어지지 않았을 때 이행하는 Promise를 반환합니다. 주어진 프로미스 중 하나가 거부하는 경우, 첫 �&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c0zWxu/hyGsx3ZNa7/vUN2VWnoQUxB7Gzgrxc6Fk/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/TBJ6U/hyGtPaTSFy/H8uRLkrooasGvLgJ9BT5KK/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c0zWxu/hyGsx3ZNa7/vUN2VWnoQUxB7Gzgrxc6Fk/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/TBJ6U/hyGtPaTSFy/H8uRLkrooasGvLgJ9BT5KK/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Promise.all()&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Promise.all() 메서드는 순회 가능한 객체에 주어진 모든 프로미스가 이행한 후, 혹은 프로미스가 주어지지 않았을 때 이행하는 Promise를 반환합니다. 주어진 프로미스 중 하나가 거부하는 경우, 첫 �&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2&gt;✒ async/await&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ES2017&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;async/await문법은 프로미스를 사용한 코드를 한 번 더 깔끔하게 해 준다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;  promise 문법&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function findAndSaveUser(Users) {
Users.findOne({})
    .then((user) =&amp;gt; {
        user.name = 'myname';
        return user.save();
    })
    .then((user) =&amp;gt; {
        return Users.findOne({gender : 'M'});
    })
    .then((user) =&amp;gt; {
        //생략
    })
    .catch(err =&amp;gt; {
        console.error(err);
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;  async/await 문법&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const findAndSaveUser = async (User) =&amp;gt; {
  try{
      let user = await Users.findOne({});
      user.name = 'myname';
      user = await user.save();
      user = await Users.findOne({gender : 'M'});
      // 생략
  }catch(error){
      console.error(err);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;await가 붙은 함수는 해당 프로미스가 resolve 될 때까지 기다린 뒤 다음 로직으로 넘어간다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;resolve될 때까지 기다린 뒤, 변수를 초기화한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;에러 처리는 try/catch 문 로직으로 감싸준다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ES2018부터 for 문과 async/await을 같이 써서 Promise.all을 대체할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const pomise1 =Promise.resolve('성공1');
const pomise2 =Promise.resolve('성공2');
(async() =&amp;gt; {
  for await (promise of [pomise1, pomise2]){
      console.log(promise);
  }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;  참고 문서&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;  JavaScript 참고자료 : &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;  ES2015 설명 :&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;  ES 상세 후보군 : &lt;a href=&quot;https://github.com/tc39/proposals&quot;&gt;https://github.com/tc39/proposals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;  ES2015+ 브라우저/서버 호환 여부 : &lt;a href=&quot;http://kangax.github.io/compat-table/es6/&quot;&gt;http://kangax.github.io/compat-table/es6/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;  ES2015+ 참고 블로그 : &lt;a href=&quot;https://www.zerocho.com/category/EcmaScript&quot;&gt;https://www.zerocho.com/category/EcmaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;  ES6 문법 정리 : &lt;a href=&quot;https://jsdev.kr/t/es6/2944&quot;&gt;https://jsdev.kr/t/es6/2944&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;  ES6 기능 확인 : &lt;a href=&quot;http://es6-features.org/&quot;&gt;http://es6-features.org/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>JavaScript</category>
      <category>es2015</category>
      <category>ES6</category>
      <category>JavaScript</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/5</guid>
      <comments>https://haranglog.tistory.com/5#entry5comment</comments>
      <pubDate>Fri, 19 Jun 2020 15:51:45 +0900</pubDate>
    </item>
    <item>
      <title>JAVA 직렬화(Serializable)란?</title>
      <link>https://haranglog.tistory.com/4</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;우리가 SPRING 개발을 하다 보면 클래스에 implements Serializable 되어있는 VO(Value Object)가 많이 보았을 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그냥 남들이 쓰니까 써야 한다고 생각하고 쓰는 개발자도 있을 것이고 나 또한 그랬는데 Serializable를 왜 쓰느냐라는 얘기를 듣고 대답할 수 없어서 직렬화에 대해서 공부를 했다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✔ 직렬화&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;자바 시스템 내부에서 사용되는 Object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 바이트 형태로 데이터를 변환하는 기술로써, JVM의 메모리에 상주되어 있는 객체 데이터를 바이트 형태로 변환하는 기술이다.&lt;/li&gt;
&lt;li&gt;다차원의 자료를 파일로 저장하거나 네트워크로 보내기에 알맞게 일차원으로 펼치고 다시 원래대로 되돌리는 것을 직렬화(serialization)이라고 부른다.&lt;/li&gt;
&lt;li&gt;역직렬화는 직렬화의 반대를 의미한다.(바이트 =&amp;gt; data, object)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;  직렬화의 종류&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;직렬화의 종류에는 직렬화되는 자료가 어떤 종류인지를 나타내는 데이터 모델이 있고, 이 데이터 모델이 언제 결정되느냐에 따라서 두 종류로 나눌 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;◼ schematic&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;데이터 모델이 고정되어 있어서 나중에도 별로 바뀌지 않는다는 걸 알고 있고, 직렬화 포맷을 데이터 모델마다 생성해서 쓸 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;schematic&lt;/b&gt;에는 프로토콜 버퍼나 Cap'n Proto가 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;schematic &lt;/b&gt;한&amp;nbsp;직렬화 방법은 성능을 우선하느냐와 데이터 크기를 우선하느냐에 따라 나눌 수 있는데 어느 쪽이든 &lt;b&gt;schemaless &lt;/b&gt;한 직렬화보다는 효율적이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;◼ schemaless&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;대부분의 데이터 모델을 포함하는 범용 데이터 모델을 쓰는 접근이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;재귀가 없고 순서가 있는 순열 및 순서가 없는 집합만 가지고 있는 것들로 JSON이나 MessagePack 같은 것들에 해당한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;XML이 DTD나 XML Schema 같은 것과 결합해서 스키마가 존재하게 되면 schematic 하고 그렇지 않으면 schemaless 하다고 할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  더 자세한 내용은 &lt;a href=&quot;https://j.mearie.org/post/122845365013/serialization#notes&quot;&gt;https://j.mearie.org/post/122845365013/serialization&lt;/a&gt;&amp;nbsp;참조&lt;/p&gt;
&lt;figure id=&quot;og_1592551154384&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;직렬화&quot; data-og-description=&quot;며칠 전 해커뉴스에서 &amp;ldquo;인터넷은 디버깅 모드로 돌아가고 있다&amp;quot;는 글을 보았다. 아, 원래 글을 읽을 필요는 없다. 글의 내용이라는 게 JSON이나 XML은 텍스트 포맷인데 바이너리 포맷보다 오버헤�&quot; data-og-host=&quot;j.mearie.org&quot; data-og-source-url=&quot;https://j.mearie.org/post/122845365013/serialization#notes&quot; data-og-url=&quot;https://j.mearie.org/post/122845365013/serialization&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cXdUZH/hyGsIdoCNZ/3cHHKkeNnoTNpg5jzBL8uK/img.jpg?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://j.mearie.org/post/122845365013/serialization#notes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://j.mearie.org/post/122845365013/serialization#notes&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cXdUZH/hyGsIdoCNZ/3cHHKkeNnoTNpg5jzBL8uK/img.jpg?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;직렬화&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;며칠 전 해커뉴스에서 &amp;ldquo;인터넷은 디버깅 모드로 돌아가고 있다&quot;는 글을 보았다. 아, 원래 글을 읽을 필요는 없다. 글의 내용이라는 게 JSON이나 XML은 텍스트 포맷인데 바이너리 포맷보다 오버헤�&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;j.mearie.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr /&gt;
&lt;h2&gt;✔ 자바에서의 직렬화(Serializable)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;자바 직렬화는 개발에 최적화되어 있기 때문에 복잡한 데이터 구조의 클래스의 객체라도 직렬화 기본 조건만 지키면 큰 작업 없이 바로 직렬화를 사용할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;자바에서 직렬화는 JVM 메모리에서만 상주되어있는 객체 데이터를 그대로 영속화가 필요할 때 사용된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이러한 장점 때문에 네트워크 전송도 가능하고 필요할 때 직렬화된 객체 데이터를 가져와서 역직렬 화하여 객체를 바로 사용할 수 있게 해 준다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;◼ 서블릿 세션(Servlet Session)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;서블릿 기반의 WAS(톰캣, 웹로직 등) 대부분 세션의 자바 직렬화를 지원한다.&lt;/li&gt;
&lt;li&gt;파일로 저장하거나 세션 클러스터링, DB에 저장하는 옵션 등을 선택하게 된다면 세션 자체가 직렬화 되어 저장되어 전달된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;◼ 캐시(Cache)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;캐시 할 부분을 자바 직렬화된 데이터를 저장해서 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;◼ &lt;a href=&quot;https://docs.oracle.com/javase/tutorial/rmi/index.html&quot;&gt;RMI(Remote Method Invocation)&lt;/a&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1592370808032&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Trail: RMI (The Java&amp;trade; Tutorials)&quot; data-og-description=&quot;The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See JDK Release Notes for information about new fe&quot; data-og-host=&quot;docs.oracle.com&quot; data-og-source-url=&quot;https://docs.oracle.com/javase/tutorial/rmi/index.html&quot; data-og-url=&quot;https://docs.oracle.com/javase/tutorial/rmi/index.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/tutorial/rmi/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.oracle.com/javase/tutorial/rmi/index.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Trail: RMI (The Java&amp;trade; Tutorials)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See JDK Release Notes for information about new fe&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;docs.oracle.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;RMI는 원결 시스템 간의 메시지 교환을 위해서 사용하는 자바에서 지원하는 기술이다.&lt;/li&gt;
&lt;li&gt;원격의 시스템의 메서드를 호출 시에 전달하는 메시지(객체)를 자동으로 직렬화 시켜 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  자바의 Serializable&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;자바에서 java.io.Serializable 인터페이스를 보면 구현해야 하는 메서드가 없다.&lt;/li&gt;
&lt;li&gt;그 이유는 Serializable 인터페이스를 구현한 구현체가 직렬화 대상이다라는 것을 JVM에게 알려주는 역할만 하기 때문이다.&lt;/li&gt;
&lt;li&gt;때문에 이 상황에서 불필요한 직렬화는 오버헤드의 원인이 되기도 하지만, Serializable은 직렬화 작업을 하는 것이 아니라 단지 해당 클래스의 인스턴스는 직렬화할 수 있다는 것을 선언하는 것 뿐이기 때문에 문제가 되지 않는다. (참고로, 자바의 ArrayList나 HashMap 등 기본 컬렉션 구현체가 모두 Serializable을 구현하고 있는 것도 비슷하다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;    public class MemberVo implements Serializable{

        private static final long serialVersionUID = 2868210232929931052L;
        private String memName;
        private String memPass;
        private String memPhone;
        private int memAge;
        ...
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;◼ serialVersionUID가 필요한 이유&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;직렬화 과정에서 serialVersionUID의 버전이 포함되게 되고, 역직렬화 과정에서 Java class에 선언되어 있는 serialVersionUID의 버전과 서로 동일한지를 체크를 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;✨ &lt;a href=&quot;https://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100&quot;&gt;자바 직렬화 스펙&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592369772417&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Java Object Serialization Specification: 4 - Class Descriptors&quot; data-og-description=&quot;The serialized form of an ObjectStreamClass instance depends on whether or not the Class object it represents is serializable, externalizable, or a dynamic proxy class. When an ObjectStreamClass instance that does not represent a dynamic proxy class is wri&quot; data-og-host=&quot;docs.oracle.com&quot; data-og-source-url=&quot;https://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100&quot; data-og-url=&quot;https://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Java Object Serialization Specification: 4 - Class Descriptors&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;The serialized form of an ObjectStreamClass instance depends on whether or not the Class object it represents is serializable, externalizable, or a dynamic proxy class. When an ObjectStreamClass instance that does not represent a dynamic proxy class is wri&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;docs.oracle.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. serialVersionUID는 기본적으로 필수 값은 아니다.&lt;br /&gt;2. 호환 가능한 클래스는 serialVersionUID값이 고정되어 있다.&lt;br /&gt;3. serialVersionUID가 선언되어 있지 않으면 클래스의 기본 해시값(SHA-1)을 사용한다. (해시 알고리즘은 참고 링크에)&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;◾ Serializable클래스가 serialVersionUID를 명시적으로 선언하지 않으면 내부적으로 serialVersionUID 정보가 추가된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;◾ 그러나 기본 serialVersionUID 계산은 컴파일러 구현에 따라 달라질 수 있는 클래스 세부 사항에 매우 민감하므로 예기치 않은 결과를 초래할 수 있으므로 모든 &lt;b&gt;Serializable 클래스는 serialVersionUID 값을 명시적으로 선언하는 것이 좋다.&lt;/b&gt; (&lt;a href=&quot;https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html&quot;&gt;참고&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;◾ 잠재적으로 네트워크를 넘나들거나 파일로 저장할 가능성이 조금이라도 있는 클래스는 기본적으로 Serializable을 구현해서 의도를 명확하게 밝히는 것이 좋은 개발 습관이다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592369963960&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Serializable (Java Platform SE 7 )&quot; data-og-description=&quot;Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselve&quot; data-og-host=&quot;docs.oracle.com&quot; data-og-source-url=&quot;https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html&quot; data-og-url=&quot;https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Serializable (Java Platform SE 7 )&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselve&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;docs.oracle.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4&gt;◼ serialVersionUID 버전 체크를 하는 이유&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;◾ 버전이 바뀌면 객체의 상태가 조금이라도 바뀌었다는 것을 의미하고 결국 역직렬화 과정에서 오류가 발생한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;◾ &lt;b&gt;때문에 클래스를 변경할 때에 직접 serialVersionUID 값을 관리해주어야 클래스 변경 시 혼란을&amp;nbsp;줄일 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;◾ serialVersionUID 값이 동일할 때 문제가 발생하는 경우 (굉장히 엄격하다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;멤버 변수명은 같은데 멤버 변수 타입이 바뀔 때&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;  public class MemberVo implements Serializable{

      private static final long serialVersionUID = 2868210232929931052L;
      private String memName;
      private String memPass;
      // StringBuilder로 변경
      private StringBuilder memPhone;
      private int memAge;
      ...
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;원시 타입으로 변경&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;  public class MemberVo implements Serializable{

      private static final long serialVersionUID = 2868210232929931052L;
      private String memName;
      private String memPass;
      private String memPhone;
      // int =&amp;gt; long
      private long memAge;
      ...
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;◾ serialVersionUID 값이 동일하면 멤버 변수 및 메서드 추가는 크게 문제 되지 않는다. 그리고 멤버 변수 제거 및 이름 변경은 오류는 발생하지 않지만 데이터는 누락된다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;  public class MemberVo implements Serializable{

      private static final long serialVersionUID = 2868210232929931052L;
      private String memName;
      private String memPass;
      private String memPhone;
      // 추가
      private String memBirth;
      // 제거
      //private int memAge;
      ...
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만, 위 경우에도 구버전에서 신버전 클래스를 읽는 경우에 문제를 일으킬 수 있다고 비호환적이라고 한다.&lt;br /&gt;(  참고 : &lt;a href=&quot;https://stackoverflow.com/questions/16261383/delete-field-from-old-java-class-implementing-serializable#answer-16261562&quot;&gt;https://stackoverflow.com/questions/16261383/delete-field-from-old-java-class-implementing-serializable#answer-16261562&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592369630507&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Delete field from old java class implementing Serializable&quot; data-og-description=&quot;Suppose i have version of class MyClass where i have two fields int count and String name. And i have persisted the byte stream to file. After i delete the attribute name from the class then also ...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/16261383/delete-field-from-old-java-class-implementing-serializable#answer-16261562&quot; data-og-url=&quot;https://stackoverflow.com/questions/16261383/delete-field-from-old-java-class-implementing-serializable&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Jodno/hyGrDvZUrS/svi6WHSsHXA6JykfCKqnCK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/16261383/delete-field-from-old-java-class-implementing-serializable#answer-16261562&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/16261383/delete-field-from-old-java-class-implementing-serializable#answer-16261562&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Jodno/hyGrDvZUrS/svi6WHSsHXA6JykfCKqnCK/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Delete field from old java class implementing Serializable&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Suppose i have version of class MyClass where i have two fields int count and String name. And i have persisted the byte stream to file. After i delete the attribute name from the class then also ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4&gt;◼ 호환성&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;자바 직렬화를 이용해서 외부 데이터를 저장하면 자바에서만 사용가능하다.&lt;/li&gt;
&lt;li&gt;긴 만료시간을 가진 데이터는 JSON등 다른 포맷을 사용하여 저장한다.&lt;/li&gt;
&lt;li&gt;  참고 : &lt;a href=&quot;https://docs.oracle.com/javase/8/docs/platform/serialization/spec/version.html&quot;&gt;https://docs.oracle.com/javase/8/docs/platform/serialization/spec/version.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592369655834&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Java Object Serialization Specification: 5 - Versioning of
Serializable Objects&quot; data-og-description=&quot;CONTENTS | PREV | NEXT Java Object Serialization Specification version 6.0 Versioning of Serializable Objects &amp;lt;!-- --&amp;gt; Topics: When Java objects use serialization to save state in files, or as blobs in databases, the potential arises that the version of a &quot; data-og-host=&quot;docs.oracle.com&quot; data-og-source-url=&quot;https://docs.oracle.com/javase/8/docs/platform/serialization/spec/version.html&quot; data-og-url=&quot;https://docs.oracle.com/javase/8/docs/platform/serialization/spec/version.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/platform/serialization/spec/version.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.oracle.com/javase/8/docs/platform/serialization/spec/version.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Java Object Serialization Specification: 5 - Versioning of Serializable Objects&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;CONTENTS | PREV | NEXT Java Object Serialization Specification version 6.0 Versioning of Serializable Objects &lt;!-- --&gt; Topics: When Java objects use serialization to save state in files, or as blobs in databases, the potential arises that the version of a&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;docs.oracle.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>JAVA&amp;amp;SPRING</category>
      <category>Java</category>
      <category>serializable</category>
      <category>Spring</category>
      <category>직렬화</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/4</guid>
      <comments>https://haranglog.tistory.com/4#entry4comment</comments>
      <pubDate>Wed, 17 Jun 2020 14:07:48 +0900</pubDate>
    </item>
    <item>
      <title>JAVA Spring의 @SuppressWarnings 어노테이션</title>
      <link>https://haranglog.tistory.com/3</link>
      <description>&lt;h2&gt;✔ @SuppressWarnings 이란?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;@SuppressWarnings&lt;/b&gt; 어노테이션은 컴파일 경고를 사용하지 않도록 설정해주는 것으로 한마디로 이클립스에서 노란색 표시줄이 나타내는 것 즉, 경고를 제외시킬 때 사용한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;또한, 두 가지의 종류를 같이 사용할 때는 &lt;code&gt;@SuppressWarnings({&quot;unused&quot;, &quot;unchecked&quot;})처럼&lt;/code&gt; 객체 형식으로 묶어서 사용한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  &lt;b&gt;@SuppressWarnings(&quot;unused&quot;)&lt;/b&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;사용하지 않는 코드 또는 불필요한 코드가 있을 경우 경고를 억제시켜준다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;    @SuppressWarnings(&quot;unused&quot;)
    private final Logger logger = LoggerFactory.getLogger(getClass());&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  &lt;b&gt;@SuppressWarnings(&quot;serial&quot;)&lt;/b&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;@SuppressWarnings(&quot;serial&quot;)&lt;/code&gt;은 Spring에서 개발하다 보면 VO(Value Object)에 implements Serializable과 같은 인터페이스를 구현한 것을 본적이 자주 있을 것이다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;  public class Member implements Serializable{

      private static final long serialVersionUID = 1L;
      private String memberId;
      private String memberPass;
      private String memberEmail;
      ...
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;위 상황에서 serialVersionUID를 누락시키면 노란색으로 경고가 나타나는데 이를 억제해주기 위해서 &lt;code&gt;@SuppressWarnings(&quot;serial&quot;)&lt;/code&gt;를 사용한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만, &lt;code&gt;@SuppressWarnings(&quot;serial&quot;)&lt;/code&gt;을 사용하는 것은 권장하고 있지 않기 때문에 serialVersionUID를 명시적으로 선언하여 처리하는 것이 좋다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;  &lt;a href=&quot;https://haranglog.tistory.com/entry/JAVA-%EC%A7%81%EB%A0%AC%ED%99%94Serializable%EB%9E%80&quot;&gt;직렬화(serializable)에 대한 자세한 설명&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592377098798&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;JAVA 직렬화(Serializable)란?&quot; data-og-description=&quot;우리가 SPRING 개발을 하다 보면 클래스에 implements Serializable 되어있는 VO(Value Object)가 많이 보았을 것이다. 그냥 남들이 쓰니까 써야 한다고 생각하고 쓰는 개발자도 있을 것이고 나 또한 그랬는데&quot; data-og-host=&quot;haranglog.tistory.com&quot; data-og-source-url=&quot;https://haranglog.tistory.com/entry/JAVA-%EC%A7%81%EB%A0%AC%ED%99%94Serializable%EB%9E%80&quot; data-og-url=&quot;https://haranglog.tistory.com/entry/JAVA-%EC%A7%81%EB%A0%AC%ED%99%94Serializable%EB%9E%80&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/7NMvc/hyGrw4QtKz/f9RTttbKCV5XM67q0ZrW4k/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/nU9UU/hyGrFHvF3H/v75yAki2Mf3iZMsJfkrrTK/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500&quot;&gt;&lt;a href=&quot;https://haranglog.tistory.com/entry/JAVA-%EC%A7%81%EB%A0%AC%ED%99%94Serializable%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://haranglog.tistory.com/entry/JAVA-%EC%A7%81%EB%A0%AC%ED%99%94Serializable%EB%9E%80&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/7NMvc/hyGrw4QtKz/f9RTttbKCV5XM67q0ZrW4k/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/nU9UU/hyGrFHvF3H/v75yAki2Mf3iZMsJfkrrTK/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;JAVA 직렬화(Serializable)란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;우리가 SPRING 개발을 하다 보면 클래스에 implements Serializable 되어있는 VO(Value Object)가 많이 보았을 것이다. 그냥 남들이 쓰니까 써야 한다고 생각하고 쓰는 개발자도 있을 것이고 나 또한 그랬는데&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;haranglog.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4&gt;  &lt;b&gt;@SuppressWarnings(&quot;all&quot;)&lt;/b&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@SuppressWarnings(&quot;all&quot;)&lt;/code&gt;은 모든 경고를 억제시켜준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  &lt;b&gt;@SuppressWarnings(&quot;deprecation&quot;)&lt;/b&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@SuppressWarnings(&quot;deprecation&quot;)&lt;/code&gt; 권장되지 않는 기능과 관련된 경고를 억제한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  &lt;b&gt;@SuppressWarnings(&quot;null&quot;)&lt;/b&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@SuppressWarnings(&quot;null&quot;)&lt;/code&gt;은 널(null) 분석과 관련된 경고를 억제한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;  &lt;b&gt;@SuppressWarnings(&quot;unchecked&quot;)&lt;/b&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;@SuppressWarnings(&quot;unchecked&quot;)&lt;/code&gt;은 미확인 오퍼레이션과 관련된 경고를 억제한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;  [IBM Knowledge Center에 SuppressWarnings 어노테이션 내부에서 사용할 수 있는 토큰 목록이 있다.]&lt;br /&gt;(&lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/ko/SSRTLW_9.5.0/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.htm&quot;&gt;https://www.ibm.com/support/knowledgecenter/ko/SSRTLW_9.5.0/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.htm&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1592377100334&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;IBM Knowledge Center&quot; data-og-description=&quot;Please note that DISQUS operates this forum. When you sign in to comment, IBM will provide your email, first name and last name to DISQUS. That information, along with your comments, will be governed by DISQUS&amp;rsquo; privacy policy. By commenting, you are acce&quot; data-og-host=&quot;www.ibm.com&quot; data-og-source-url=&quot;https://www.ibm.com/support/knowledgecenter/ko/SSRTLW_9.5.0/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.htm&quot; data-og-url=&quot;https://www.ibm.com/support/knowledgecenter/ko/SSRTLW_9.5.0/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.htm&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.ibm.com/support/knowledgecenter/ko/SSRTLW_9.5.0/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.ibm.com/support/knowledgecenter/ko/SSRTLW_9.5.0/org.eclipse.jdt.doc.user/tasks/task-suppress_warnings.htm&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;IBM Knowledge Center&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Please note that DISQUS operates this forum. When you sign in to comment, IBM will provide your email, first name and last name to DISQUS. That information, along with your comments, will be governed by DISQUS&amp;rsquo; privacy policy. By commenting, you are acce&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.ibm.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JAVA&amp;amp;SPRING</category>
      <category>Java</category>
      <category>Spring</category>
      <category>SuppressWarnings</category>
      <category>어노테이션</category>
      <author>seungmin2</author>
      <guid isPermaLink="true">https://haranglog.tistory.com/3</guid>
      <comments>https://haranglog.tistory.com/3#entry3comment</comments>
      <pubDate>Tue, 16 Jun 2020 18:16:58 +0900</pubDate>
    </item>
  </channel>
</rss>