<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>빠리의 택시 운전사</title>
    <link>https://geonlee.tistory.com/</link>
    <description>나는 빠리의 택시 운전사 어쩌구 저쩌구 어쩌구 저쩌구</description>
    <language>ko</language>
    <pubDate>Wed, 11 Mar 2026 22:44:27 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>택시 운전사</managingEditor>
    <image>
      <title>빠리의 택시 운전사</title>
      <url>https://tistory1.daumcdn.net/tistory/1840821/attach/692980f4c4ba4594a45eade842c9bfa8</url>
      <link>https://geonlee.tistory.com</link>
    </image>
    <item>
      <title>  2025  </title>
      <link>https://geonlee.tistory.com/265</link>
      <description>&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;h2 data-ke-size=&quot;size26&quot;&gt;블로그&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해 블로그는 하나의 글도 쓰지 않았다. 그리고 작년에 계획했던 개인 블로그로의 이전도 하지 않았다.&lt;br /&gt;요즘 워낙 AI 성능이 좋아서 개인 블로그 사이트야 하루면 멋지게 나오겠다는 생각에 연말쯤 되어서 실천을 하려했지만, 2017년 진로로 방황하던 시절 마음을 다잡게했던 이 곳을 박차고 벗어나기는 여간 쉽지 않아보인다. 이래놓고 만들수도 있다. 사람 일은 모르는거니까&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;일&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쇼핑에서 지도로 조직을 이동했다. 이 회사에서만 벌써 2번째 자의적 조직 이동이다. 조직 이동하는 과정에서 우여곡절도 있었고, 현재 조직에서도 초기에 우여곡절이 많았지만, 현재는 모든 것이 안정되어서 나름 행복한 직장생활을 하고 있다. 도메인도 앞에 겪었던 두 조직과 다른 도메인인데 O2O라는 도메인 특성이 반영되다 보니 새로운 것도 많고 재미있는 도메인이여서 더 재미있게 개발하고 있다. 그리고 조직에 새로 들어왔다보니 내 존재 가치를 증명하기 위해 예전보다 좀 더 노력중이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사이드프로젝트&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c32BJz/dJMcachrX36/rCoURvGefG9M9vEhestjk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c32BJz/dJMcachrX36/rCoURvGefG9M9vEhestjk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c32BJz/dJMcachrX36/rCoURvGefG9M9vEhestjk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc32BJz%2FdJMcachrX36%2FrCoURvGefG9M9vEhestjk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;513&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해, 회사에서의 어수선한 분위기에 머리를 식히고 집중할 곳이 필요해서, 일이 끝나면 언제나 사이드프로젝트를 잡았다. 그러다보니 정말 작업을 많이했는데, 찾아보니 작년은 458 commit 수준이였는데 올 해는 1600 commit이 넘는다. 물론 시간을 많이 투입한 것도 있지만, 올 해 사이드프로젝트로 AI 툴 PoC를 정말 많이 했다. Cursor를 정말 딥하게 써보고, Claude Code도 과금을 해서 몇 달간 써보기도 했다. 그러다보니 작업 속도도 빨라지고, 커밋 양도 자연스럽게 많아지게 된 것 같다.&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시간 유산소는 최소 30분으로 잡고 운동을 했고, 플랜핏 앱의 도움을 정말 많이 받았다. 플랜핏에서 짜준 3분할로 매번 운동을 진행했고, 무게도 늘고 몸의 변화도 있었다. 물론 연말이 다가오고 추워지면서 다시 헬스장을 못가고 많이 먹다보니 거의 돌아왔지만...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내년에는 다른 헬스장에서 운동을 할 예정이라 기대도 되면서 열심히하려한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;게임&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년에 PS5로 정말 많은 게임을 했는데, 올 해는 아내와 함께한 스플릿 픽션이 제일 재미있지 않았나 싶다. 각자 실세 성격과는 다른 캐릭터를 일부러 골랐는데, It takes two 때의 즐거움을 또 다시 느꼈다. 이번에는 스위치가 아닌 PS5로 했는데 확실히 그래픽에서 오는 즐거움도 있는 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KCNeG/dJMb99Szhcu/ebRZkkwaGPpWXDQnTrdwu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KCNeG/dJMb99Szhcu/ebRZkkwaGPpWXDQnTrdwu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KCNeG/dJMb99Szhcu/ebRZkkwaGPpWXDQnTrdwu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKCNeG%2FdJMb99Szhcu%2FebRZkkwaGPpWXDQnTrdwu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에 한 게임은 리틀나이트메어3, 킹 덤 컴 정도가 있고, 요새 롤체에 빠져버렸다.. 열심히 하다보니 플래까지 찍었는데 아마 여기가 한계가 아닐까 싶다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SmuTS/dJMcafyttsk/kWjB1bvCgklb5pO1I4bmE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SmuTS/dJMcafyttsk/kWjB1bvCgklb5pO1I4bmE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SmuTS/dJMcafyttsk/kWjB1bvCgklb5pO1I4bmE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSmuTS%2FdJMcafyttsk%2FkWjB1bvCgklb5pO1I4bmE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2556&quot; height=&quot;1179&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1179&quot;/&gt;&lt;/span&gt;&lt;/figure&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;올 해에는 여행을 한 번 갔는데, 작년에 간 발리가 너무 좋아서 이번에 또 가게되었다. 작년에는 우붓과 짐바란 쪽으로 갔는데 이번에는 짱구에서만 7박 8일을 보냈다. 오랫만에 하와이 온 것 같은 기분도 나고 음식도 너무 맛있었다.&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/bpWEVa/dJMcagRGshe/2kfyb0axvlpvwCkLrIqen0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpWEVa/dJMcagRGshe/2kfyb0axvlpvwCkLrIqen0/img.png&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;943&quot; data-is-animation=&quot;false&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/bpWEVa/dJMcagRGshe/2kfyb0axvlpvwCkLrIqen0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpWEVa%2FdJMcagRGshe%2F2kfyb0axvlpvwCkLrIqen0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;707&quot; height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpFU96/dJMcaioqtvG/wOYezt3rbxTXulafPUDVpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpFU96/dJMcaioqtvG/wOYezt3rbxTXulafPUDVpk/img.png&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;943&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpFU96/dJMcaioqtvG/wOYezt3rbxTXulafPUDVpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpFU96%2FdJMcaioqtvG%2FwOYezt3rbxTXulafPUDVpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;707&quot; height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;/div&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/YnTER/dJMcafL0UlH/4Prc4fvuzX6xSz9n4Kj071/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YnTER/dJMcafL0UlH/4Prc4fvuzX6xSz9n4Kj071/img.png&quot; data-origin-width=&quot;1257&quot; data-origin-height=&quot;943&quot; data-is-animation=&quot;false&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/YnTER/dJMcafL0UlH/4Prc4fvuzX6xSz9n4Kj071/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYnTER%2FdJMcafL0UlH%2F4Prc4fvuzX6xSz9n4Kj071%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1257&quot; height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDbqTI/dJMcaiBVekJ/RFqkMQgKOY77Nmx72OnfDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDbqTI/dJMcaiBVekJ/RFqkMQgKOY77Nmx72OnfDK/img.png&quot; data-origin-width=&quot;1257&quot; data-origin-height=&quot;943&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDbqTI/dJMcaiBVekJ/RFqkMQgKOY77Nmx72OnfDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDbqTI%2FdJMcaiBVekJ%2FRFqkMQgKOY77Nmx72OnfDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1257&quot; height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;/div&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/bKft04/dJMcahiLdf8/ycmqjRp782sWXZAUoUkqy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKft04/dJMcahiLdf8/ycmqjRp782sWXZAUoUkqy1/img.png&quot; data-origin-width=&quot;1257&quot; data-origin-height=&quot;943&quot; data-is-animation=&quot;false&quot; style=&quot;width: 63.2578%; margin-right: 10px;&quot; data-widthpercent=&quot;64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKft04/dJMcahiLdf8/ycmqjRp782sWXZAUoUkqy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKft04%2FdJMcahiLdf8%2FycmqjRp782sWXZAUoUkqy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1257&quot; height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eAqto0/dJMcafL0Uoe/DzMOHLLpEtyKlcWywWQFx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eAqto0/dJMcafL0Uoe/DzMOHLLpEtyKlcWywWQFx1/img.png&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;943&quot; data-is-animation=&quot;false&quot; style=&quot;width: 35.5794%;&quot; data-widthpercent=&quot;36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eAqto0/dJMcafL0Uoe/DzMOHLLpEtyKlcWywWQFx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeAqto0%2FdJMcafL0Uoe%2FDzMOHLLpEtyKlcWywWQFx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;707&quot; height=&quot;943&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;사업&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 아니고 아내가 사업을 시작했다. 나도 언젠가는 사업을 하고 싶다는 막연한 꿈만 꾸고 있지만, 아내는 직장을 박차고 실제 도전을 하고 있다. 아직 초기이다보니 쉽지는 않지만 점점 노하우가 쌓이고, 곧 멋진 사장님이 될거라 믿는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;26년을 바라보며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 한 해 돌아보니 많은 일들이 있었고, 돌이켜보니 올 해도 나쁘지 않은 한 해를 보낸 것 같다. 내년에도 더 좋은 사람이 되도록 노력하면서 살아야겠다.&lt;/p&gt;</description>
      <category>Devlog/회고록</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/265</guid>
      <comments>https://geonlee.tistory.com/265#entry265comment</comments>
      <pubDate>Wed, 31 Dec 2025 17:48:32 +0900</pubDate>
    </item>
    <item>
      <title>  2024  </title>
      <link>https://geonlee.tistory.com/264</link>
      <description>&lt;h1&gt;  들어가며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년도 이제 며칠 안 남았다. 작년처럼 이번에도 해가 바뀌기전에 시간을 내서 회고 글을 써보고자 한다.&lt;/p&gt;
&lt;h1&gt;블로그&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 블로그도 올 해 미안하게도 많이 방치가 되었다. 작성한 글은 1개 그나마도 단순한 팁 글이다. 후술하겠지만, 일이 바쁘고 결혼을 하고 아내와 지내는 시간들이 행복하다보니 혼자서 무언가를 하는 시간이 줄어들게 되고, 그마저도 혼자서했을 때 재미있는 것들이 많아지면서 블로그의 우선순위는 뒤로 계속 밀려나게 되었다. 이번에 회고를 작성하기 전에 방문자를 보니 거의 1/5 수준으로 줄어들고, 열심히 블로그를 가꾸던 열정은 많이 사라진 것 같아서 조금 아쉽기도했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 도메인을 사서 gatsby 같은 걸로 블로그나 만들어서 회고 글을 올릴까 하다가 빠르게 포기하고 티스토리에 다시 글을 적고 있다. 티스토리 좋긴하지만, 내가 사용하는 스킨 설정과의 충돌로 내 마음대로 커스터마이징이 안되는 부분들이 있어서 좀 더 자유로운 방식으로 언젠가 바꾸고 싶다는 욕심은 계속 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 생각은 몇 달 전, 회사분임을 알게 되었던 &lt;a href=&quot;https://bluewings.github.io/&quot;&gt;블로그&lt;/a&gt;를 다시 보면서 들었는데 글의 양이 많지 않아도, 남들이 보고 기억에 남을 수 있는 글들이 있는 블로그를 꾸려가고 싶었다. 물론 내년에도 똑같은 고민을 하고 있을 수 있다.&lt;/p&gt;
&lt;h1&gt;일&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년 네이버 쇼핑 MY 영역 개편을 담당하면서 들었던 생각이 회사에 들어오고나서 가장 바쁘고 힘들었다는 것이였는데, 올 해 그 생각이 바뀌었다. 올 해가 더 힘든 한 해였다. 회사에서 주어진 일 + 기존 저장소의 이관 작업까지 진행하면서, 실상 회사에서 팀에 기대한 작업 그 이상을 진행했다. 다만 이전과 같은 상황이라면 더 힘들었을 것이지만, 해당 프로젝트를 담당하는 동료들이 많아지면서, 동료들과 협업을 통해 문제없이 작업들을 마무리했다.&lt;/p&gt;
&lt;h1&gt;사이드프로젝트&lt;/h1&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-origin-width=&quot;770&quot; data-origin-height=&quot;201&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YBNmn/btsLAZVA0Tz/53pAEnoq0YckJGHm4I7Is1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YBNmn/btsLAZVA0Tz/53pAEnoq0YckJGHm4I7Is1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YBNmn/btsLAZVA0Tz/53pAEnoq0YckJGHm4I7Is1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYBNmn%2FbtsLAZVA0Tz%2F53pAEnoq0YckJGHm4I7Is1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;201&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;201&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;커밋수도 작년과 비슷한 수준이다. 기존 프로젝트들의 개선과 기능 추가등을 진행하고, 요즘 핫한 AI 기술을 이용한 cursor나 claude를 적극적으로 이용한 프로젝트도 진행해보았다.&lt;/p&gt;
&lt;h1&gt;운동&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해 초 PT도 진행하고, 나름 열심히 운동을 했다고 생각했는데 역시 아쉽게도 큰 변화는 없었다. 결혼 이후, 아내와 너무 행복해진 나머지 금요일마다 영화와 함께 먹는 야식과 술이 문제이긴 했다. 내년에는 나도 아내도 건강을 위해 열심히 운동할 예정이다. 몇 년전부터 꾸준히 해도던 러닝도 계속 하고 있다. 요즈음은 너무 추워서 힘들긴하지만...&lt;/p&gt;
&lt;h1&gt;게임&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해는 요 근래 모바일 게임만 하다가 콘솔에 빠지게 되었다. 아내와는 함께 스위치로 &quot;it takes two&quot;를 했고, 오래 알고지낸 선배께서 감사하게도 생일 선물로 PS5를 주신 뒤로는 2023년 GOTY를 싹쓸이한 &quot;발더스게이트3&quot;를 시작으로 &quot;검은신화 오공&quot;, &quot;레드데드리뎀션&quot;, &quot;갓오브워&quot;, &quot;갓오브워:라그나로크&quot;, &quot;스파이더맨2&quot;까지 플스5로 꼭 플레이해봐야하는 작품들은 거의 플레이한 것 같다. 오랫만에 맛본 콘솔은 영화와는 다른 느낌으로 몰입감을 만들어주었다. 대부분의 게임들이 재미있었지만, 가장 몰임감있고, 길게 했던 &quot;발더스게이트3&quot;가 단연코 제일 기억에 남는다.&lt;/p&gt;
&lt;h1&gt;여행&lt;/h1&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;4871D33E-347F-411B-AA34-26D226FF1D06_1_102_o.jpeg&quot; data-origin-width=&quot;3172&quot; data-origin-height=&quot;990&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oNT9S/btsLzua0vec/4Ox2enOScsY9o6GGCU4e1k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oNT9S/btsLzua0vec/4Ox2enOScsY9o6GGCU4e1k/img.jpg&quot; data-alt=&quot;바투르산 일출&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oNT9S/btsLzua0vec/4Ox2enOScsY9o6GGCU4e1k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoNT9S%2FbtsLzua0vec%2F4Ox2enOScsY9o6GGCU4e1k%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;3172&quot; height=&quot;990&quot; data-filename=&quot;4871D33E-347F-411B-AA34-26D226FF1D06_1_102_o.jpeg&quot; data-origin-width=&quot;3172&quot; data-origin-height=&quot;990&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;h1&gt;25년을 바라보며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 한 해 회사 일도 많았고, 아내와의 신혼 첫 해로 같이 보내는 재미있는 시간도 많이 보내고, 나라도 연말부터 뭔가 뒤숭숭하고 정신없는 한 해가 또 이렇게 가고 있다. 나쁘지않은 한 해를 보낸 것 같고, 내년에도 더 좋은 사람이 되도록 노력하면서 살아야겠다.&lt;/p&gt;</description>
      <category>Devlog/회고록</category>
      <category>회고</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/264</guid>
      <comments>https://geonlee.tistory.com/264#entry264comment</comments>
      <pubDate>Sun, 29 Dec 2024 21:24:19 +0900</pubDate>
    </item>
    <item>
      <title>nvm use 자동화</title>
      <link>https://geonlee.tistory.com/263</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;node.js 버전이 다른 프로젝트를 왔다갔다 하면서 매번 nvm use를 해서 노드 버전을 바꿔왔는데, 아래의 스크립트를 사용하면 .nvmrc가 있는 프로젝트라면 자동으로 nvm use를 해주고, 해당 버전이 없다면 nvm install까지 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.zsh를 사용하는 경우에 사용가능하고 주석에 나와있듯이 nvm 초기화 이후에 해당 코드를 넣으면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1705539265644&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# place this after nvm initialization!
autoload -U add-zsh-hook
load-nvmrc() {
  [[ -a .nvmrc ]] || return
  local node_version=&quot;$(nvm version)&quot;
  local nvmrc_path=&quot;$(nvm_find_nvmrc)&quot;

  if [ -n &quot;$nvmrc_path&quot; ]; then
    local nvmrc_node_version=$(nvm version &quot;$(cat &quot;${nvmrc_path}&quot;)&quot;)

    if [ &quot;$nvmrc_node_version&quot; = &quot;N/A&quot; ]; then
      nvm install
    elif [ &quot;$nvmrc_node_version&quot; != &quot;$node_version&quot; ]; then
      nvm use
    fi
  elif [ &quot;$node_version&quot; != &quot;$(nvm version default)&quot; ]; then
    echo &quot;Reverting to nvm default version&quot;
    nvm use default
  fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc&lt;/code&gt;&lt;/pre&gt;</description>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/263</guid>
      <comments>https://geonlee.tistory.com/263#entry263comment</comments>
      <pubDate>Thu, 18 Jan 2024 10:16:38 +0900</pubDate>
    </item>
    <item>
      <title>  2023  </title>
      <link>https://geonlee.tistory.com/262</link>
      <description>&lt;h1&gt;  들어가며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023년이 이제 얼마 안남았다. 작년에는 해가 지나고 나서야 회고글을 작성했는데, 아내가 회고를 작성하는 사이에 나도 짬이 나서 회고를 작성해보려한다. 아내는 인스타에 회고 포스팅을 하지만, 나는 늘 블로그에 올리던 회고를 올리는 블로그에 올리고 있는 중이다.&lt;/p&gt;
&lt;h1&gt; ️ 쇼핑 팀 이동&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀 이동은 작년에 했지만, 제대로 일을 한 건 올 해부터였다. 주된 일은 일본 쪽 쇼핑을 담당하고 있었는데, 어쩌다보니 네이버 쇼핑 MY 영역의 개편을 담당하게되었다. 새로운 도메인에 적응도 하면서 새로운 프로젝트를 맨 바닥부터 해야하는 작업을 해야해서 서비스 출시하기까지 우여곡절이 많았다. 하지만, 좋은 동료분들과 협업을 통해 문제없이 잘 출시했고, 현재도 잘 서비스되고 있다. (개편된 네이버 쇼핑 MY를 보고 싶다면 &lt;a href=&quot;https://shopping.naver.com/my/home&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;780&quot; data-origin-height=&quot;1686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cM1Wcy/btsCMxHFdNM/W2BWI1vwOJsnpkhYimzBc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cM1Wcy/btsCMxHFdNM/W2BWI1vwOJsnpkhYimzBc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cM1Wcy/btsCMxHFdNM/W2BWI1vwOJsnpkhYimzBc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcM1Wcy%2FbtsCMxHFdNM%2FW2BWI1vwOJsnpkhYimzBc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;780&quot; height=&quot;1686&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;1686&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;  운동&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결혼을 앞두고, 많은 사람들 앞에 서야되다보니 살을 좀 빼고, 근육을 키우고자 나름 열심히 헬스장을 다녔지만, 생각했던 것보다 큰 성과는 나지 못했다  내년에는 아내의 조언을 듣고 PT를 받으면서 본격적으로 운동을 해보려한다. 내년 회고때는 정확한 수치와 함께 변화된 내 모습을 기록했으면한다.&lt;/p&gt;
&lt;h1&gt;사이드 프로젝트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년만큼은 아니지만, 사이드 프로젝트는 꾸준히 하고있다. 출시를 앞두고 있을 때는 거의 하지 못해서 중간에 좀 빈 곳들이 있긴했지만, 나름 선방했다고본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dv0dPD/btsCOzrsAQR/XbH2LD2vgmxt9DjcUFoP01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dv0dPD/btsCOzrsAQR/XbH2LD2vgmxt9DjcUFoP01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dv0dPD/btsCOzrsAQR/XbH2LD2vgmxt9DjcUFoP01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdv0dPD%2FbtsCOzrsAQR%2FXbH2LD2vgmxt9DjcUFoP01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;768&quot; height=&quot;200&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 프로젝트 개선은 꾸준히 진행중이고, 사용하던 서비스들도 전환하는 작업도 진행하고있다. 기획, 개발, 디자인을 모두 혼자하려다보니 쉽지는 않지만 초반과 비교하면 나름 많이 성장한 것 같다.&lt;/p&gt;
&lt;h1&gt;블로그&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년에 한 해에 블로그 글 3개 썼다고 너무하다고 했는데, 올해는 1개를 썼다. 올해는 너무 바쁘기도하고, 나름의 변명거리들이 있기 때문에 양심의 가책은 없지만, 1개는 좀 아쉽긴하다. 내년에는 간단한 글이라도 짜잘 짜잘하게 써야겠다.&lt;/p&gt;
&lt;h1&gt; &amp;zwj;♀️ &amp;zwj;♂️ 결혼&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년 회고글에는 여자친구에게 프로포즈했다는 글을 적었는데, 그 결실로 올해 10월에 결혼을 했다. 올 해를 돌이켜보면 모든 일들이 결혼을 빼고는 이야기할 수 없었던 것 같다. 그만큼 일생일대 가장 중요한 이벤트를 마무리했다. 아내의 노력이 없었다면 이만큼 성공적으로 마무리하지는 못했을 것 같다. 대부분의 일정 조율을 아내가 하고, 아내가 내가 많이 부족한 부분들을 케어해주고 노력이 있었기에 성공적인 결혼이 된 것 간다. 이런 좋은 사람이 내 곁에 있어주어서 고맙고 감사하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5000&quot; data-origin-height=&quot;3330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8wK6d/btsCUZPO7aN/5j1PRggWyvoXjkO1RfGc30/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8wK6d/btsCUZPO7aN/5j1PRggWyvoXjkO1RfGc30/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8wK6d/btsCUZPO7aN/5j1PRggWyvoXjkO1RfGc30/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8wK6d%2FbtsCUZPO7aN%2F5j1PRggWyvoXjkO1RfGc30%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;5000&quot; height=&quot;3330&quot; data-origin-width=&quot;5000&quot; data-origin-height=&quot;3330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결혼을 끝나고 간 8박 10일간의 하와이 신혼여행은 들인 돈에 비례하듯 일생을 통틀어 최고의 경험이였고, 돌아오고 나서도 아내와 함께 하루하루 행복한 나날을 보내고 있다. 결혼이라는 인생 일대의 이벤트를 성공적으로 마무리했으니 올 해는 성공한 한 해같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_1866.jpg&quot; data-origin-width=&quot;3478&quot; data-origin-height=&quot;2608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ca92P0/btsCQoQLwZO/udiKRKKdEmbvcnlOHz0j31/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ca92P0/btsCQoQLwZO/udiKRKKdEmbvcnlOHz0j31/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ca92P0/btsCQoQLwZO/udiKRKKdEmbvcnlOHz0j31/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fca92P0%2FbtsCQoQLwZO%2FudiKRKKdEmbvcnlOHz0j31%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;3478&quot; height=&quot;2608&quot; data-filename=&quot;IMG_1866.jpg&quot; data-origin-width=&quot;3478&quot; data-origin-height=&quot;2608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;24년을 바라보며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇 시간 뒤면 24년, 아내과 한 가정을 꾸리고 살면서 맞이하는 첫 새해이다. 아내와 함께 보낼 완전한 한 해를 생각하니 어떻게 보낼 지 정말 궁금하다. 내년에도 더 좋은 사람이 되도록 노력하면서 살아야겠다.&lt;/p&gt;</description>
      <category>Devlog/회고록</category>
      <category>회고</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/262</guid>
      <comments>https://geonlee.tistory.com/262#entry262comment</comments>
      <pubDate>Sun, 31 Dec 2023 22:44:52 +0900</pubDate>
    </item>
    <item>
      <title>미드저니(Midjourney)를 써보았다.</title>
      <link>https://geonlee.tistory.com/261</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Geon_1_is_a_middle-aged_man_sitting_by_a_large_window_on_a_rain_1e5252e6-1017-4d7e-85d7-804ac07593d3.png&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;896&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/92Afs/btr0IvyZYJg/EDLfUVzktzNZ6CIOlq1qpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/92Afs/btr0IvyZYJg/EDLfUVzktzNZ6CIOlq1qpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/92Afs/btr0IvyZYJg/EDLfUVzktzNZ6CIOlq1qpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F92Afs%2Fbtr0IvyZYJg%2FEDLfUVzktzNZ6CIOlq1qpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1568&quot; height=&quot;896&quot; data-filename=&quot;Geon_1_is_a_middle-aged_man_sitting_by_a_large_window_on_a_rain_1e5252e6-1017-4d7e-85d7-804ac07593d3.png&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;896&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 이미지 생성 서비스인 미드 저니를 써보았다. 해당 서비스가 기존에 존재했던 건 알고 있었지만 사용하지 않았는데, 요즘 ChatGPT를 사용하다가 알게된&amp;nbsp;&lt;a href=&quot;https://chrome.google.com/webstore/detail/aiprm-for-chatgpt/ojnbohmppadfgpejeebfnmnknjdlckgj?hl=en-US&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AIPRM for ChatGPT&lt;/a&gt; 이라는 확장 프로그램에서 키워드를 넣으면 미드저니용 프롬프트를 짜주는 프롬프트(Midjourney&amp;nbsp;Prompt&amp;nbsp;Generator)를 제공해서 궁금증에 한 번 써보았다.&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;2846&quot; data-origin-height=&quot;1934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b68fff/btr0GaI7R64/wOFHNH68sDjsWduBBLIxdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b68fff/btr0GaI7R64/wOFHNH68sDjsWduBBLIxdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b68fff/btr0GaI7R64/wOFHNH68sDjsWduBBLIxdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb68fff%2Fbtr0GaI7R64%2FwOFHNH68sDjsWduBBLIxdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2846&quot; height=&quot;1934&quot; data-origin-width=&quot;2846&quot; data-origin-height=&quot;1934&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;span style=&quot;color: #343541;&quot;&gt;You will now act as a prompt generator for a generative AI called &quot;Midjourney&quot;. Midjourney AI generates images based on given prompts. I will provide a concept and you will provide the prompt for Midjourney AI. You will never alter the structure and formatting outlined below in any way and obey the following guidelines: You will not write the words &quot;description&quot; or use &quot;:&quot; in any form. Never place a comma between [ar] and [v]. You will write each prompt in one line without using return. Structure: [1] = 창밖을 보며 미래에 대해 고민하는 남성 [2] = a detailed description of [1] that will include very specific imagery details. [3] = with a detailed description describing the environment of the scene. [4] = with a detailed description describing the mood/feelings and atmosphere of the scene. [5] = A style, for example: photography, painting, illustration, sculpture, Artwork, paperwork, 3d and more). [1] [6] = A description of how [5] will be realized. (e.g. Photography (e.g. Macro, Fisheye Style, Portrait) with camera model and appropriate camera settings, Painting with detailed descriptions about the materials and working material used, rendering with engine settings, a digital Illustration, a woodburn art (and everything else that could be defined as an output type) [ar] = &quot;--ar 16:9&quot; if the image looks best horizontally, &quot;--ar 9:16&quot; if the image looks best vertically, &quot;--ar 1:1&quot; if the image looks best in a square. (Use exactly as written) [v] = If [5] looks best in a Japanese art style use, &quot;--niji&quot;. Otherwise use, &quot;--v 4&quot; (Use exactly as written) Formatting: What you write will be exactly as formatted in the structure below, including the &quot;/&quot; and &quot;:&quot; This is the prompt structure: &quot;/imagine prompt: [1], [2], [3], [4], [5], [6], [ar] [v]&quot;. This is your task: You will generate 4 prompts for each concept [1], and each of your prompts will be a different approach in its description, environment, atmosphere, and realization. The prompts you provide will be in English. Please pay attention: - Concepts that can't be real would not be described as &quot;Real&quot; or &quot;realistic&quot; or &quot;photo&quot; or a &quot;photograph&quot;. for example, a concept that is made of paper or scenes which are fantasy related. - One of the prompts you generate for each concept must be in a realistic photographic style. you should also choose a lens type and size for it. Don't choose an artist for the realistic photography prompts. - Separate the different prompts with two new lines&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 키워드에 맞춰 위와 같은 프롬프트를 요청하게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 질의에 대한 결과 값은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f7f7f8; color: #374151;&quot;&gt;/imagine prompt: [1] is a middle-aged man sitting by a large window on a rainy day, staring outside with a pensive expression. He is wearing a tweed jacket with leather elbow patches and holding a cup of steaming tea in his hands. The light coming through the window illuminates his face, casting a shadow on the left side. He appears lost in thought and deep contemplation. [3] The room is decorated with antique furniture, including a mahogany desk with brass handles, an oriental rug, and a bookshelf filled with leather-bound books. The walls are painted in a soft gray color, and there are several potted plants scattered throughout the room. [4] The atmosphere is introspective and melancholic, with a hint of nostalgia. The sound of raindrops hitting the windowpane creates a soothing background noise. The man's posture and facial expression convey a sense of loneliness and longing. [5] Photography [6] A close-up portrait shot with a 50mm prime lens, using natural light. The aperture will be set to f/2.8 to create a shallow depth of field and draw attention to the man's face. The camera will be a Canon EOS 5D Mark IV. --ar 16:9 --v 4&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프롬프트를 미드저니에 질의하면, 미드저니는 해당 질의에 대해 4개의 결과물을 주고 그 중에 하나를 골라서 또 다른 4개의 결과물을 얻거나 화질을 높여서 결과 이미지를 받는 방식으로 되어있다. 맨 처음에 올린 사진이 바로 미드저니를 통해 얻은 결과물이다. 다음 사진은 또 다른 결과물인 &quot;호텔 주방장이 된 스티브 잡스&quot;이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Geon_Steve_Jobs_Becomes_Famous_Hotel_Chef_a7ac0dc9-a724-432f-99ff-226699e9356a.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8K1K9/btr0KRhfvHM/i0JFyKRwsXuVAXUe7rpMWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8K1K9/btr0KRhfvHM/i0JFyKRwsXuVAXUe7rpMWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8K1K9/btr0KRhfvHM/i0JFyKRwsXuVAXUe7rpMWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8K1K9%2Fbtr0KRhfvHM%2Fi0JFyKRwsXuVAXUe7rpMWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-filename=&quot;Geon_Steve_Jobs_Becomes_Famous_Hotel_Chef_a7ac0dc9-a724-432f-99ff-226699e9356a.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&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;</description>
      <category>Devlog</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/261</guid>
      <comments>https://geonlee.tistory.com/261#entry261comment</comments>
      <pubDate>Sat, 25 Feb 2023 18:38:09 +0900</pubDate>
    </item>
    <item>
      <title>  2022 </title>
      <link>https://geonlee.tistory.com/260</link>
      <description>&lt;h1&gt;  들어가며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년은 이미 지나갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그냥 쓰는거다 회고니까. 그리고 2022년에 글을 너무 안써서 2022년은 글을 안 쓴 해로 정하고, 2023년에 힘을 더 주기 위해 2023년에 쓴다는 변명과 함께 시작해보자.&lt;/p&gt;
&lt;h1&gt; &amp;zwj;  출근 어게인&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 회사를 출근하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년 회고에서는 재택 어게인을 이야기했지만, 2022년 중순부터 회사에서 의해 3일 출근 2일 재택의 하이브리드 근무제와 완전 재택 중에 고를 수 있는 기회를 주었다. 재택의 뽕맛에 취해있던 나는 당연히 재택을 선택하려했지만, 주변의 만류로 결국 하이브리드 근무제를 선택했다. 재택에 익숙해져서 회사와 가까운 거리임에도 3일 출근이 고통스러웠지만, 동료들을 직접 만나서 이야기하고 일하는 것이 어느정도 필요하다고는 느껴졌다. 오랫만에 느껴보는 회사밥은 자취생의 최대 고민거리인 식사를 해결해주었고, 신사옥으로 들어가면서 새로운 회사에 다니는 느낌도 들었다. 물론 금방 사라졌지만말이다.&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/G6UgH/btrVcNKQxsi/Kf629Tv6TXHtzoNwUCtQg0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G6UgH/btrVcNKQxsi/Kf629Tv6TXHtzoNwUCtQg0/img.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1547&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.5686%; margin-right: 10px;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G6UgH/btrVcNKQxsi/Kf629Tv6TXHtzoNwUCtQg0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG6UgH%2FbtrVcNKQxsi%2FKf629Tv6TXHtzoNwUCtQg0%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;1280&quot; height=&quot;1547&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k7qV7/btrU0umjt7y/fwhMzFosQt9KFpBb5xGyxK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k7qV7/btrU0umjt7y/fwhMzFosQt9KFpBb5xGyxK/img.jpg&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;1441&quot; data-is-animation=&quot;false&quot; style=&quot;width: 29.4738%; margin-right: 10px;&quot; data-widthpercent=&quot;30.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k7qV7/btrU0umjt7y/fwhMzFosQt9KFpBb5xGyxK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk7qV7%2FbtrU0umjt7y%2FfwhMzFosQt9KFpBb5xGyxK%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;1079&quot; height=&quot;1441&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgAvXI/btrU1NePYwA/UKGrO1Pt9837WkVRHoUx70/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgAvXI/btrU1NePYwA/UKGrO1Pt9837WkVRHoUx70/img.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1414&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;36.48&quot; style=&quot;width: 35.632%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgAvXI/btrU1NePYwA/UKGrO1Pt9837WkVRHoUx70/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgAvXI%2FbtrU1NePYwA%2FUKGrO1Pt9837WkVRHoUx70%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;1280&quot; height=&quot;1414&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;팀 이동&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀 이동을 2번이나 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해 어쩌다보니 팀 이동을 2번이나 했다. 새로운 사람들을 만나는 건 항상 걱정되면서도 기대된다. 과연 내가 새로운 사람들과 잘 어울릴 수 있을까? 내가 팀이 도움을 많이 줄 수 있을까? 라는 생각을 계속한다. 나 자체가 변화를 싫어하는 사람이라 이런 식으로 자의든 타의든 변화를 시도하는 것이 나에게는 굉장히 좋은 영향을 주고 있다. 새로운 사람들과 만나는 환경에 나 자신을 강제로 던져놓으면서 새로운 사람과 더 잘 어울리는 방법을 배워가고 있다.&lt;/p&gt;
&lt;h1&gt;운동&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;달리기도 꾸준히하고, 크로스핏을 찍먹하다가, 헬스를 끊었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해도 달리기는 꾸준히 해왔다. 다만, 시간이 없기도 하고, 꾸준히하기 위해 한 시간까지 달리지는 않고, 3키로 정도로 달리면서 리프레시를 하고 있다. 중간에 크로스핏을 잠깐 다녔는데, 너무 재미있긴했는데, 나의 운동 신경으로는 아직 힘들어서 2달정도 다니고 그만뒀다. 연말부터는 여자친구와 같이 헬스장을 끊어서 열심히 다니고 있다. 헬스장은 진짜 오랫만인데, 생각보다 근육량이 늘지는 않고 있지만, 꾸준히 다니는 데 집중하려한다.&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/bQG0pW/btrVamfKf54/KeZYrp8YWqJUk35GVqM1IK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQG0pW/btrVamfKf54/KeZYrp8YWqJUk35GVqM1IK/img.jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.7931%; margin-right: 10px;&quot; data-widthpercent=&quot;49.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQG0pW/btrVamfKf54/KeZYrp8YWqJUk35GVqM1IK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQG0pW%2FbtrVamfKf54%2FKeZYrp8YWqJUk35GVqM1IK%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;1080&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YGatl/btrUZY19xkf/dp9MQbcUHDkOEcosdR46w1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YGatl/btrUZY19xkf/dp9MQbcUHDkOEcosdR46w1/img.jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1053&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.0442%;&quot; data-widthpercent=&quot;50.63&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YGatl/btrUZY19xkf/dp9MQbcUHDkOEcosdR46w1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYGatl%2FbtrUZY19xkf%2Fdp9MQbcUHDkOEcosdR46w1%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;1053&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;30&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;30대에 접어들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10대 심지어는 20대때에도 30이라는 숫자는 엄청 멀어보였는데, 내가 그 나이가 되어서 살고 있다. 근데 살아보니 주변의 20대가 놀리는 빈도가 늘었을 뿐 별건 없었다. 30대가 되자마자 전직하듯이 성숙해지는 것도 아니고...&lt;/p&gt;
&lt;h1&gt;사이드 프로젝트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 프로젝트에 진정으로 몰빵 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년에 블로그에 신경을 많이 못 쓰고, 사이드 프로젝트에 몰빵을 했다고했는데, 진정한 몰빵은 올해였다. 업무 이후에는 노는 것 대신에 바로 사이드 프로젝트를 진행하고, 거의 회사 Github 수준의 깃허브 활동량을 보였다.&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;2312&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2Eb5E/btrU1qxemGi/3mGiJkj4oX9JNJV0hfRpt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2Eb5E/btrU1qxemGi/3mGiJkj4oX9JNJV0hfRpt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2Eb5E/btrU1qxemGi/3mGiJkj4oX9JNJV0hfRpt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2Eb5E%2FbtrU1qxemGi%2F3mGiJkj4oX9JNJV0hfRpt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2312&quot; height=&quot;584&quot; data-origin-width=&quot;2312&quot; data-origin-height=&quot;584&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;기존 프로젝트들도 개선하고, 새로운 프로젝트도 진행하고, 작년의 목표였던 Flutter를 접하면서 앱까지 만들어보았다. 여전히 앱에 많이 익숙하지 않지만, 2023년에는 앱을 조금 더 꾸준히 공부하고, 작업하면서 더 많은 것을 배우고 싶다. 웹이 아닌 새로운 분야를 공부하는 건 역시 기존 분야를 지속할 수 있는 원동력이 되는 것 같다.&lt;/p&gt;
&lt;h1&gt;블로그&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그를 많이 신경쓰지 못했다.&lt;/p&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-origin-width=&quot;1786&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cp977r/btrU2JXxSrj/EK4jLFgsDKNc3ZcEOHBQZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cp977r/btrU2JXxSrj/EK4jLFgsDKNc3ZcEOHBQZ1/img.png&quot; data-alt=&quot;아무리 생각해도 글 3개는 좀 너무하긴했다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cp977r/btrU2JXxSrj/EK4jLFgsDKNc3ZcEOHBQZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcp977r%2FbtrU2JXxSrj%2FEK4jLFgsDKNc3ZcEOHBQZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1786&quot; height=&quot;496&quot; data-origin-width=&quot;1786&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;아무리 생각해도 글 3개는 좀 너무하긴했다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;프로포즈&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여자친구에게 프로포즈를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해, 나에게 있어 가장 중요한 이벤트가 아닐까 싶다. 올 해 프로포즈 당일까지도 내 머리에는 프로포즈 생각밖에 없었다. 신선하지 않은 것은 나에게는 사형선고와 같은 것이다. 따라서 뭐라도 특별한 것을 해주고 싶은 마음이 컸고, 웹 프론트엔드 엔지니어라는 특성을 살려 프로포즈 영상 같은 포맷을 사이트로 구현해서 프로포즈했다. 결과는 대성공이었다. 여자친구도 너무 좋아했고, 열심히 준비했던 시간을 보상 받는 것 같아서 나도 뿌듯했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Jan-02-2023 00-57-36.gif&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VEZdM/btrUYQQIpST/9pRQHnawSCcqIQDEOSOvlk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VEZdM/btrUYQQIpST/9pRQHnawSCcqIQDEOSOvlk/img.gif&quot; data-alt=&quot;한땀한땀만든 프로포즈 반지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VEZdM/btrUYQQIpST/9pRQHnawSCcqIQDEOSOvlk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/VEZdM/btrUYQQIpST/9pRQHnawSCcqIQDEOSOvlk/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;492&quot; height=&quot;390&quot; data-filename=&quot;Jan-02-2023 00-57-36.gif&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;한땀한땀만든 프로포즈 반지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jo0jo/btrU2KIT4Am/S1FAvCKIyg1lmqASeLDqT0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jo0jo/btrU2KIT4Am/S1FAvCKIyg1lmqASeLDqT0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jo0jo/btrU2KIT4Am/S1FAvCKIyg1lmqASeLDqT0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJo0jo%2FbtrU2KIT4Am%2FS1FAvCKIyg1lmqASeLDqT0%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;1160&quot; height=&quot;1440&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;23년을 바라보며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 언급했던 프로포즈에서 눈치챘겠지만, 23년에 결혼이 예정되어있다. 프로포즈는 그 해에 가장 중요한 이벤트였는데, 결혼은 내 인생에서 가장 중요한 이벤트가 아닐까 싶다. 여자친구와 함께 이 중요한 이벤트를 잘 준비하고, 잘 마무리한다면, 그것만으로도 23년은 훌륭한 해가 될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWWUNi/btrVaRNuBxV/qywSBkbEyfTHfjG0ivajmk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWWUNi/btrVaRNuBxV/qywSBkbEyfTHfjG0ivajmk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWWUNi/btrVaRNuBxV/qywSBkbEyfTHfjG0ivajmk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWWUNi%2FbtrVaRNuBxV%2FqywSBkbEyfTHfjG0ivajmk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;409&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Devlog/회고록</category>
      <category>2022</category>
      <category>2023</category>
      <category>개발자</category>
      <category>회고</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/260</guid>
      <comments>https://geonlee.tistory.com/260#entry260comment</comments>
      <pubDate>Mon, 2 Jan 2023 01:15:59 +0900</pubDate>
    </item>
    <item>
      <title>깃허브(Github) 프로필 README 꾸미기</title>
      <link>https://geonlee.tistory.com/259</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 과거에 작성했던 &lt;a href=&quot;https://geonlee.tistory.com/206&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;당신의 Github을 매력적으로 보일 수 있는 방법들 ✨&lt;/a&gt;을 작성했을 당시에는 없었던 영역이자, 어떻게 보면 Github 프로필에서 가장 중요한 부분이 될 수 있는 영역에 대한 이야기와 이를 사용하는 방법에 대해 알아보고자한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1655912515685&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;당신의 Github을 매력적으로 보일 수 있는 방법들 ✨&quot; data-og-description=&quot;  들어가며 개발자에게 Github이란 어떤 공간일까? 나의 코드를 공유하는 저장소이자, 남들의 코드를 쉽게 다운로드 받고 쓸 수 있는 마켓이면서, 멋진 개발자들을 볼 수 있는 만남의 공간이다. &quot; data-og-host=&quot;geonlee.tistory.com&quot; data-og-source-url=&quot;https://geonlee.tistory.com/206&quot; data-og-url=&quot;https://geonlee.tistory.com/206&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bygIJ0/hyOREFN8Et/q0OOOnukPE1klrfKY6m4t1/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533,https://scrap.kakaocdn.net/dn/boAvYB/hyORLyape7/8rFTQzdC4Wx6OdOUojimW1/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533,https://scrap.kakaocdn.net/dn/cqr34E/hyORKeXoY6/kTZCwdUMHVxJWyfcj36Pk1/img.png?width=1954&amp;amp;height=1084&amp;amp;face=0_0_1954_1084&quot;&gt;&lt;a href=&quot;https://geonlee.tistory.com/206&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://geonlee.tistory.com/206&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bygIJ0/hyOREFN8Et/q0OOOnukPE1klrfKY6m4t1/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533,https://scrap.kakaocdn.net/dn/boAvYB/hyORLyape7/8rFTQzdC4Wx6OdOUojimW1/img.jpg?width=800&amp;amp;height=533&amp;amp;face=0_0_800_533,https://scrap.kakaocdn.net/dn/cqr34E/hyORKeXoY6/kTZCwdUMHVxJWyfcj36Pk1/img.png?width=1954&amp;amp;height=1084&amp;amp;face=0_0_1954_1084');&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을 매력적으로 보일 수 있는 방법들 ✨&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  들어가며 개발자에게 Github이란 어떤 공간일까? 나의 코드를 공유하는 저장소이자, 남들의 코드를 쉽게 다운로드 받고 쓸 수 있는 마켓이면서, 멋진 개발자들을 볼 수 있는 만남의 공간이다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;geonlee.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 보는 프로필 영역은 다음과 같다. 가장 대표적인 왼쪽 프로필 영역에는 프로필 이미지부터, 닉네임, 자기소개(bio), 직장등의 정보를 보여주고, 오른쪽에는 내가 고정한 저장소와 나의 Github 풀밭을 보여준다. 그리고 위에 안녕하세요라고 뭔가 글이 작성된 영역이 새로 생겼다. 이 부분이 바로 프로필 README 부분이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1746&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p6A16/btrFuVyjSNV/s1DBKmbPEgwO7tpP7lg0D0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p6A16/btrFuVyjSNV/s1DBKmbPEgwO7tpP7lg0D0/img.png&quot; data-alt=&quot;Github Profile - Geon Lee&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p6A16/btrFuVyjSNV/s1DBKmbPEgwO7tpP7lg0D0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp6A16%2FbtrFuVyjSNV%2Fs1DBKmbPEgwO7tpP7lg0D0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1746&quot; data-origin-width=&quot;2532&quot; data-origin-height=&quot;1746&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Github Profile - Geon Lee&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로필 README란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로필 README는 Github 프로필 최상단에 있는 영역으로, 당신의 정보를 마음껏 공유할 수 있는 영역이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3750&quot; data-origin-height=&quot;1478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CtBXI/btrFvVR767A/EBRJbfMAIkP3HSlQx2PXhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CtBXI/btrFvVR767A/EBRJbfMAIkP3HSlQx2PXhK/img.png&quot; data-alt=&quot;프로필 README 영역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CtBXI/btrFvVR767A/EBRJbfMAIkP3HSlQx2PXhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCtBXI%2FbtrFvVR767A%2FEBRJbfMAIkP3HSlQx2PXhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3750&quot; height=&quot;1478&quot; data-origin-width=&quot;3750&quot; data-origin-height=&quot;1478&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로필 README 영역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로필 README는 이름에서 알 수 있듯이 README.md 파일 즉, 마크다운 언어로 작성된다. 따라서 저장소에서 마크다운 사용하는 것처럼 글 뿐만아니라 이모지, 이미지 심지어는 움짤 등도 사용 가능하다. 고정된 영역에 주어진 값만 넣을 수 있던 전에 비하면, 말 그대로 당신 깃허브 프로필 페이지에 대한 모든 권한을 가진 셈이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github&quot;&gt;Github의 마크다운&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로필 README 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로필 README를 사용하기 위해서는 다음과 같은 과정을 거친다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본인 Github의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;username&lt;/b&gt;과 같은 이름의 저장소를 생성한다.&lt;/li&gt;
&lt;li&gt;저장소는 공개(public) 저장소이여야한다.&lt;/li&gt;
&lt;li&gt;저장소에 README.md라는 파일을 최상단에 포함하고 있어야한다.&lt;/li&gt;
&lt;li&gt;README.md 파일에 어떠한 내용이라도 들어가있어야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UekYX/btrFuf4WByf/jbCm5da1TsHIOfOUV0Xs4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UekYX/btrFuf4WByf/jbCm5da1TsHIOfOUV0Xs4K/img.png&quot; data-alt=&quot;Github username&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UekYX/btrFuf4WByf/jbCm5da1TsHIOfOUV0Xs4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUekYX%2FbtrFuf4WByf%2FjbCm5da1TsHIOfOUV0Xs4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;690&quot; height=&quot;956&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Github username&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;내용 채우기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인을 표현하기 위한 공간인 것은 알겠고, 그럼 무슨 내용으로 채울지 고민이 들 것이다. Github에서는 다음과 같은 내용이 들어가기를 권장하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/psVHm/btrFvsirOU5/ywFz8IEVbHcxHfhMb5E7lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/psVHm/btrFvsirOU5/ywFz8IEVbHcxHfhMb5E7lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/psVHm/btrFvsirOU5/ywFz8IEVbHcxHfhMb5E7lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpsVHm%2FbtrFvsirOU5%2FywFz8IEVbHcxHfhMb5E7lk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1112&quot; height=&quot;204&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;204&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;b&gt;&quot;내 소개&quot;&lt;/b&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만... 저런 내용으로 채울 수 있는 개발자들이 몇이나 될까 싶다. 그래서 나만의 권장 내용을 정해보았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본인 소개 - 장황하게 쓸 필요 없이 본인이 어떤 개발자인지에 대해 간단하게 적는게 좋아보인다.&lt;/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;p data-ke-size=&quot;size16&quot;&gt;자료조사를 하고, 이 글을 쓰면서 나도 프로필 README를 꾸며보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1780&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZieGg/btrFt9YsuUQ/KVj0zidKZakY6HrHEwZX4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZieGg/btrFt9YsuUQ/KVj0zidKZakY6HrHEwZX4k/img.png&quot; data-alt=&quot;Geon Lee Github 프로필 README&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZieGg/btrFt9YsuUQ/KVj0zidKZakY6HrHEwZX4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZieGg%2FbtrFt9YsuUQ%2FKVj0zidKZakY6HrHEwZX4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1780&quot; height=&quot;614&quot; data-origin-width=&quot;1780&quot; data-origin-height=&quot;614&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Geon Lee Github 프로필 README&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화려하게 꾸미는 방법도 있지만, 난잡스러운건 싫어서 최대한 간단하게 꾸며보았다. 깃꾸(Github 꾸미기)를 한다면 한 번쯤은 들어봤을 쉴드 뱃지를 이용해서 나와 관련된 링크들을 나열해보았다. 당연한 이야기이지만 티스토리는 로고가 없어서 T로 시작하는 로고중에 가장 멋진 테슬라 로고를 사용했다. 언어 및 도구에 사용된 이미지는 Github의 토픽에서 가져왔다. 자세한 코드는 다음을 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1655916695072&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 - dl0312/dl0312&quot; data-og-description=&quot;Contribute to dl0312/dl0312 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/dl0312/dl0312/blob/master/README.md&quot; data-og-url=&quot;https://github.com/dl0312/dl0312&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/IS8xW/hyOQr2pJPs/b838Awla0LtOo2qhuTVki0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/dl0312/dl0312/blob/master/README.md&quot; data-source-url=&quot;https://github.com/dl0312/dl0312/blob/master/README.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/IS8xW/hyOQr2pJPs/b838Awla0LtOo2qhuTVki0/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 - dl0312/dl0312&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to dl0312/dl0312 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;h2 data-ke-size=&quot;size26&quot;&gt;참고자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Github &amp;gt; Customizing your profile &amp;gt; Your profile README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shields.io&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;배지&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://simpleicons.org&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;배지 아이콘 생성&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Devlog</category>
      <category>github</category>
      <category>개발자</category>
      <category>깃허브</category>
      <category>깃허브꾸미기</category>
      <category>깃허브프로필</category>
      <category>프로필</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/259</guid>
      <comments>https://geonlee.tistory.com/259#entry259comment</comments>
      <pubDate>Thu, 23 Jun 2022 01:55:59 +0900</pubDate>
    </item>
    <item>
      <title>티스토리(Tistory) API - 앱 등록부터 브라우저에서 글 목록 받기까지</title>
      <link>https://geonlee.tistory.com/258</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WgNvt/btrzadl5iUJ/veUqpGQkk7RmUPfpIvbCm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WgNvt/btrzadl5iUJ/veUqpGQkk7RmUPfpIvbCm0/img.png&quot; data-alt=&quot;API&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WgNvt/btrzadl5iUJ/veUqpGQkk7RmUPfpIvbCm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWgNvt%2Fbtrzadl5iUJ%2FveUqpGQkk7RmUPfpIvbCm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;628&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;API&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;티스토리에 글을 어떻게 작성할 수 있을까요? 에디터에 들어가서 글을 작성하는게 일반적일 것입니다. 하지만 티스토리에서 제공하는 API를 이용하면 에디터를 통하지 않고도 글을 작성할 수 있습니다. 앞으로의 글을 통해서 파이썬(Python)에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;티스토리(Tistory) API를 이용하여 티스토리에 글을 작성하는 방법에 대해 알아보도록하겠습니다. 이번 글에서는 파이썬은 전혀 사용하지 않고 브라우저와 티스토리 API만을 이용하여 내 블로그의 글 목록을 가져오는 작업을 해보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;티스토리 API 사용을 위한 앱 등록&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 처음 단계는 티스토리 API 사용을 위한 나의 앱을 등록하는 과정입니다. 티스토리 API는 &lt;b&gt;앱(App)&lt;/b&gt;이라는 단위로 관리되며, 앱에는 해당 앱이 사용되는 서비스의 정보들이 포함되게 됩니다. 또한 앱을 단위로 토큰이 생성되고 각 앱에 따라 사용량이 정해져있기 때문에 앱 등록은 중요한 과정이라 볼 수 있습니다. 우선 티스토리 계정에 로그인을 하고 아래의 페이지로 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.tistory.com/guide/api/manage/register&quot;&gt;https://www.tistory.com/guide/api/manage/register&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649727148690&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;TISTORY&quot; data-og-description=&quot;나를 표현하는 블로그를 만들어보세요.&quot; data-og-host=&quot;www.tistory.com&quot; data-og-source-url=&quot;https://www.tistory.com/guide/api/manage/register&quot; data-og-url=&quot;https://www.tistory.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/o7fWz/hyN0qpFsrn/IoOnbo1Yf2uQuMFmOofpi1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/dqUvem/hyN0blJPAL/0hRL0kavkdBAPz0tnbCwJ0/img.jpg?width=1200&amp;amp;height=900&amp;amp;face=0_0_1200_900,https://scrap.kakaocdn.net/dn/bJXKeK/hyN1xOfWYv/tqcHk2gRpYvmGJTLnAmThk/img.jpg?width=1200&amp;amp;height=900&amp;amp;face=0_0_1200_900&quot;&gt;&lt;a href=&quot;https://www.tistory.com/guide/api/manage/register&quot; data-source-url=&quot;https://www.tistory.com/guide/api/manage/register&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/o7fWz/hyN0qpFsrn/IoOnbo1Yf2uQuMFmOofpi1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/dqUvem/hyN0blJPAL/0hRL0kavkdBAPz0tnbCwJ0/img.jpg?width=1200&amp;amp;height=900&amp;amp;face=0_0_1200_900,https://scrap.kakaocdn.net/dn/bJXKeK/hyN1xOfWYv/tqcHk2gRpYvmGJTLnAmThk/img.jpg?width=1200&amp;amp;height=900&amp;amp;face=0_0_1200_900');&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;TISTORY&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.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 페이지에 들어가게 되면 바로, 앱 등록을 위한 화면이 나오게됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;1902&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o0wG7/btrzdM8kUwu/5UlhXIrkOi0QkOhhUlBK0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o0wG7/btrzdM8kUwu/5UlhXIrkOi0QkOhhUlBK0k/img.png&quot; data-alt=&quot;티스토리 API 앱 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o0wG7/btrzdM8kUwu/5UlhXIrkOi0QkOhhUlBK0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo0wG7%2FbtrzdM8kUwu%2F5UlhXIrkOi0QkOhhUlBK0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1718&quot; height=&quot;1902&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;1902&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;티스토리 API 앱 등록&lt;/figcaption&gt;
&lt;/figure&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;서비스명 : API를 사용하고자하는 블로그 명&lt;/li&gt;
&lt;li&gt;서비스 URL : 블로그 전체 주소&lt;/li&gt;
&lt;li&gt;서비스 형태 : 서비스를 만들고자하는 건 아니기 때문에 아무거나 체크해도 되지만, 가장 무난한 웹서비스를 체크&lt;/li&gt;
&lt;li&gt;서비스 권한 : API를 이용한 글 작성이 목적이니 읽기, 쓰기 권한을 체크&lt;/li&gt;
&lt;li&gt;CallBack : 서비스 URL과 동일하게 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 다 넣었다면 등록을 눌러줍니다. 등록이 완료 되었다면 앱 관리 탭을 선택해서 등록된 앱을 확인해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1626&quot; data-origin-height=&quot;952&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lIXpR/btrzenmAfG5/qEW3lcriyeeUpNkowxHKSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lIXpR/btrzenmAfG5/qEW3lcriyeeUpNkowxHKSK/img.png&quot; data-alt=&quot;티스토리 API 앱 관리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lIXpR/btrzenmAfG5/qEW3lcriyeeUpNkowxHKSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlIXpR%2FbtrzenmAfG5%2FqEW3lcriyeeUpNkowxHKSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1626&quot; height=&quot;952&quot; data-origin-width=&quot;1626&quot; data-origin-height=&quot;952&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;티스토리 API 앱 관리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록이 잘 된 것을 확인했으면 이제 인증관리 아래 &lt;b&gt;설정&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;1624&quot; data-origin-height=&quot;1382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHmQxB/btrzdg2DT2L/2vvc6pkJnAAJqjaTIeRNK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHmQxB/btrzdg2DT2L/2vvc6pkJnAAJqjaTIeRNK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHmQxB/btrzdg2DT2L/2vvc6pkJnAAJqjaTIeRNK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHmQxB%2Fbtrzdg2DT2L%2F2vvc6pkJnAAJqjaTIeRNK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1624&quot; height=&quot;1382&quot; data-origin-width=&quot;1624&quot; data-origin-height=&quot;1382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 영역은 앱 등록시 입력한 앱 정보가 보이고 우리에게 중요한 정보는 &lt;b&gt;앱 아이디/키&lt;/b&gt;의 정보들입니다. 이제 이 정보들을 이용해서 &lt;b&gt;Access Token&lt;/b&gt;을 받아봅시다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;접근 권한 허용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 특정 티스토리 블로그에 해당 앱의 접근을 권한을 허용해주어야합니다. 이를 위해 아래의 주소로 접속해봅시다. 각 쿼리에는 다음의 내용을 채워넣읍시다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;client_id : 위의 앱 관리에 있던 App ID를 넣습니다.&lt;/li&gt;
&lt;li&gt;redirect_rul : 사용하고자하는 블로그의 주소를 넣습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;https://www.tistory.com/oauth/authorize?client_id=&lt;span style=&quot;color: #f3c000;&quot;&gt;${App&amp;nbsp;ID}&lt;/span&gt;&amp;amp;redirect_uri=&lt;span style=&quot;color: #f3c000;&quot;&gt;${블로그&amp;nbsp;주소}&lt;/span&gt;&amp;amp;response_type=code&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649756082479&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;https://www.tistory.com/oauth/authorize?client_id=${App ID}&amp;amp;redirect_uri=${블로그 주소}&amp;amp;response_type=code&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 주소를 입력해서 이동하게 되면, 아래와 같은 페이지로 가게됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3818&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqbdJP/btry8xdskba/88rKC5MlKm8dAaegzvFKL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqbdJP/btry8xdskba/88rKC5MlKm8dAaegzvFKL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqbdJP/btry8xdskba/88rKC5MlKm8dAaegzvFKL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqbdJP%2Fbtry8xdskba%2F88rKC5MlKm8dAaegzvFKL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3818&quot; height=&quot;814&quot; data-origin-width=&quot;3818&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;허가하기를 눌러주면 사용하고자하는 블로그 페이지에 code 쿼리가 추가된 URL로 리다이렉트 해줍니다. 여기에서 code에 해당하는 값을 사용하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1948&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BRIue/btrzeK9XIxH/dfbxmrkVwJOXIst5BCM021/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BRIue/btrzeK9XIxH/dfbxmrkVwJOXIst5BCM021/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BRIue/btrzeK9XIxH/dfbxmrkVwJOXIst5BCM021/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBRIue%2FbtrzeK9XIxH%2FdfbxmrkVwJOXIst5BCM021%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1948&quot; height=&quot;144&quot; data-origin-width=&quot;1948&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Access Token 발급&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 받았던 App ID, Secret Code, Code 등의 값들을 이용해서 Access Token을 받을 차례입니다. 이번에도 역시 값들을 조합해서 URL을 만들어서 들어가도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;https://www.tistory.com/oauth/access_token?client_id=&lt;span style=&quot;color: #f3c000;&quot;&gt;${App ID}&lt;/span&gt;&amp;amp;client_secret=&lt;span style=&quot;color: #f3c000;&quot;&gt;${Secret Code}&lt;/span&gt;&amp;amp;redirect_uri=&lt;span style=&quot;color: #f3c000;&quot;&gt;${티스토리 주소}&lt;/span&gt;&amp;amp;code=&lt;span style=&quot;color: #f3c000;&quot;&gt;${Code}&lt;/span&gt;&amp;amp;grant_type=authorization_code&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649757103367&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;https://www.tistory.com/oauth/access_token?client_id=${App ID}&amp;amp;client_secret=${Secret Code}&amp;amp;redirect_uri=${티스토리 주소}&amp;amp;code=${Code}&amp;amp;grant_type=authorization_code&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Token 요청의 경우에 원래는 서버를 이용해서 요청을 보내야하지만, 브라우저만 사용할 수 있는 좀 더 쉬운 방법으로 알려드리겠습니다. 우선 개발자도구를 키기위해 F12를 눌릅니다. 그리고 나온 개발자 도구의 탭 중에 Network를 클릭합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2004&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ChFP4/btrzadzuyWE/KoYlloSiH1KuJQN0EkGQn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ChFP4/btrzadzuyWE/KoYlloSiH1KuJQN0EkGQn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ChFP4/btrzadzuyWE/KoYlloSiH1KuJQN0EkGQn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FChFP4%2FbtrzadzuyWE%2FKoYlloSiH1KuJQN0EkGQn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2004&quot; height=&quot;372&quot; data-origin-width=&quot;2004&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 위의 URL로 이동해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3836&quot; data-origin-height=&quot;1954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TvD1M/btrzbG2hxlE/PE9Kd5daMGtbMNWJzeAzKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TvD1M/btrzbG2hxlE/PE9Kd5daMGtbMNWJzeAzKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TvD1M/btrzbG2hxlE/PE9Kd5daMGtbMNWJzeAzKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTvD1M%2FbtrzbG2hxlE%2FPE9Kd5daMGtbMNWJzeAzKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3836&quot; height=&quot;1954&quot; data-origin-width=&quot;3836&quot; data-origin-height=&quot;1954&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 왼쪽 화면에는 랜더링할 페이지가 없다는 오류가 나오지만 오른쪽에는 요청으로 인한 값이 온것을 확인할 수 있습니다. 여기에서 Preview 탭을 클릭하면 발급된 &lt;b&gt;access_token&lt;/b&gt;의 값을 확인할 수 있습니다. 이제 해당 토큰을 이용해서 내 블로그의 글을 조회하거나 작성할 수 있게 되었습니다. 이번 글에서는 코드는 사용하지 않고 브라우저를 이용한 요청으로만 확인해볼 예정이니 글 조회에 대한 요청을 해봅시다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;글 목록 API&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 페이지는 티스토리에서 제공하는 API에 대한 스펙 문서입니다 글 목록과 관련된 API 쪽을 확인해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tistory.github.io/document-tistory-apis/apis/v1/post/list.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tistory.github.io/document-tistory-apis/apis/v1/post/list.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649759177541&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;middot; GitBook&quot; data-og-description=&quot;No results matching &amp;quot;&amp;quot;&quot; data-og-host=&quot;tistory.github.io&quot; data-og-source-url=&quot;https://tistory.github.io/document-tistory-apis/apis/v1/post/list.html&quot; data-og-url=&quot;https://tistory.github.io/document-tistory-apis/apis/v1/post/list.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://tistory.github.io/document-tistory-apis/apis/v1/post/list.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tistory.github.io/document-tistory-apis/apis/v1/post/list.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; data-ke-size=&quot;size16&quot;&gt;글 목록 &amp;middot; GitBook&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;No results matching &quot;&quot;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tistory.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OpmNo/btrzdNGanvC/QzDj4xGIdP3bUGexkWwxS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OpmNo/btrzdNGanvC/QzDj4xGIdP3bUGexkWwxS1/img.png&quot; data-alt=&quot;티스토리 글 목록 API&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OpmNo/btrzdNGanvC/QzDj4xGIdP3bUGexkWwxS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOpmNo%2FbtrzdNGanvC%2FQzDj4xGIdP3bUGexkWwxS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1734&quot; height=&quot;722&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;722&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;티스토리 글 목록 API&lt;/figcaption&gt;
&lt;/figure&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;access-token : 위에서 발급받은 Access Token을 이용합니다.&lt;/li&gt;
&lt;li&gt;output : 기본 값은 xml이고, json 요청도 가능합니다. 가독성을 위해 json으로 요청합니다.&lt;/li&gt;
&lt;li&gt;blogName : 내 티스토리 주소에서 앞에 부분에 해당되는 값입니다.&lt;/li&gt;
&lt;li&gt;page : 불러올 페이지 번호입니다. 기본적으로 10개씩 받아오며 페이지 번호는 개발자답지 않게 1부터 시작합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp0yyX/btry9gWWYvH/DaDfpr07Y0WApOy5vQ1GaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp0yyX/btry9gWWYvH/DaDfpr07Y0WApOy5vQ1GaK/img.png&quot; data-alt=&quot;blogName&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp0yyX/btry9gWWYvH/DaDfpr07Y0WApOy5vQ1GaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp0yyX%2Fbtry9gWWYvH%2FDaDfpr07Y0WApOy5vQ1GaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;738&quot; height=&quot;134&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;blogName&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 URL로 요청을 보내면 다음과 같이 보여집니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;1494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOwhMs/btrzdNMXVJK/BsnK1y46F1sY85iKq2oyxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOwhMs/btrzdNMXVJK/BsnK1y46F1sY85iKq2oyxk/img.png&quot; data-alt=&quot;티스토리 글 목록 API 요청 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOwhMs/btrzdNMXVJK/BsnK1y46F1sY85iKq2oyxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOwhMs%2FbtrzdNMXVJK%2FBsnK1y46F1sY85iKq2oyxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1898&quot; height=&quot;1494&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;1494&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;티스토리 글 목록 API 요청 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 &lt;a href=&quot;https://chrome.google.com/webstore/detail/json-formatter/bcjindcccaagfpapjjmafapmmgkkhgoa&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;JSON Formatter&lt;/b&gt;&lt;/a&gt; 같은 확장프로그램을 깔아서 더 깔끔하게 나왔는데, 데이터는 비슷하게 나오는 것을 확인할 수 있을 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 티스토리 API를 이용하기위해 앱 등록부터 Access Token 발급에 간단한 글 목록을 가져오는 작업까지 브라우저에서 작업해보았는데요, 다음 글에서는 티스토리 글 작성 및 좀 더 개발자적 접근을 위해 &lt;b&gt;파이썬(Python)&lt;/b&gt;을 이용하여 티스토리 API를 사용하는 방법을 알려드리겠습니다.&lt;/p&gt;</description>
      <category>Side Project</category>
      <category>Access Token</category>
      <category>Code</category>
      <category>PYTHON</category>
      <category>tistory</category>
      <category>Tistory API</category>
      <category>글 작성</category>
      <category>티스토리</category>
      <category>티스토리 API</category>
      <category>티스토리 글 작성</category>
      <category>파이썬</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/258</guid>
      <comments>https://geonlee.tistory.com/258#entry258comment</comments>
      <pubDate>Tue, 12 Apr 2022 22:30:51 +0900</pubDate>
    </item>
    <item>
      <title>모두 자동화해버리는 Github Actions!</title>
      <link>https://geonlee.tistory.com/257</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/dPRZX6/btrxLlLU6xi/Bf7hLGYHp9h2rq121jBc4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPRZX6/btrxLlLU6xi/Bf7hLGYHp9h2rq121jBc4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPRZX6/btrxLlLU6xi/Bf7hLGYHp9h2rq121jBc4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPRZX6%2FbtrxLlLU6xi%2FBf7hLGYHp9h2rq121jBc4k%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;h1&gt;Github Actions란?&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Github에서 제공하는 저장소에서 소프트웨어 개발 &lt;code&gt;Workflow&lt;/code&gt;를 자동화, 커스터마이징, 실행할 수 있는 서비스&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Jenkins&lt;/code&gt;를 이용했던 CI/CD를 포함하여 원하는 작업을 수행하기 위한 작업을 검색, 생성 및 공유 가능&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Action&lt;/code&gt;들을 결합해서 맞춤형 &lt;code&gt;Workflow&lt;/code&gt; 생성 가능&lt;/li&gt;
&lt;li&gt;기존에 &lt;code&gt;Jenkins&lt;/code&gt;, &lt;code&gt;Lambda&lt;/code&gt;를 이용했던 자동화 작업들을 OSS로 통합 가능&lt;/li&gt;
&lt;li&gt;간단히하자면 Workflow 툴&lt;ul&gt;
&lt;li&gt;Build, Test, Deploy, Release Drafter, Labeler ... &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자세한 설명은 역시 &lt;a href=&quot;https://docs.github.com/en/actions&quot;&gt;Github Actions 공식문서&lt;/a&gt; 참고&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;개념&lt;/h2&gt;
&lt;p&gt;Github Action은 다음과 같은 개념들로 이루어져있습니다.&lt;/p&gt;
&lt;h3&gt;Workflow&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;최상위 개념, &lt;code&gt;yaml&lt;/code&gt;로 작성하는 파일의 단위&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.github/workflows&lt;/code&gt; 하위에 위치하면 저장소에서 자동으로 인식&lt;/li&gt;
&lt;li&gt;단일 혹은 여러개의 &lt;code&gt;Job&lt;/code&gt;으로 구성되어, 특정 &lt;code&gt;Event&lt;/code&gt;에 트리거 될 수 있는 프로세스&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Event&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Workflow&lt;/code&gt;를 실행하는 규칙&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Job&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;가상 환경에서 실해되는 주체, 다른 &lt;code&gt;Job&lt;/code&gt;과의 의존성을 가질 수 있고 병렬로도 실행가능&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Jenkins&lt;/code&gt;의 &lt;code&gt;Job&lt;/code&gt;과 비슷한 개념&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Step&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Task&lt;/code&gt;의 집합으로 &lt;code&gt;Command&lt;/code&gt; 혹은 &lt;code&gt;Action&lt;/code&gt;을 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Action&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Workflow&lt;/code&gt;의 가장 작은 단위&lt;/li&gt;
&lt;li&gt;다른 사람들의 &lt;code&gt;Action&lt;/code&gt;이 public으로 공개되어있다면, 이를 아무런 조건없이 사용할 수 있음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Job&lt;/code&gt;과 &lt;code&gt;Step&lt;/code&gt;을 연결할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Runner&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Github Action Runner App이 설치된 머신으로 Workflow가 실제로 실행될 인스턴스&lt;/li&gt;
&lt;li&gt;Jenkins가 설치된 Server와 비슷한 개념&lt;/li&gt;
&lt;li&gt;github-hosted runner와 Self-hosted runner로 구분&lt;ul&gt;
&lt;li&gt;github-hosted runner: 저장소에서 설치된 서버에 자동으로 설치되어 있는 runner&lt;/li&gt;
&lt;li&gt;self-hosted runner: 사용자가 직접 설치한 서버에 자동으로 설치되어 있는 runner&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;workflow 작성하기&lt;/h2&gt;
&lt;p&gt;가장 간단한 형태인 Build Test를 위한 Workflow를 작성해보겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;name: Build

on:
  # PR의 base 브렌치가 master, RB-**인 경우 해당 workflow를 실행합니다.
  pull_request:
    branches: [ master, RB-** ]

jobs:
  build:
    # 사용할 러너를 설정합니다.
    runs-on: [ self-hosted ]

    # 변수를 정의합니다.
    strategy:
      matrix:
        node-version: [ 14.17 ]

    steps:
      # 소스를 가져옵니다.
      - name: Checkout code
        uses: actions/checkout@v2
        with:
          fetch-depth: 100

      # node-version에 해당하는 Node 세팅합니다.
      - name: Use Node.js
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}

      # 프로젝트를 build합니다.
      - name: Build
        run: |
          npm install -g yarn
          yarn install --prefer-offline --frozen-lockfile
          yarn build&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;해당 코드를 &lt;code&gt;.github/workflows/build.yml&lt;/code&gt; 파일로 저장하고, base 브렌치가 &lt;code&gt;master&lt;/code&gt;혹은 &lt;code&gt;RB-**&lt;/code&gt;인 PR이 올라오면 &lt;code&gt;Workflow&lt;/code&gt;가 자동으로 실행됩니다. 위 코드에서 사용된 &lt;code&gt;actions/checkout@v2&lt;/code&gt;, &lt;code&gt;actions/setup-node@v1&lt;/code&gt;는 Github에서 제공하는 action들로 위와 같이 바로 사용할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;사용할만한 커스텀 Action&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/marketplace/actions/release-drafter&quot;&gt;Release Drafter&lt;/a&gt; - 수정한 파일의 확장자, 브렌치명, PR 제목등에 따라 자동으로 라벨을 추가해주고 정해진 형식에따라 자동으로 릴리즈 초안을 작성해주는 Action&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/marketplace/actions/auto-author-assign&quot;&gt;Auto Author Assign&lt;/a&gt; - PR을 올린 유저를 자동으로 해당 PR의 어사인에 추가해주는 Action&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/marketplace/actions/merge-pull-requests-automerge-action&quot;&gt;Merge pull requests&lt;/a&gt; - 조건을 만족하면 PR을 자동으로 merge하는 Action&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;참고자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/actions&quot;&gt;Github Actions 공식문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/marketplace?category=&amp;amp;query=&amp;amp;type=actions&amp;amp;verification&quot;&gt;Github Actions Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Git</category>
      <category>Actions</category>
      <category>CD</category>
      <category>CI</category>
      <category>github</category>
      <category>자동화</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/257</guid>
      <comments>https://geonlee.tistory.com/257#entry257comment</comments>
      <pubDate>Tue, 29 Mar 2022 22:16:30 +0900</pubDate>
    </item>
    <item>
      <title>  2021  </title>
      <link>https://geonlee.tistory.com/256</link>
      <description>&lt;h1&gt;  들어가며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년이 이제 정말 얼마 안남았다. 요 몇 달간 블로그 글을 안 올리고 있었는데, 한 해도 끝나가는 겸 뭐라도 써야지 하는 심정으로 올 해도 2021년 개발자 회고를 해보려한다.&lt;/p&gt;
&lt;h1&gt; &amp;zwj;  재택 어게인&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2020년 코로나가 터진 이후 2020년에는 회사도 직원들도 이전에 겪어보지 못한 상황에 우왕좌왕하며 재택과 출근을 번갈아했었다. 그러다 2020년 후반부터 회사는 전면 재택을 선언하였고, 계속되는 재택 연장으로 2021년 동안 풀로 재택으로 근무하게 되었다. 그래서 새로운 장비를 구입해서 가져오거나 특별한 일이 있지 않은 이상 회사를 가지 않아서 부서 층수도 까먹고 내 자리가 어디인지도 까먹었을 지경이다... 내년 초까지는 계속 이러한 재택 기조가 이어질 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;2455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vvnIf/btroC6XuPJN/h2Ex4K7DqGG3vySOmDCNBK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vvnIf/btroC6XuPJN/h2Ex4K7DqGG3vySOmDCNBK/img.jpg&quot; data-alt=&quot;2021 데스크 세팅&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vvnIf/btroC6XuPJN/h2Ex4K7DqGG3vySOmDCNBK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvvnIf%2FbtroC6XuPJN%2Fh2Ex4K7DqGG3vySOmDCNBK%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;2455&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;2455&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2021 데스크 세팅&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1년 넘게 집에서만 일을 하다보니 자연스럽게 &quot;코딩은 장비빨&quot;이라는 생각으로 모니터, 키보드, 마우스, 도킹 스테이션, 맥북 거치대 등 이런 저런 장비들을 추가했다. 조만간 해당 내용으로 글도 작성해보려한다.&lt;/p&gt;
&lt;h1&gt;서비스 출시&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년에 이어서 올 해에도 &lt;a href=&quot;https://contents.premium.naver.com/&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;540&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQ2BMX/btroDJnczG9/68O4L5aNB6DF1sqFnoRZJ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQ2BMX/btroDJnczG9/68O4L5aNB6DF1sqFnoRZJ1/img.jpg&quot; data-alt=&quot;프리미엄 콘텐츠&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQ2BMX/btroDJnczG9/68O4L5aNB6DF1sqFnoRZJ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQ2BMX%2FbtroDJnczG9%2F68O4L5aNB6DF1sqFnoRZJ1%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;540&quot; height=&quot;437&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;437&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;해당 프로젝트는 이전 프로젝트보다 의미가 더 깊었다. Next.js, TypeScript, Styled-Components, Ant Design 등 해당 부서에서 많이 사용해보지 않았던 기술 스택들을 적극적으로 도입을 추진하였고, 기초가 되는 프로젝트를 하나하나 만들어갔던 경험을 했기 때문이다. 또한 해당 결과물이 성공적으로 서비스되고 있다는 점도 기뻤다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관련 링크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://contents.premium.naver.com/&quot;&gt;프리미엄 콘텐츠&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://news.naver.com/main/read.naver?mode=LSD&amp;amp;mid=sec&amp;amp;sid1=101&amp;amp;oid=215&amp;amp;aid=0000957739&quot;&gt;네이버, 유료 콘텐츠 실험&amp;hellip;'프리미엄 콘텐츠' 베타 오픈&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;DEVIEW 2021 발표&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 출시를 마치고, DEVIEW 2021이 다가오면서 프리미엄 콘텐츠 스튜디오를 만들면서 배운 내용을 DEVIEW에서 발표해볼 생각이 없냐는 제의가 왔었다. 아직 주니어이고, 내가 정보가 될 만한 내용을 발표할 수 있을까? 잘 발표는 할 수 있을까 라는 생각을 했지만, 사실 어짜피 안 될 것 같아서... 한 번 발표 개요를 올려보았다. 그런데 덜컥 DEVIEW 연사가 되어버린 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmKtJp/btroDO3jdhu/GNLqfTK2ovrHPvPblztaLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmKtJp/btroDO3jdhu/GNLqfTK2ovrHPvPblztaLk/img.png&quot; data-alt=&quot;DEVIEW 2021&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmKtJp/btroDO3jdhu/GNLqfTK2ovrHPvPblztaLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmKtJp%2FbtroDO3jdhu%2FGNLqfTK2ovrHPvPblztaLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;804&quot; height=&quot;408&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DEVIEW 2021&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후에 발표 준비했던 시간은 정말 정신없이 돌아갔었던 것 같다. 공식 문서부터 내가 짜온 코드, 유튜브 영상등 많은 자료를 검토하고 PPT 만들고 팀원들에게 모의 발표도 해보았다. 그렇게 발표날이 다가오고 여차저차 발표를 마쳤다. 이후에 편집본을 보는데 세상 어색했다, 유튜브에서 보던 연사들은 그렇게 말 잘하고 위트있게 하던데... 다시 한 번 대단하다고 느껴졌다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1726&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/suUMr/btrozwbGaax/rrDOEuZaTIzwkYPcXI9nY1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/suUMr/btrozwbGaax/rrDOEuZaTIzwkYPcXI9nY1/img.jpg&quot; data-alt=&quot;Next.js, Apollo와 함께 리액트 개발의 Next Level로 가자!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/suUMr/btrozwbGaax/rrDOEuZaTIzwkYPcXI9nY1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsuUMr%2FbtrozwbGaax%2FrrDOEuZaTIzwkYPcXI9nY1%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;1726&quot; height=&quot;964&quot; data-origin-width=&quot;1726&quot; data-origin-height=&quot;964&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Next.js, Apollo와 함께 리액트 개발의 Next Level로 가자!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 작년에 DEVIEW 2020에서 다른 분들이 발표했던 것을 정리하던 입장에서 이전까지는 상상만하던 &quot;개발자 발표&quot;라는 것을 이루었고, 주니어를 대상으로 하는 발표이다보니 대단한 내용은 아니지만 도움이 되었다는 댓글도 보여서 뿌듯했던 경험이였다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관련 링크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://tv.naver.com/v/23652620&quot;&gt;Next.js, Apollo와 함께 리액트 개발의 Next Level로 가자!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;  운동 그리고 달리기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재택을 하다보면 &quot;움직임&quot;이라는 것이 0에 가까워지게 된다. 아침에 일어나서 대충 씻고, 바로 책상에 앉아서 몇 시간 일하고, 짬 내서 밥 먹고 커피 마시고, 다시 침대로 가서 자고 무한 반복이다. 나와 나무늘보의 만보기 기록을 재면 나무늘보의 수치가 더 높을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4KlWO/btroE8Af6gg/iwuZmgKec1pOjCtx803HCk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4KlWO/btroE8Af6gg/iwuZmgKec1pOjCtx803HCk/img.jpg&quot; data-alt=&quot;나보다 많이 움직이는 나무늘보&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4KlWO/btroE8Af6gg/iwuZmgKec1pOjCtx803HCk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4KlWO%2FbtroE8Af6gg%2FiwuZmgKec1pOjCtx803HCk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;524&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;524&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;이러다보니 살도 찌고, 몸도 둔해지면서 운동을 해야겠다는 생각에 꾸준히 운동을 해왔고 근력과 유산소를 병행하다. 어느 순간 달리기의 매력에 푹 빠졌었다. 근처에 탄천이라는 좋은 코스가 있어서 탄천을 따라 하루 30분 5~6km 정도를 하염없이 뛰는 것이다. 달리기는 하는 순간은 힘들기 때문에 고통도 있지만, 뛰면서 밀렸던 슈카월드도 듣고 노래도 듣고 잠시 여러 상념에도 빠져보는 나에게는 하나의 힐링이였다. 그렇게 거의 매일 달리기를 하였고, 개발 장비처럼 달리기용 장비들도 하나 둘 늘어나게 되었다. 요즘은 너무너무 추워서... 집에서 근력 운동하며 잠시 숨고르기를 하고 있다. 봄이 되면 또 러닝의 계절이 올 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot_20211223-003317_Strava.jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;2165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byrF3S/btroDgMNq4M/X8Js6dtFTj6tjqQR3SZQh1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byrF3S/btroDgMNq4M/X8Js6dtFTj6tjqQR3SZQh1/img.jpg&quot; data-alt=&quot;Strava 11월 기록 - 11월에는 정말 많이 뛰었다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byrF3S/btroDgMNq4M/X8Js6dtFTj6tjqQR3SZQh1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyrF3S%2FbtroDgMNq4M%2FX8Js6dtFTj6tjqQR3SZQh1%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;2165&quot; data-filename=&quot;Screenshot_20211223-003317_Strava.jpg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;2165&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Strava 11월 기록 - 11월에는 정말 많이 뛰었다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관련 링크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=AGxKNNj09X8&amp;amp;ab_channel=%EB%9F%B0%EC%97%85&quot;&gt;달리기는 뇌운동이다&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;블로그&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해는 간단한 정보 제공성 글들은 썼지만, 작년처럼 글다운 글을 쓰지는 못한 것 같다. 작년에는 글쓰는 즐거움 같은게 있었던 것 같은데 올 해는 뭔가 글을 쓰기위해 준비하는 과정 과정이 일처럼 느껴지는 순간이 많았다. 억지로 글을 쓰지는 않을 것 같고 내년에도 간간히 쉬운 글들을 적을 것 같다.&lt;/p&gt;
&lt;h1&gt;사이드 프로젝트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해 블로그에는 많은 신경을 못 썼지만, 그 신경을 모두 사이드 프로젝트에 거의 몰빵했다. 올해 서비스 출시를 하면서 업무도 하면서 끝나고는 사이드 프로젝트를 하고, 덕분에 개인 깃허브의 풀밭도 푸릇푸릇해졌다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAuvZ9/btroDQfLBLj/uq0P2KgEKW3bM6MdXvHes1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAuvZ9/btroDQfLBLj/uq0P2KgEKW3bM6MdXvHes1/img.png&quot; data-alt=&quot;2021년 Github Contribution&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAuvZ9/btroDQfLBLj/uq0P2KgEKW3bM6MdXvHes1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAuvZ9%2FbtroDQfLBLj%2Fuq0P2KgEKW3bM6MdXvHes1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1546&quot; height=&quot;390&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2021년 Github Contribution&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 프로젝트를 계속하면서 느끼는 점이지만 혼자 프로젝트를 하다보니, 실제 일을 하면서 배우는 것들 이외에 생각치도 않았던 것들에 대해 고민해보고 배우게 되었다. 실제 유저들이 사용하는 서비스를 만들고 내가 홍보하고 댓글로 직접 피드백을 받다보니 조금 더 유저 입장에서 생각하면서 만들게 되고, 디자인 적인 부분도 더 챙기게 되었다. 또한 서비스의 크기가 점점 커져도 결국 내가 다 감당해야하다보니 코드의 재사용성을 좀 더 신경써서 짜게 되었다. 사이드 프로젝트로 얻어가는 것들이 많아져서 내년에도 사이드 프로젝트는 꾸준히 병행하게 될 것 같다. 요즘 크로스 플랫폼을 이용한 앱 개발에 관심이 생겨서 내년에는 앱을 하나 출시하는 게 목표이다. 하지만 유데미 강의를 사놓고 튜토리얼 하나 들었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관련 링크&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/238&quot;&gt;더 많이 더 적게: 고장나버린 사이트 살려야한다&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/248&quot;&gt;구글 애드센스로 1000달러 벌기까지의 여정  &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;  22년을 바라보며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년 회고를 읽으며 글을 작성하고 있는 데, 작년 회고에 &quot;일단 2021년에는 코로나가 좀 사라졌으면 좋겠다.&quot;라고 적었더라. 미안하지만 과거의 나야 아직도 코로나 시국이고 변이는 오미크론까지 나왔어 이제 그리스 알파벳도 부족할 지경이야... 신 사옥이 지어졌다는데 아직 입구도 보질 못했다. 내년에는 정말 코로나가 종식되어서 신사옥에서 즐겁게 코딩할 수 있길 바란다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ddk295/btroEzdKTwn/7MwKQzUKk0i6pSVG8QZXmK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ddk295/btroEzdKTwn/7MwKQzUKk0i6pSVG8QZXmK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ddk295/btroEzdKTwn/7MwKQzUKk0i6pSVG8QZXmK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fddk295%2FbtroEzdKTwn%2F7MwKQzUKk0i6pSVG8QZXmK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;409&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Devlog/회고록</category>
      <category>개발자</category>
      <category>회고</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/256</guid>
      <comments>https://geonlee.tistory.com/256#entry256comment</comments>
      <pubDate>Thu, 23 Dec 2021 00:45:00 +0900</pubDate>
    </item>
    <item>
      <title>더 나은 광고 표준(Better Ads Standard)사이트의 광고 경험 문제 해결</title>
      <link>https://geonlee.tistory.com/255</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;더 나은 광고 표준(Better Ads Standard)에 위배되는 광고 경험 알림&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년 10월 21일 새벽 구글 애드센스에 &lt;b&gt;더 나은 광고 표준(Better Ads Standard)에 위배되는 광고 경험&lt;/b&gt;이라는 구글 애드센스의 경고 알림이 떴다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1337&quot; data-origin-height=&quot;93&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBn5Du/btripFd8N8w/RTNwanrNCGkGNWaWeyMLkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBn5Du/btripFd8N8w/RTNwanrNCGkGNWaWeyMLkK/img.png&quot; data-alt=&quot;더 나은 광고 표준(Better Ads Standard)에 위배되는 광고 경험 경고 알림&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBn5Du/btripFd8N8w/RTNwanrNCGkGNWaWeyMLkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBn5Du%2FbtripFd8N8w%2FRTNwanrNCGkGNWaWeyMLkK%2Fimg.png&quot; data-origin-width=&quot;1337&quot; data-origin-height=&quot;93&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;figcaption&gt;더 나은 광고 표준(Better Ads Standard)에 위배되는 광고 경험 경고 알림&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;운영&amp;nbsp;중이신&amp;nbsp;사이트&amp;nbsp;중&amp;nbsp;하나&amp;nbsp;이상이&amp;nbsp;더&amp;nbsp;나은&amp;nbsp;광고&amp;nbsp;표준(Better&amp;nbsp;Ads&amp;nbsp;Standard)에&amp;nbsp;위배되는&amp;nbsp;광고&amp;nbsp;경험을&amp;nbsp;포함하는&amp;nbsp;것으로&amp;nbsp;확인되어&amp;nbsp;경고&amp;nbsp;상태가&amp;nbsp;되었습니다.&amp;nbsp;Google&amp;nbsp;Search&amp;nbsp;Console의&amp;nbsp;광고&amp;nbsp;경험&amp;nbsp;보고서에서&amp;nbsp;수정이&amp;nbsp;필요한&amp;nbsp;광고&amp;nbsp;경험을&amp;nbsp;확인하시기&amp;nbsp;바랍니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 해당 알림을 보고는 깜짝놀랐다. 이와 같은 알림을 받은 분들 모두가 비슷한 감정을 느꼈을 것이다. 구글 애드센스는 사이트에 수익을 가져다주는 좋은 서비스이지만, 잘못해서 한 번 애드센스의 심기를 건드리면 꾸준하던 수익을 사라지게 만들 수 있는 무서운 서비스이다. 그러다보니 이러한 경고문구 하나 하나를 빨리 해결하는 것이 몸에 체득되었고, 이 글을 검색을 통해 들어오신 분들도 비슷할것이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자는 블로그를 포함해 3개 정도의 사이트를 운영하고 있고 모두 수익을 얻고 있기 때문에 더 빠르게 처리해야했고, 우선 자세히 알아보기를 통해 무슨 경고인지 확인해보자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;더 나은 광고 표준(Better Ads Standard)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사이트의 광고 경험 문제 해결&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1866&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LBcbV/btriuivdyqT/kMbPrld82ODwxPgkos0KUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LBcbV/btriuivdyqT/kMbPrld82ODwxPgkos0KUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LBcbV/btriuivdyqT/kMbPrld82ODwxPgkos0KUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLBcbV%2FbtriuivdyqT%2FkMbPrld82ODwxPgkos0KUk%2Fimg.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1866&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;p data-ke-size=&quot;size16&quot;&gt;자세히 알아보기를 누르면 다음과 같은 페이지가 뜨게된다. 그리고 주목할 부분은 &lt;b&gt;1. 보고서 액세스&lt;/b&gt; 부분이다 이 곳에서 어떤 부분이 문제가 되었는 지 확인 할 수 있다. 아마 구글 서치 콘솔(Google Search Console)에 해당 사이트가 연동 되어있는 경우에만 해당 사이트의 권한을 얻어 확인할 수 있는 것 같다. 여기에서 &lt;b&gt;광고 경험 보고서&lt;/b&gt;를 클릭하자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;광고 환경 보고서&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;737&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4e45S/btriq54GP4j/gTLadkDgCSQdk2ngcuTYN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4e45S/btriq54GP4j/gTLadkDgCSQdk2ngcuTYN1/img.png&quot; data-alt=&quot;광고 환경 보고서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4e45S/btriq54GP4j/gTLadkDgCSQdk2ngcuTYN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4e45S%2Fbtriq54GP4j%2FgTLadkDgCSQdk2ngcuTYN1%2Fimg.png&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;737&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;figcaption&gt;광고 환경 보고서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링크를 클릭하면 Web Tools에 있는 광고 환경 보고서로 이동시켜준다. 여기서 &lt;b&gt;광고 환경 보고서 &amp;gt; 모바일&lt;/b&gt; 로 들어간다. 그리고 속성을 선택하는데 서치 콘솔에 소유권을 가진 사이트가 옵션으로 뜨게 된다. 해당 사이트를 클릭해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;326&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqakZ9/btripxUt1pp/Zsj0HG3PKg5P6yCGat7B00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqakZ9/btripxUt1pp/Zsj0HG3PKg5P6yCGat7B00/img.png&quot; data-alt=&quot;광고 환경 보고서: 속성 선택&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqakZ9/btripxUt1pp/Zsj0HG3PKg5P6yCGat7B00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqakZ9%2FbtripxUt1pp%2FZsj0HG3PKg5P6yCGat7B00%2Fimg.png&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;326&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;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;imageblock alignCenter&quot; data-origin-width=&quot;1281&quot; data-origin-height=&quot;479&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqiLTD/btrinP2X6wx/QkZrr0EK2BJZt5VZWFBLz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqiLTD/btrinP2X6wx/QkZrr0EK2BJZt5VZWFBLz0/img.png&quot; data-alt=&quot;광고 환경 보고서: 문제 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqiLTD/btrinP2X6wx/QkZrr0EK2BJZt5VZWFBLz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqiLTD%2FbtrinP2X6wx%2FQkZrr0EK2BJZt5VZWFBLz0%2Fimg.png&quot; data-origin-width=&quot;1281&quot; data-origin-height=&quot;479&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;figcaption&gt;광고 환경 보고서: 문제 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팝업 광고 및 초기 화면 광고라고 되어있는데... 필자는 구글 애드센스에서 제공하는 광고 단위 이외에는 어떤 광고도 붙이고 있지 않다. 그래서 여기서부터 뭔가 이상하게 느껴졌었다. 발견된 URL은 실제 문제가 있었던 예시 URL을 보여주는 것으로 이를 클릭해서 문제가 된 부분을 확인해보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;623&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czX28k/btritMwN6DS/LfkJ8JjuPUYrUJXifqpOc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czX28k/btritMwN6DS/LfkJ8JjuPUYrUJXifqpOc1/img.png&quot; data-alt=&quot;문제가 된 URL&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czX28k/btritMwN6DS/LfkJ8JjuPUYrUJXifqpOc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczX28k%2FbtritMwN6DS%2FLfkJ8JjuPUYrUJXifqpOc1%2Fimg.png&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;623&quot; data-filename=&quot;blob&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;figcaption&gt;문제가 된 URL&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면에 나온 블로그는 내 블로그가 아니였다?.. 실제 블로그 주소는 직접 확인하는게 좋을 것 같다. 뭔가 어그로성 콘텐츠를 다루는 블로그로 보이는데 언론사 사이트마냥 팝업 광고를 덕지덕지 붙여놓은 것이다. 여기서 뭔가 더 이상하다고 느꼈었다. 그리고 위의 루트 도메인을 확인해보니 tistory.com 뭔가 티스토리와 관련된 문제인 것 같아서 검색을 해보니 이유는 다음과 같았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어그로성 콘텐츠로 팝업 광고가 덕지덕지 붙은 티스토리를 운영하는 몇 명의 티스토리 블로거가 있었고, 해당 블로그가 구글 애드센스에 광고 정책 위반으로 신고가 된 것이다. 그러나 티스토리 전체 블로그의 루트 도메인이 tistory.com이기 때문에, 해당 블로그만의 문제로 끝나는 것이 아닌 모든 티스토리 블로그 유저에게 경고가 간 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그럼 이제 나랑 상관 없는건가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니다 해당 블로그에 문제가 된 광고가 계속 있는 이상 경고는 누적 될 것이고, &lt;b&gt;최악의 경우에는 모든 티스토리 블로그에 애드센스가 먹통이 될 것이다. 단지 저 블로거들 때문에 말이다. 거의 군대이상의 연대책임이다.&lt;/b&gt; 따라서 할 수 있는 방법은 해당 블로그를 신고하거나, 항의 글을 작성하는 방법을 취해야한다. 과거 19년에도 비슷한 일이 생겼으나, 신고를 통해 최악의 경우까지는 안 갔던 것처럼 보인다. 이번에도 최악의 상황이 벌어지지 않도록 많은 블로거 분들이 힘을 합쳤으면 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1561&quot; data-origin-height=&quot;306&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wx4Qb/btriuj18P8m/pxiTa9UpiLfMl6Ny39InZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wx4Qb/btriuj18P8m/pxiTa9UpiLfMl6Ny39InZ1/img.png&quot; data-alt=&quot;광고 환경 보고서 변경 로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wx4Qb/btriuj18P8m/pxiTa9UpiLfMl6Ny39InZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwx4Qb%2Fbtriuj18P8m%2FpxiTa9UpiLfMl6Ny39InZ1%2Fimg.png&quot; data-origin-width=&quot;1561&quot; data-origin-height=&quot;306&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;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;blockquote data-ke-style=&quot;style2&quot;&gt;2021년 10월 26일 해당 블로그가 또 경고를 먹었다. 국내 대응은 매크로 답변밖에 안해주는 애드센스라 애드센스에 뭔가를 기대하긴 어려울 것 같고, 티스토리 신고하기를 통해 지속적으로 티스토리에 메시지를 던져주는 것이 좋을 것 같다.&lt;/blockquote&gt;</description>
      <category>Google Adsense</category>
      <category>광고 경험 보고서</category>
      <category>광고 환경 보고서</category>
      <category>더 나은 광고 표준</category>
      <category>블로그</category>
      <category>수익</category>
      <category>애드센스</category>
      <category>애드센스 경고</category>
      <category>티스토리</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/255</guid>
      <comments>https://geonlee.tistory.com/255#entry255comment</comments>
      <pubDate>Thu, 21 Oct 2021 17:29:52 +0900</pubDate>
    </item>
    <item>
      <title>트위터 클론 코딩 #3 Nweeting</title>
      <link>https://geonlee.tistory.com/254</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1xKeX/btrgACJKtkd/Z3tjeRKjRsK9V7Bm6VXlR0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1xKeX/btrgACJKtkd/Z3tjeRKjRsK9V7Bm6VXlR0/img.jpg&quot; data-alt=&quot;노마드 코더 트위터 클론 코딩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1xKeX/btrgACJKtkd/Z3tjeRKjRsK9V7Bm6VXlR0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1xKeX%2FbtrgACJKtkd%2FZ3tjeRKjRsK9V7Bm6VXlR0%2Fimg.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&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;figcaption&gt;노마드 코더 트위터 클론 코딩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노마드 코더의 &lt;a href=&quot;https://nomadcoders.co/nwitter&quot;&gt;트위터 클론 코딩 강의&lt;/a&gt;를 듣고 정리한 강의노트입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3 NWEETING&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;3.0 Form and Database Setup / 양식과 데이터베이스 설정&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase &amp;gt; Firestore Database&lt;/p&gt;
&lt;h1&gt;3.1 Nweeting! / 느위팅!&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase의 Cloude Database(Firestore Database)는 NoSQL database이기 때문에 유연하고 사용하기 쉽다. NoSQL database에는 다음과 같은 분류로 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Collection : 폴더와 같은 역할, 한 데이터베이스에는 여러개의 Collection이 존재할 수 있다.&lt;/li&gt;
&lt;li&gt;Document : 폴더 안에 있는 문서와 같은 역할, 한 Collection에는 여러 개의 Document가 존재할 수 있다.&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;Document &amp;sub; Collection &amp;sub; Database&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase v9.0으로 바뀌면서 firebaseConfig의 값이 달라졌다. 혹시 오류가 생긴다면 .env의 값이 현재 firebaseConfig에서 원하는 값과 같은 지 확인해보자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;// Home.jsx
import React, { useState } from 'react'
import { db } from 'fbase'
import { collection, addDoc, serverTimestamp } from '@firebase/firestore'

const Home = () =&amp;gt; {
  const [nweet, setNweet] = useState('')

  const handleOnSubmit = async (e) =&amp;gt; {
    e.preventDefault()

    const docRef = await addDoc(collection(db, 'nweets'), {
      nweet,
      createdAt: serverTimestamp(),
    })

    console.log('Document written with ID: ', docRef.id)

    setNweet('')
  }

  const handleOnChange = (e) =&amp;gt; {
    const {
      target: { value },
    } = e
    setNweet(value)
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form onSubmit={handleOnSubmit}&amp;gt;
        &amp;lt;input type=&quot;text&quot; placeholder=&quot;What's on your mind?&quot; maxLength={120} onChange={handleOnChange} value={nweet} /&amp;gt;
        &amp;lt;input type=&quot;submit&quot; value=&quot;Nweet&quot; /&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
export default Home&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;3.2 Getting the Nweets / 느윗 가져오기&lt;/h1&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;// Home.jsx
import React, { useState, useEffect } from 'react'
import { db } from 'fbase'
import { collection, addDoc, getDocs, serverTimestamp } from '@firebase/firestore'

const Home = () =&amp;gt; {
  const [nweet, setNweet] = useState('')
  const [nweets, setNweets] = useState([])

  useEffect(() =&amp;gt; {
    const getNweets = async () =&amp;gt; {
      const dbNweets = await getDocs(collection(db, 'nweets'))
      dbNweets.forEach((document) =&amp;gt; {
        const nweetObject = {
          ...document.data(),
          id: document.id,
        }
        setNweets((prev) =&amp;gt; [nweetObject, ...prev])
      })
      // setNweets(dbNweets.map((document) =&amp;gt; document.data()))
    }
    getNweets()
  }, [])

  const handleOnSubmit = async (e) =&amp;gt; {
    e.preventDefault()

    const docRef = await addDoc(collection(db, 'nweets'), {
      nweet,
      createdAt: serverTimestamp(),
    })

    console.log('Document written with ID: ', docRef.id)

    setNweet('')
  }

  const handleOnChange = (e) =&amp;gt; {
    const {
      target: { value },
    } = e
    setNweet(value)
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form onSubmit={handleOnSubmit}&amp;gt;
        &amp;lt;input type=&quot;text&quot; placeholder=&quot;What's on your mind?&quot; maxLength={120} onChange={handleOnChange} value={nweet} /&amp;gt;
        &amp;lt;input type=&quot;submit&quot; value=&quot;Nweet&quot; /&amp;gt;
      &amp;lt;/form&amp;gt;
      &amp;lt;div&amp;gt;
        {nweets.map(({ id, nweet }) =&amp;gt; (
          &amp;lt;div key={id}&amp;gt;
            &amp;lt;h4&amp;gt;{nweet}&amp;lt;/h4&amp;gt;
          &amp;lt;/div&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
export default Home&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;3.3 Realtime Nweets / 실시간 느윗&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 구현된 건 새로고침을 해야 다시 DB에 있는 정보를 가져와서 업데이트하는 방식, 하지만 FireStore Database는 Realtime Database이기 때문에 해당 이점을 살리기 위해 리얼 타임을 구현&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;// Home.jsx
import React, { useState, useEffect } from 'react'
import { db } from 'fbase'
import { collection, addDoc, query, onSnapshot, orderBy, serverTimestamp } from '@firebase/firestore'

const Home = ({ user }) =&amp;gt; {
  const [nweet, setNweet] = useState('')
  const [nweets, setNweets] = useState([])

  useEffect(() =&amp;gt; {
    // 실시간으로 데이터를 데이터베이스에서 가져오기

    const q = query(collection(db, 'nweets'), orderBy('createdAt', 'desc'))
    const unsubscribe = onSnapshot(q, (querySnapshot) =&amp;gt; {
      const nextNweets = querySnapshot.docs.map((doc) =&amp;gt; {
        return {
          id: doc.id,
          ...doc.data(),
        }
      })
      setNweets(nextNweets)
    })

    return () =&amp;gt; {
      unsubscribe()
    }
  }, [])

  const handleOnSubmit = async (e) =&amp;gt; {
    e.preventDefault()

    const docRef = await addDoc(collection(db, 'nweets'), {
      text: nweet,
      createdAt: serverTimestamp(),
      creatorId: user.uid,
    })

    console.log('Document written with ID: ', docRef.id)

    setNweet('')
  }

  const handleOnChange = (e) =&amp;gt; {
    const {
      target: { value },
    } = e
    setNweet(value)
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form onSubmit={handleOnSubmit}&amp;gt;
        &amp;lt;input type=&quot;text&quot; placeholder=&quot;What's on your mind?&quot; maxLength={120} onChange={handleOnChange} value={nweet} /&amp;gt;
        &amp;lt;input type=&quot;submit&quot; value=&quot;Nweet&quot; /&amp;gt;
      &amp;lt;/form&amp;gt;
      &amp;lt;div&amp;gt;
        {nweets.map(({ id, text }) =&amp;gt; (
          &amp;lt;div key={id}&amp;gt;
            &amp;lt;h4&amp;gt;{text}&amp;lt;/h4&amp;gt;
          &amp;lt;/div&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
export default Home&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;3.4 Delete and Update / 삭제와 업데이트&lt;/h1&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// Nweet.jsx
import React, { useState } from 'react'
import { updateDoc, deleteDoc, doc } from '@firebase/firestore'
import { db } from 'fbase'

const Nweet = ({ nweet: { id, text }, isOwner }) =&amp;gt; {
  const [editing, setEditing] = useState(false)
  const [newNweet, setNewNweet] = useState(text)

  const handleOnClickDelete = async () =&amp;gt; {
    const ok = window.confirm('Are you sure you want to delete this nweet?')
    if (ok) {
      await deleteDoc(doc(db, `nweets/${id}`))
    }
  }

  const toggleEditing = () =&amp;gt; {
    setEditing((prevEditing) =&amp;gt; !prevEditing)
  }

  const handleOnChange = (e) =&amp;gt; {
    const {
      target: { value },
    } = e
    setNewNweet(value)
  }

  const handleOnSubmit = async (e) =&amp;gt; {
    e.preventDefault()
    await updateDoc(doc(db, `nweets/${id}`), {
      text: newNweet,
    })
    setEditing(false)
  }

  return (
    &amp;lt;div&amp;gt;
      {editing ? (
        &amp;lt;&amp;gt;
          &amp;lt;form onSubmit={handleOnSubmit}&amp;gt;
            &amp;lt;input type=&quot;text&quot; placeholder=&quot;Edit your nweet&quot; value={newNweet} required onChange={handleOnChange} /&amp;gt;
            &amp;lt;input type=&quot;submit&quot; value=&quot;Update Nweet&quot; /&amp;gt;
          &amp;lt;/form&amp;gt;
          &amp;lt;button onClick={toggleEditing}&amp;gt;Cancel&amp;lt;/button&amp;gt;
        &amp;lt;/&amp;gt;
      ) : (
        &amp;lt;h4&amp;gt;{text}&amp;lt;/h4&amp;gt;
      )}
      {isOwner &amp;amp;&amp;amp; (
        &amp;lt;&amp;gt;
          &amp;lt;button onClick={handleOnClickDelete}&amp;gt;Delete Nweet&amp;lt;/button&amp;gt;
          &amp;lt;button onClick={toggleEditing}&amp;gt;Edit Nweet&amp;lt;/button&amp;gt;
        &amp;lt;/&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  )
}

export default Nweet&lt;/code&gt;&lt;/pre&gt;</description>
      <category>노마드코더/트위터 클론코딩</category>
      <category>Firebase</category>
      <category>react</category>
      <category>노마드코더</category>
      <category>리액트</category>
      <category>코딩</category>
      <category>클론</category>
      <category>클론코딩</category>
      <category>트위터</category>
      <category>파이어베이스</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/254</guid>
      <comments>https://geonlee.tistory.com/254#entry254comment</comments>
      <pubDate>Fri, 1 Oct 2021 15:39:36 +0900</pubDate>
    </item>
    <item>
      <title>트위터 클론 코딩 #2 Authentication</title>
      <link>https://geonlee.tistory.com/253</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c67ufl/btrgxCXMivx/KFostRNlk6gJM82KfnQ4XK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c67ufl/btrgxCXMivx/KFostRNlk6gJM82KfnQ4XK/img.jpg&quot; data-alt=&quot;노마드 코더 트위터 클론 코딩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c67ufl/btrgxCXMivx/KFostRNlk6gJM82KfnQ4XK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc67ufl%2FbtrgxCXMivx%2FKFostRNlk6gJM82KfnQ4XK%2Fimg.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&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;figcaption&gt;노마드 코더 트위터 클론 코딩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노마드 코더의 &lt;a href=&quot;https://nomadcoders.co/nwitter&quot;&gt;트위터 클론 코딩 강의&lt;/a&gt;를 듣고 정리한 강의노트입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2.0 Using Firebase Auth / Firebase 인증 사용하기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;특정 폴더 기준으로 상대 경로로 import 하는 방법&lt;/h2&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;src&quot;
  },
  &quot;include&quot;: [&quot;src&quot;]
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://create-react-app.dev/docs/importing-a-component/#absolute-imports&quot;&gt;CRA Absolute Imports&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Firebase 설정 수정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 관련 값을 가져오기 위해 Firebase 설정 수정&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ 강의 영상 이후로 firebase가 v9.0으로 업데이트하면서 기존 코드와 호환이 안되는 경우가 있어서 v9.0를 사용하는 경우 아래 코드로 진행&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// Import the functions you need from the SDKs you need
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'

// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_APP_AUTH_DOMAIN,
  databaseURL: process.env.REACT_APP_DATABASE_URL,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGIN_ID,
  appId: process.env.REACT_APP_APP_ID,
}

// Initialize Firebase
const app = initializeApp(firebaseConfig)
export const authService = getAuth()&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;2.1 Login Form part One / 로그인 양식 첫번째&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase 인증에서 로그인 제공 업체를 다음과 같이 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github의 경우 로그인 후 &lt;a href=&quot;https://github.com/settings/developers&quot;&gt;Developer settings&lt;/a&gt;에 들어가서 &lt;code&gt;OAuth Application&lt;/code&gt;을 생성하여 &lt;code&gt;client id&lt;/code&gt;와 &lt;code&gt;secret key&lt;/code&gt;를 넣어야 함&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;로그인 양식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 &lt;code&gt;onChange&lt;/code&gt; 이벤트 핸들러를 이용해서 &lt;code&gt;email&lt;/code&gt;과 &lt;code&gt;password&lt;/code&gt; 입력을 다룰 수 있도록 코드를 구성&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import React, { useState } from 'react'

const Auth = () =&amp;gt; {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')

  const handleOnChange = (e) =&amp;gt; {
    const {
      target: { name, value },
    } = e

    console.log(name)

    if (name === 'email') {
      setEmail(value)
    } else if (name === 'password') {
      setPassword(value)
    }
  }

  const handleOnSubmit = (e) =&amp;gt; {
    e.preventDefault();
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form onSubmit={handleOnSubmit}&amp;gt;
        &amp;lt;input name=&quot;email&quot; type=&quot;text&quot; placeholder=&quot;Email&quot; required value={email} onChange={handleOnChange} /&amp;gt;
        &amp;lt;input
          name=&quot;password&quot;
          type=&quot;password&quot;
          placeholder=&quot;Password&quot;
          required
          value={password}
          onChange={handleOnChange}
        /&amp;gt;
        &amp;lt;input type=&quot;sumbit&quot; valeu=&quot;Log In&quot; /&amp;gt;
      &amp;lt;/form&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;button&amp;gt;Continue with Google&amp;lt;/button&amp;gt;
        &amp;lt;button&amp;gt;Continue with Github&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default Auth&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;2.2 Recap / 복습하기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;onSubmit에서 preventDefault를 하는 이유?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 &lt;code&gt;type=submit&lt;/code&gt;의 &lt;code&gt;input&lt;/code&gt; 요소는 클릭 시 값에 해당하는 쿼리 스트링을 만들어서 해당 URL로 이동하는 동작을 한다. 하지만, SPA의 작동 방식과는 어울리지 않아서 이러한 기본 동작을 막는 것이 필요한데 그 때 필요한 것이 &lt;code&gt;preventDefault&lt;/code&gt;로 특정 이벤트의 기본 동작을 막아주는 역할을 한다.&lt;/p&gt;
&lt;h1&gt;2.3 Creating Account / 계정 생성하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;firebase.auth.EmailAuthProvider&lt;/code&gt;를 사용할 예정&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ 강의 코드와 달리 v9.0 버전에 맞춰서 수정해야할 부분이 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import { authService } from 'fbase'
import React, { useState } from 'react'
import { createUserWithEmailAndPassword, signInWithEmailAndPassword } from 'firebase/auth';

const Auth = () =&amp;gt; {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [newAccount, setNewAccount] = useState(true)

  const handleOnChange = (e) =&amp;gt; {
    const {
      target: { name, value },
    } = e

    if (name === 'email') {
      setEmail(value)
    } else if (name === 'password') {
      setPassword(value)
    }
  }

  const handleOnSubmit = async (e) =&amp;gt; {
    e.preventDefault();
    try{
      let data;
      if(newAccount){
        data = await createUserWithEmailAndPassword(authService, email, password)
      } else {
        data = await signInWithEmailAndPassword(authService, email, password)
      }
      console.log(data);
    } catch(error){
      console.error(error)
    }
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form onSubmit={handleOnSubmit}&amp;gt;
        &amp;lt;input name=&quot;email&quot; type=&quot;text&quot; placeholder=&quot;Email&quot; required value={email} onChange={handleOnChange} /&amp;gt;
        &amp;lt;input
          name=&quot;password&quot;
          type=&quot;password&quot;
          placeholder=&quot;Password&quot;
          required
          value={password}
          onChange={handleOnChange}
        /&amp;gt;
        &amp;lt;input type=&quot;submit&quot; value={newAccount ? &quot;Create Account&quot; : &quot;Log In&quot;} /&amp;gt;
      &amp;lt;/form&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;button&amp;gt;Continue with Google&amp;lt;/button&amp;gt;
        &amp;lt;button&amp;gt;Continue with Github&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default Auth&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Create Account&quot;를 하게 되면, 계정이 생성되고 해당 계정으로 로그인 상태가 된다. (구글 크롬 &amp;gt; Application 탭 &amp;gt; IndexedDB &amp;gt; firebase~ 에서 확인 가능)&lt;/p&gt;
&lt;h1&gt;2.4 Log In / 로그인&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 회원가입을 할 것인지 로그인을 할 것인지 토글하는 버튼 추가&lt;/p&gt;
&lt;h1&gt;2.5 Social Login / 소셜 로그인&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ Firebase v9.0 코드&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import { auth } from 'fbase'
import React, { useState } from 'react'
import {
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  GoogleAuthProvider,
  GithubAuthProvider,
  signInWithPopup,
} from '@firebase/auth'

const Auth = () =&amp;gt; {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [newAccount, setNewAccount] = useState(true)
  const [error, setError] = useState('')

  const handleOnChange = (e) =&amp;gt; {
    const {
      target: { name, value },
    } = e

    if (name === 'email') {
      setEmail(value)
    } else if (name === 'password') {
      setPassword(value)
    }
  }

  const handleOnSubmit = async (e) =&amp;gt; {
    e.preventDefault()
    try {
      let data
      if (newAccount) {
        data = await createUserWithEmailAndPassword(auth, email, password)
      } else {
        data = await signInWithEmailAndPassword(auth, email, password)
      }
      console.log(data)
    } catch (error) {
      setError(error.message)
    }
  }

  const toggleAccount = () =&amp;gt; {
    setNewAccount((prev) =&amp;gt; !prev)
  }

  const handleOnSocialClick = async (e) =&amp;gt; {
    const {
      target: { name },
    } = e

    let provider
    if (name === 'google') {
      provider = new GoogleAuthProvider()
    } else if (name === 'github') {
      provider = new GithubAuthProvider()
    }
    const data = await signInWithPopup(auth, provider)
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;form onSubmit={handleOnSubmit}&amp;gt;
        &amp;lt;input name=&quot;email&quot; type=&quot;text&quot; placeholder=&quot;Email&quot; required value={email} onChange={handleOnChange} /&amp;gt;
        &amp;lt;input
          name=&quot;password&quot;
          type=&quot;password&quot;
          placeholder=&quot;Password&quot;
          required
          value={password}
          onChange={handleOnChange}
        /&amp;gt;
        &amp;lt;input type=&quot;submit&quot; value={newAccount ? 'Create Account' : 'Sign In'} /&amp;gt;
        {error}
      &amp;lt;/form&amp;gt;
      &amp;lt;span onClick={toggleAccount}&amp;gt;{newAccount ? 'Sign In' : 'Create Account'}&amp;lt;/span&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;button name=&quot;google&quot; onClick={handleOnSocialClick}&amp;gt;
          Continue with Google
        &amp;lt;/button&amp;gt;
        &amp;lt;button name=&quot;github&quot; onClick={handleOnSocialClick}&amp;gt;
          Continue with Github
        &amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default Auth&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;2.6 Log Out / 로그아웃&lt;/h1&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { auth } from 'fbase'
import React from 'react'
import { useHistory } from 'react-router'

const Profile = () =&amp;gt; {
  const history = useHistory()

  const handleOnLogOutClick = () =&amp;gt; {
    auth.signOut()
    history.push('/')
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;button onClick={handleOnLogOutClick}&amp;gt;Log Out&amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  )
}
export default Profile&lt;/code&gt;&lt;/pre&gt;</description>
      <category>노마드코더/트위터 클론코딩</category>
      <category>Firebase</category>
      <category>react</category>
      <category>노마드코더</category>
      <category>리액트</category>
      <category>코딩</category>
      <category>클론코딩</category>
      <category>트위터</category>
      <category>파이어베이스</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/253</guid>
      <comments>https://geonlee.tistory.com/253#entry253comment</comments>
      <pubDate>Fri, 1 Oct 2021 15:35:30 +0900</pubDate>
    </item>
    <item>
      <title>어떻게하면 구글링을 잘 할까?</title>
      <link>https://geonlee.tistory.com/252</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 글은 &lt;a href=&quot;https://www.youtube.com/watch?v=Y8bH6lGZD1I&amp;amp;ab_channel=%EC%BD%94%EB%94%A9%EC%95%A0%ED%94%8C&quot;&gt;코딩애플 - 구글링 잘하는 법 6개 (네이버쓰셈 ㅅㄱ)&lt;/a&gt; 을 보고 해당 영상 내용을 정리 및 제 생각을 추가한 글입니다. 코딩애플님께서도 재미있게 잘 소개해주셨으니 영상을 보는 것도 추천드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온전히 &lt;b&gt;구글&lt;/b&gt;만 사용하는 것이 아니기 때문에&amp;nbsp;&lt;b&gt;&quot;웹사이트를 통한 검색&quot;&lt;/b&gt;이 좀 더 명확하지만, 글에서는 좀 더 편한 용어인 &lt;b&gt;&quot;구글링&quot;&lt;/b&gt;이라는 용어를 사용하려합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;922&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vSVJ3/btrgpMfIx6M/xnF8AS81ZoOuVcWRkrjwKK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vSVJ3/btrgpMfIx6M/xnF8AS81ZoOuVcWRkrjwKK/img.jpg&quot; data-alt=&quot;구글링&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vSVJ3/btrgpMfIx6M/xnF8AS81ZoOuVcWRkrjwKK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvSVJ3%2FbtrgpMfIx6M%2FxnF8AS81ZoOuVcWRkrjwKK%2Fimg.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;922&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;figcaption&gt;구글링&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;  구글링은 왜하는 걸까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 개발 초보인 경우, 심지어는 고수일지라도 구글링 없이 문제를 해결할 수는 없다. 특히 개발 분야는 특정 기술에 대해 모든 것을 알 수 없고, 정확히 코드화되어있기 때문에 생각을 통해 뇌피셜로 결론을 낼 수 없는 문제이기 때문이다.&lt;/p&gt;
&lt;h1&gt; &amp;zwj;  구글링의 차이 = 개발 실력 성장 속도의 차이&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;357&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3Zr6U/btrgvVBX1aI/9qB86CWKg6R2zs6Pu1Qazk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3Zr6U/btrgvVBX1aI/9qB86CWKg6R2zs6Pu1Qazk/img.gif&quot; data-alt=&quot;구글링의 차이가 개발 실력 성장 속도의 차이를 만든다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3Zr6U/btrgvVBX1aI/9qB86CWKg6R2zs6Pu1Qazk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b3Zr6U/btrgvVBX1aI/9qB86CWKg6R2zs6Pu1Qazk/img.gif&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;357&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;figcaption&gt;구글링의 차이가 개발 실력 성장 속도의 차이를 만든다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 같은 구글링을 할 지라도, 개발 초보와 고수가 구글링을 통해 원하는 결과를 찾기까지의 시간은 엄청난 차이가 나게 된다. 그리고 이 시간의 차이가 개발 실력 성장 속도를 좌우하기 때문에 빠른 성장을 위해서라면 구글링을 잘하는 건 필수적이다.&lt;/p&gt;
&lt;h1&gt;  어떻게하면 구글링을 잘 할까?&lt;/h1&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;말 그대로 내가 무엇을 모르는 지 몰라서 뭘 검색해야 할 지 모르는 상태에 좋은 구글링 팁이다. 개발을 하면서 모르는 것에 대한 분류는 세가지 정도로 나뉘어지는데 각각에 해당하는 구글링 방법들을 알아보자.&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 style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;언어&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;} ${&lt;/span&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;문법&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 자바스크립트의 for 문법에 대해서 모른다면, &lt;b&gt;&quot;javascript for&quot;&lt;/b&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;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;라이브러리&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;} ${&lt;/span&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;문법&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 리액트의 useState 문법에 대해 모른다면, &lt;b&gt;&quot;react usestate&quot;&lt;/b&gt;를 구글링하면 된다. 라이브러리의 경우 검색했을 때, 검색 결과는 크게 2가지로 나올 것이다. 하나는 공식 API 스펙문서(Docs)일 것이고, 다른 하나는 블로그에 해당 스펙에 정리해놓은 글일 것이다. 초반에 공식 API 스펙문서를 읽고 이해하는 건 어렵기 때문에, 배경지식이 없으면 없을 수록 블로그에 쉽게 쓰여진 글을 읽는 것을 추천한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전반적인 만드는 법을 모른다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;b&gt;how to ~&quot;&lt;/b&gt;같은 문장을 만들어서 검색해도 되지만 더 쉽게 &lt;b&gt;&quot;tutorial&quot;&lt;/b&gt; 같은 단어를 추가해서 검색하는 걸 추천한다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;언어&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;} ${&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;만들고자하는 대상&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;} tutorial&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 자바스크립트로 Todo List를 만드는 법을 알고 싶다면, &lt;b&gt;&quot;javascript todo tutorial&quot;&lt;/b&gt;를 구글링하면 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;영어를 쓰자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 보면 당연한 이야기이겠지만, 모든 개발언어들이 영어 기반이기도 하고, 영어 사용자도 더 많기 때문에 한국어를 이용해서 구글링 했을 때보다, 영어를 이용해서 구글링했을 때 얻는 정보의 양이 더 많을 것이다. 또한 한글로 된 자료들은 실제 정보 전달을 위한 글들도 있지만, 대부분이 본인이 공부한 내용을 정리한 글 혹은 학습을 위해 영어로 된 글이나 문서를 번역한 글들이다. 기존에 영어로 된 내용을 한국어 바꾼 글들의 경우 번역의 과정을 거치다보니 글이 잘 읽히지도 않아서 오히려 이해를 방해할 것이다. 그러니 파파고의 힘을 빌려서라도 최대한 영어로 구글링해보자. 추가적으로 영어를 사용해서 구글링할 시 굳이 소문자/대문자를 지키지 않아도 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;166&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbtL1A/btrgwDA5Etn/clmVk7cRenl0L9sgZnFj7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbtL1A/btrgwDA5Etn/clmVk7cRenl0L9sgZnFj7K/img.png&quot; data-alt=&quot;영어가 안되면~ 파파고~&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbtL1A/btrgwDA5Etn/clmVk7cRenl0L9sgZnFj7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbtL1A%2FbtrgwDA5Etn%2FclmVk7cRenl0L9sgZnFj7K%2Fimg.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;166&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;figcaption&gt;영어가 안되면~ 파파고~&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구글 검색 명령어 사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 필터링된 결과를 얻고싶은 경우에는 &lt;a href=&quot;https://support.google.com/websearch/answer/2466433?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;구글 검색 명령어&lt;/a&gt;을 이용해서 검색을 할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특정 키워드가 꼭 필요하다면, &quot;키워드&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 2개 이상의 키워드를 검색하는 경우 검색 결과는 두 키워드의 OR에 해당하는 결과가 나오게된다. 이 경우 특정 키워드가 꼭 포함되길 원한다면 &lt;b&gt;따옴표&lt;/b&gt;을 사용하자. 따옴표 안에 특정 키워드를 적으면, 따옴표 안에 적은 키워드를 포함한 문서만 나오게 된다. 예시는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;javascript &lt;b&gt;&quot;type error&quot;&lt;/b&gt; : type error 키워드가 반드시 포함된 javascript type error에 대한 문서&lt;/li&gt;
&lt;li&gt;next.js &lt;b&gt;&quot;ssr&quot;&lt;/b&gt; : ssr 키워드가 반드시 포함된 next.js ssr에 대한 문서&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘은 구글 AI가 따옴표를 무시하는 경우도 있다고한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최신 문서를 봐야한다면, after:날짜&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 개발 특히 프론트엔드 개발의 경우 새로운 기술이 날로날로 나오고 스펙도 해가 다르게 바뀌어간다. 그러다보니 생각없이 구글링하다보면 폐기된 스펙을 보고 따라서 코드를 작성하다가 나중에 되어서야 스펙아웃되었다는 걸 알아채는 경우도 있다. 이 경우 &lt;b&gt;after&lt;/b&gt;를 사용해서 구글링을 해보자. 검색어를 입력 후 &lt;b&gt;after:&lt;/b&gt; 뒤에 시작 날짜를 입력하면 된다. 예시는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;react pwa &lt;b&gt;after:2020&lt;/b&gt; : 2021년 1월 1일 이후의 react pwa에 대한 문서&lt;/li&gt;
&lt;li&gt;vue.js &lt;b&gt;after:2020.1.1&lt;/b&gt; : 2020년 1월 1일 이후의 vue.js에 대한 문서&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;b&gt;-&lt;/b&gt;를 사용하자. &lt;b&gt;-&lt;/b&gt;를 사용하게되면, 해당 키워드를 포함하지 않은 문서만 나오게 된다. 예시는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;react state management &lt;b&gt;-redux&lt;/b&gt; : redux 키워드가 포함된 문서를 제외한 react state management에 대한 문서&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;b&gt;*&lt;/b&gt;를 사용하자. &lt;b&gt;*&lt;/b&gt;를 사용하게 되면, * 위치에 구글에서 알아서 &lt;b&gt;*&lt;/b&gt; 자리를 다른 키워드로 채워서 보여준다. 예시는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;how to do &lt;b&gt;*&lt;/b&gt; in python : &lt;b&gt;*&lt;/b&gt; 자리를 다른 키워드로 채운 문서&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;844&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnKfeu/btrgqFHU8M0/Hkc6rlcYQNQAuS52rMXc01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnKfeu/btrgqFHU8M0/Hkc6rlcYQNQAuS52rMXc01/img.png&quot; data-alt=&quot;how to make * in react 에 대한 구글 검색 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnKfeu/btrgqFHU8M0/Hkc6rlcYQNQAuS52rMXc01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnKfeu%2FbtrgqFHU8M0%2FHkc6rlcYQNQAuS52rMXc01%2Fimg.png&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;844&quot; data-filename=&quot;blob&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;figcaption&gt;how to make * in react 에 대한 구글 검색 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  유튜브 동영상 검색&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글 검색을 했을 때 원하는 결과가 나오지 않았다면, 유튜브 비디오 검색을 통해 원하는 정보를 얻을 수도 있다. 신기한 기능인데, 구글에서 유튜브 영상의 음성을 분석하여 해당 키워드가 영상에 등장한다면 해당 영상을 유튜브 비디오 검색 결과에 보여준다. 추가적으로 필자는 글보다 유튜브 영상을 선호하는 편인데, 특히 튜토리얼 같은 내용들은 글보다 영상을 통해 정보를 얻는게 더 효과적이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;788&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q7iBs/btrgu9HXuwy/gJeO7dS3gDKlq47cbd1vR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q7iBs/btrgu9HXuwy/gJeO7dS3gDKlq47cbd1vR0/img.png&quot; data-alt=&quot;리액트와 뷰에 대한 구글의 유튜브 동영상 검색 결과&amp;amp;amp;nbsp;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q7iBs/btrgu9HXuwy/gJeO7dS3gDKlq47cbd1vR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq7iBs%2Fbtrgu9HXuwy%2FgJeO7dS3gDKlq47cbd1vR0%2Fimg.png&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;788&quot; data-filename=&quot;blob&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;figcaption&gt;리액트와 뷰에 대한 구글의 유튜브 동영상 검색 결과&amp;nbsp;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  라이브러리 에러는 Github 저장소 검색&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글링을 통해 라이브러리의 에러를 해결할 수 없다면 해당 라이브러리의 Github 저장소에 들어가서 이슈를 검색해보는 것이 더 좋을 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  최신 업데이트 소식은 Reddit 검색&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 기술의 최신 업데이트 소식을 딱딱하지 않게 알고 싶다면 해당 기술에 해당하는 서브 레딧에 들어가서 검색을 하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;893&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8wbpW/btrgx0C6EWQ/x3LQcGA1kAk7i7BAKWgqq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8wbpW/btrgx0C6EWQ/x3LQcGA1kAk7i7BAKWgqq0/img.png&quot; data-alt=&quot;React.js 서브 레딧&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8wbpW/btrgx0C6EWQ/x3LQcGA1kAk7i7BAKWgqq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8wbpW%2Fbtrgx0C6EWQ%2Fx3LQcGA1kAk7i7BAKWgqq0%2Fimg.png&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;893&quot; data-filename=&quot;blob&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;figcaption&gt;React.js 서브 레딧&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;네이버 검색&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발하면서 가장 첫번째 듣는 말이 네이버로 개발 검색하지 말라는 말이다 근데 네이버 검색을 사용하라는 건 뭘까? 네이버를 검색 엔진 사이트가 아니라 포털 사이트라는 점을 인지하고 사용해보자. 네이버의 서비스 중 주로 네이버 카페를 이용하는데 특정 기술을 주제로 모여있는 카페들을 찾아서 해당 카페에 질문을 올리는 것이다. 이 방법의 최고 장점은 한국어 사용자와 이야기할 수 있다는 점이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;519&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIHwYx/btrgpv5N0Qh/5swsqU33FLRba8mRk8yHEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIHwYx/btrgpv5N0Qh/5swsqU33FLRba8mRk8yHEk/img.png&quot; data-alt=&quot;웹 개발과 관련된 네이버 카페&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIHwYx/btrgpv5N0Qh/5swsqU33FLRba8mRk8yHEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIHwYx%2Fbtrgpv5N0Qh%2F5swsqU33FLRba8mRk8yHEk%2Fimg.png&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;519&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;figcaption&gt;웹 개발과 관련된 네이버 카페&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Devlog</category>
      <category>검색</category>
      <category>구글</category>
      <category>구글링</category>
      <category>네이버</category>
      <category>레딧</category>
      <category>유튜브</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/252</guid>
      <comments>https://geonlee.tistory.com/252#entry252comment</comments>
      <pubDate>Thu, 30 Sep 2021 18:56:49 +0900</pubDate>
    </item>
    <item>
      <title>티스토리 블로그에 TOC 추가하기</title>
      <link>https://geonlee.tistory.com/251</link>
      <description>&lt;p&gt;마크다운으로 글을 작성하다보면 글이 계층화되어진다. 제목의 크기로 계층을 확인할 수 있지만, 좀 더 가시적으로 보여줄 무언가가 필요했고, TOC를 적용하기로 결정했다.&lt;/p&gt;
&lt;h1&gt;TOC란?&lt;/h1&gt;
&lt;p&gt;TOC는 목차(&lt;strong&gt;T&lt;/strong&gt;able &lt;strong&gt;O&lt;/strong&gt;f &lt;strong&gt;C&lt;/strong&gt;ontent)라는 뜻으로 웹 문서에서는 헤더 태그들을 모아서 계증화하여 보여주고, 클릭 시 해당 헤더 태그로 자동으로 이동해주는 역할을 하는 부분을 뜻한다. TOC를 아주 잘 사용한 사이트로는 나무위키가 있겠다. 나무위키는 설명하고자하는 내용이 계층화되어 있어서 이를 자동으로 목차화해준다. 그리고 위에 언급한 것처럼 클릭시 해당 헤더 태그로 이동하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;880&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IDCM2/btrgjF8TKns/tK328XRk0JOkC8AXDcl1f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IDCM2/btrgjF8TKns/tK328XRk0JOkC8AXDcl1f0/img.png&quot; data-alt=&quot;쿠키런: 킹덤 나무위키 목차&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IDCM2/btrgjF8TKns/tK328XRk0JOkC8AXDcl1f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIDCM2%2FbtrgjF8TKns%2FtK328XRk0JOkC8AXDcl1f0%2Fimg.png&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;880&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;figcaption&gt;쿠키런: 킹덤 나무위키 목차&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;TOC 적용하기&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;아래 소개할 적용 방식은 프라치노 공간 스킨으로 진행한 내용으로, 본인의 스킨에 따라 다르게 적용해야할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Tocbot&lt;/h2&gt;
&lt;p&gt;TOC 관련 스크립트를 모두 짜는 건 어렵기 때문에 &lt;a href=&quot;https://tscanlin.github.io/tocbot/&quot;&gt;Tocbot&lt;/a&gt;이라는 npm 라이브러리를 이용한다.&lt;/p&gt;
&lt;h3&gt;Tocbot 스크립트, CSS 가져오기&lt;/h3&gt;
&lt;p&gt;우선 Tocbot의 라이브러리 스크립트와 CSS를 가져오는 코드를 head 태그 안에 넣어준다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!-- tocbot --&amp;gt;
&amp;lt;script src=&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.4.2/tocbot.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.4.2/tocbot.css&amp;quot;&amp;gt;
&amp;lt;!-- tocbot --&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;TOC가 들어갈 위치 정하기&lt;/h3&gt;
&lt;p&gt;CSS에서 상대위치를 사용하기 때문에 티스토리 글의 본문이 시작하는 부분에 TOC가 들어가는 것이 제일 좋다. 본문이 시작하는 부분은 티스토리 스킨마다 다른데 &lt;code&gt;s_permalink_article_rep&lt;/code&gt; 라는 태그를 모두 찾아서 해당 태그 안의 첫번째 위치에 다음 태그를 넣었다. 제대로 안된다면 넣은 태그를 하나씩 지워가면서 확인해보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&amp;#39;toc&amp;#39;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;88&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Efgiz/btrgqGkx2gD/ilOEyR5dOmpd2TzeUA0uK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Efgiz/btrgqGkx2gD/ilOEyR5dOmpd2TzeUA0uK0/img.png&quot; data-alt=&quot;TOC의 위치&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Efgiz/btrgqGkx2gD/ilOEyR5dOmpd2TzeUA0uK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEfgiz%2FbtrgqGkx2gD%2FilOEyR5dOmpd2TzeUA0uK0%2Fimg.png&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;88&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;figcaption&gt;TOC의 위치&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;태그는 꼭 여는 태그와 닫는 태그 둘 다 작성해야한다. 다음과 같이 썼을 때는 원하는 대로 나오지 않는 오류가 발생했다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&amp;#39;toc&amp;#39;/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;TOC 스크립트&lt;/h3&gt;
&lt;p&gt;body 태그 안의 마지막에 들어가는 Tocbot 스크립트이다. 코드에서 &lt;strong&gt;#article&lt;/strong&gt; 에 해당하는 부분은 article 태그의 &lt;strong&gt;class&lt;/strong&gt; 혹은 &lt;strong&gt;id&lt;/strong&gt;에 따라 달라지기 때문에 티스토리 스킨의 article 태그에 맞게 수정해서 사용해야한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script&amp;gt;
  var content = document.querySelector(&amp;#39;#article&amp;#39;)
  var headings = content.querySelectorAll(&amp;#39;h1, h2, h3, h4, h5, h6, h7&amp;#39;)
  var headingMap = {}

  Array.prototype.forEach.call(headings, function (heading) {
      var id = heading.id ? heading.id : heading.textContent.trim().toLowerCase()
                 .split(&amp;#39; &amp;#39;).join(&amp;#39;-&amp;#39;).replace(/[\!\@\#\$\%\^\&amp;amp;\*\(\):]/ig, &amp;#39;&amp;#39;)
      headingMap[id] = !isNaN(headingMap[id]) ? ++headingMap[id] : 0
      if (headingMap[id]) {
        heading.id = id + &amp;#39;-&amp;#39; + headingMap[id]
      } else {
        heading.id = id
      }
    })

  tocbot.init({
    tocSelector: &amp;#39;.toc&amp;#39;,
    contentSelector: &amp;#39;#article&amp;#39;,
    headingSelector:&amp;#39;h1, h2, h3&amp;#39;,
    hasInnerContainers: false
  });

  $(document).ready(function(){
            $(&amp;#39;.toc&amp;#39;).addClass(&amp;#39;toc-absolute&amp;#39;);
            var toc_top = $(&amp;#39;.toc&amp;#39;).offset().top - 165;
            $(window).scroll(function() {
                if ($(this).scrollTop() &amp;gt;= toc_top) {
                    $(&amp;#39;.toc&amp;#39;).addClass(&amp;#39;toc-fixed&amp;#39;);
                    $(&amp;#39;.toc&amp;#39;).removeClass(&amp;#39;toc-absolute&amp;#39;);
                } else {
                    $(&amp;#39;.toc&amp;#39;).addClass(&amp;#39;toc-absolute&amp;#39;);
                    $(&amp;#39;.toc&amp;#39;).removeClass(&amp;#39;toc-fixed&amp;#39;);
                }
            });
        });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;TOC 스타일링&lt;/h3&gt;
&lt;p&gt;Tocbot의 스타일링을 위해 CSS도 추가해주자. 다른 스킨과 달리 프라치노 공간은 전체 화면을 덮는 커버를 가지고 있어서 &lt;strong&gt;.toc-absolute&lt;/strong&gt; 부분을 수정해야했다. TOC의 가로 위치는 &lt;strong&gt;.toc&lt;/strong&gt;의 right 값을 수정하면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;/* tocbot */
.toc-absolute {
  position: absolute;
  margin-top: calc(100vh + 165px);
}

.toc-fixed {
  position: fixed;
  top: 165px;
}

.toc {
  right: calc((100% - 850px) / 2 - 500px);
  width: 250px;
  padding: 10px;
  box-sizing: border-box;
}

.toc-list {
  margin-top: 10px !important;
  font-size: 0.9em;
}

.toc &amp;gt; .toc-list li {
  margin-bottom: 10px;
}

.toc &amp;gt; .toc-list li:last-child {
  margin-bottom: 0;
}

.toc &amp;gt; .toc-list li a {
  text-decoration: none;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;참고자료&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dailyheumsi.tistory.com/135?category=800540&quot;&gt;https://dailyheumsi.tistory.com/135?category=800540&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pwnbit.kr/89&quot;&gt;https://pwnbit.kr/89&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Web</category>
      <category>TableOfContent</category>
      <category>TOC</category>
      <category>tocbot</category>
      <category>나무위키</category>
      <category>목차</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/251</guid>
      <comments>https://geonlee.tistory.com/251#entry251comment</comments>
      <pubDate>Thu, 30 Sep 2021 03:12:09 +0900</pubDate>
    </item>
    <item>
      <title>URI와 URL</title>
      <link>https://geonlee.tistory.com/250</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전까지는 URL만 알고 있다가, 학교에서 웹 프로그래밍 수업을 들었을때 URI라는 것을 처음 알게되었다. 그렇다고 찾아본 건 아니고, 현재까지도 일을 하면서 매번 URI, URL 구분없이 사용하고 있다. 구분없이 사용한다고 큰 문제는 생기지 않지만, 개발자는 명확한 정의를 아는 직업 아닌가? 이 기회에 URI와 URL의 정의와 용도를 통해 둘의 차이를 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;621&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clRzsc/btrgjF1Xxrq/dMBv0UUKw0cDI6WO3KX0PK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clRzsc/btrgjF1Xxrq/dMBv0UUKw0cDI6WO3KX0PK/img.png&quot; data-alt=&quot;URI, URN, URL&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clRzsc/btrgjF1Xxrq/dMBv0UUKw0cDI6WO3KX0PK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclRzsc%2FbtrgjF1Xxrq%2FdMBv0UUKw0cDI6WO3KX0PK%2Fimg.png&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;621&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;figcaption&gt;URI, URN, URL&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;URI&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URI는 특정 자원(Resource)를 식별하는 &lt;b&gt;통합 자원 식별자&lt;/b&gt;(&lt;b&gt;U&lt;/b&gt;niform &lt;b&gt;R&lt;/b&gt;esource &lt;b&gt;I&lt;/b&gt;dendifier)를 의미한다. 웹 상에서 논리적/물리적 리소스를 식별하는데 사용되는 &lt;b&gt;고유한&lt;/b&gt; 문자열의 시퀀스이다. URI의 하위 개념으로는 URL과 URN이 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;URL&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL은 일반적으로 이야기하는 &lt;b&gt;웹사이트 주소&lt;/b&gt;이다. &lt;b&gt;통합 자원 지시자&lt;/b&gt;(&lt;b&gt;U&lt;/b&gt;niform &lt;b&gt;R&lt;/b&gt;esource &lt;b&gt;L&lt;/b&gt;ocator)라는 뜻이며, 컴퓨터의 네트워크 상에서 자원이 어디 있는 지 알려주기 위한 규약이다. URL은 웹사이트 주소 이외에도 컴퓨터 네트워크 상의 자원을 모두 나타낼 수 있다. 다만, 해당 주소에 접속하려면 해당 URL에 맞는 프로토콜로 접속해야한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;URI와 URL의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL이 URI의 서브셋이라는 점에서 포함 관계인 것 이외에도 URI와 URL의 가장 큰 차이는 다음과 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #f3c000;&quot;&gt;URI&lt;/span&gt;은 &lt;span style=&quot;color: #f3c000;&quot;&gt;식별&lt;/span&gt;, &lt;span style=&quot;color: #009a87;&quot;&gt;URL&lt;/span&gt;은 &lt;span style=&quot;color: #009a87;&quot;&gt;위치&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 상의 주소를 예시로 URI와 URL의 차이를 알아보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;https://www.example.com&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URI(O): &lt;b&gt;https://www.example.com&lt;/b&gt;라는 고유한 식별자&lt;/li&gt;
&lt;li&gt;URL(O): &lt;b&gt;https://www.example.com&lt;/b&gt;라는 서버 자원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;https://www.example.com/skin.css&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URI(O): https://www.example.com/skin.css라는 고유한 식별자&lt;/li&gt;
&lt;li&gt;URL(O): https://www.example.com이라는 서버에 존재하는 skin.css라는 실제 자원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;https://www.example.com/fruits/banana/index.html&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URI(O):&lt;span&gt; https://www.example.com/fruits/banana/index.html&lt;/span&gt;라는 고유한 식별자&lt;/li&gt;
&lt;li&gt;URL(O): https://www.example.com라는 서버의 fruits/banana 폴더 아래의 index.html이라는 실제 자원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;https://www.example.com/123&lt;/b&gt; (&lt;b&gt;123&lt;/b&gt;은 실제 자원이 아닌 식별자 역할)&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URI(O):&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;https://www.example.com/123&lt;/b&gt;라는 고유한 식별자&lt;/li&gt;
&lt;li&gt;URL(X): &lt;b&gt;https://www.example.com&lt;/b&gt;까지만 URL 이후의 &lt;b&gt;123&lt;/b&gt;은 실제 자원이 아님&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;https://www.example.com/fruits?id=123&lt;/b&gt; (&lt;b&gt;fruits&lt;/b&gt;는 실제 자원)&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URI(O):&lt;b&gt;&lt;span&gt; https://www.example.com/fruits?id=123&lt;/span&gt;&lt;/b&gt;라는 고유한 식별자&lt;/li&gt;
&lt;li&gt;URL(X): &lt;b&gt;https://www.example.com/fruits&lt;/b&gt;까지만 URL 이후의 &lt;b&gt;?id=123은&lt;/b&gt; 실제 자원이 아님&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, 둘의 차이를 이해하고 사용하는 건 위의 케이스들처럼 매우 복잡하다. 따라서 업무에서 단 한 가지 용어만 사용해야한다면 상위 개념인 URI를 쓰는게 편할 것이다.&lt;/p&gt;</description>
      <category>Web</category>
      <category>uri</category>
      <category>url</category>
      <category>web</category>
      <category>주소</category>
      <category>형식</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/250</guid>
      <comments>https://geonlee.tistory.com/250#entry250comment</comments>
      <pubDate>Wed, 29 Sep 2021 22:52:46 +0900</pubDate>
    </item>
    <item>
      <title>로컬과 리모트 데이터 상태 관리를 동시에 Apollo Client</title>
      <link>https://geonlee.tistory.com/249</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;262&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W9EOl/btrfXzAsIzq/ds73UQVXMQMmx5W4GEKRl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W9EOl/btrfXzAsIzq/ds73UQVXMQMmx5W4GEKRl0/img.png&quot; data-alt=&quot;Apollo Client&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W9EOl/btrfXzAsIzq/ds73UQVXMQMmx5W4GEKRl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW9EOl%2FbtrfXzAsIzq%2Fds73UQVXMQMmx5W4GEKRl0%2Fimg.png&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;262&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;figcaption&gt;Apollo Client&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apollo Client는 GraphQL을 이용하여 로컬과 리모트 데이터를 다룰 수 있는 자바스크립트의 포괄적인 상태 관리 라이브러리이다. 이를 이용해서 데이터를 가져오거나, 캐시, 애플리케이션 데이터의 수정 이 모든 것을 UI를 업데이트하면서 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apollo Client는 현대의 개발 방식과 동일하게 코드를 경제적이고, 예상가능하며 선언적인 방식으로 짤 수 있게 해준다. &lt;code&gt;@apollo/client&lt;/code&gt; 라이브러리는 리액트와의 통합성을 제공하며, 다른 유명 라이브러리/프레임워크를 위한 통합도 거대한 아폴로 커뮤니티를 통해 유지되고 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;특징&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선언적인 데이터 패칭: 직접 로딩 상태를 따라갈 필요 없이 쿼리를 작성하고 데이터를 가져올 수 있다.&lt;/li&gt;
&lt;li&gt;휼륭한 개발자 경험: 타입스크립트, 크롬 / 파이어폭스 개발자 도구, VS Code 등을 위한 편의 도구를 제공해준다.&lt;/li&gt;
&lt;li&gt;최신 리액트를 위해 디자인된 라이브러리: 최신 리액트 문법인 React Hooks를 지원하는 useQuery, useMutation을 사용하는 등 리액트와 찰떡궁합&lt;/li&gt;
&lt;li&gt;점진적 적용 가능성: 아폴로를 자바스크립트 앱에 몇 방울 뿌려가면서 기능별로 적용해나갈 수 있다.&lt;/li&gt;
&lt;li&gt;보편적 호환성: 어떠한 빌드 설정이나 어떠한 GraphQL API도 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;커뮤니티 중심: 관련 지식을 GraphQL 커뮤니티에 잇는 수 많은 개발자들과 나눌 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 Apollo Client인가?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 데이터를 다루는 데, Apollo Client를 선택해야하는가?&lt;/h3&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;데이터 패칭에 대한 아폴로의 선언적 접근을 통해, 데이터를 받는 것, 로딩과 에러 상태를 추적하는 것 그리고 UI를 업데이트하는 것까지 모든 로직을 useQuery 훅을 통해 캡슐화할 수 있다. 이 캡슐화를 통해 쿼리 결과를 프레젠테이션 컴포넌트에 쉽게 통합할 수 있다! Apollo Client와 리액트를 통해 실제로 어떤 모습인지 알아보자:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Feed() {
  const { loading, error, data } = useQuery(GET_DOGS);
  if (error) return &amp;lt;Error /&amp;gt;;
  if (loading) return &amp;lt;Fetching /&amp;gt;;

  return &amp;lt;DogList dogs={data.dogs} /&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 useQuery 훅을 사용하여 GraphQL 서버에서 몇 개의 개 데이터들을 가져와 목록에 표시하고 있다. useQuery는 리액트의 Hooks API를 활용하여 쿼리를 구성 요소에 바인딩하고 쿼리 결과에 따라 렌더링한다. 데이터가 반환되면 구성 요소는 필요한 데이터에 따라 반응적으로 업데이트된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apollo Client는 로딩 및 오류 상태 추적을 포함하여 처음부터 끝까지 요청의 사이클을 처리한다. 첫 번째 요청 전에는 작성할 미들웨어나 상용 소프트웨어가 없으며, 응답의 변환 및 캐싱에 대해 걱정할 필요가 없다. 컴포넌트에 필요한 데이터를 설명하고 Apollo Client가 어려운 작업을 수행할 수 있도록 하면 된다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apollo Client로 전환하면 데이터 관리와 관련된 불필요한 코드를 많이 삭제할 수 있다. 애플리케이션에 따라 정확한 양은 달라지겠지만, 일부 팀에서는 최대 수천 코드를 줄였다고 한다. 여러분이 아폴로로 코드를 덜 쓰겠지만, 그것이 여러분이 기능들과 타협해야 한다는 것을 의미하지 않는다! useQuery 옵션을 통해 옵티미스틱 UI, 리페칭 및 페이지네이션과 같은 고급 기능도 모두 쉽게 사용할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정이 전혀 필요없는 캐싱&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apollo Client가 다른 데이터 관리 솔루션과 차별화되는 주요 기능 중 하나는 표준화된 캐시이다. Apollo Client에는 지능형 캐시가 포함되어 있어, 초기 구성이 거의 필요없는 수준이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  cache: new InMemoryCache()
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프를 캐싱하는 것은 쉬운 일은 아니지만, Apollo Client 팀은 그래프 해결에 2년을 집중했다고 한다. 여러 경로를 통해 동일한 데이터를 얻을 수 있으므로 여러 컴포넌트에 걸쳐 데이터를 일관되게 유지하려면 정규화가 필수적이다. 몇 가지 실제 사례를 살펴보자.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;const GET_ALL_DOGS = gql`
  query GetAllDogs {
    dogs {
      id
      breed
      displayImage
    }
  }
`;

const UPDATE_DISPLAY_IMAGE = gql`
  mutation UpdateDisplayImage($id: String!, $displayImage: String!) {
    updateDisplayImage(id: $id, displayImage: $displayImage) {
      id
      displayImage
    }
  }
`;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리 &lt;code&gt;GET_ALL_DOGS&lt;/code&gt;는 개의 목록과 개의 &lt;code&gt;displayImage&lt;/code&gt;를 가져온다. &lt;code&gt;UPDATE_DISPLAY_IMAGE&lt;/code&gt;이라는 뮤테이션은 단일 개의 &lt;code&gt;displayImage&lt;/code&gt;를 업데이트한다. 만약 특정 개의 &lt;code&gt;displayImage&lt;/code&gt;를 업데이트 한다면, 새로운 데이터를 반영하기 위해 모든 개의 목록에 있는 해당 항목이 필요하다. Apollo Client는 &lt;code&gt;__typename&lt;/code&gt; 및 &lt;code&gt;id&lt;/code&gt; 속성이 있는 GraphQL 결과의 각 개체를 Apollo 캐시에서 자체 항목으로 분할한다. 이렇게 하면 &lt;code&gt;id&lt;/code&gt;가 있는 뮤테이션에서 값을 반환하면 동일한 id로 개체를 가져오는 모든 쿼리가 자동으로 업데이트된다. 또한 동일한 데이터를 반환하는 두 개의 쿼리가 항상 동기화되도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 실행이 복잡한 기능은 아폴로 캐시를 사용하여 구축하기에는 매우 간단하다. 개 목록을 표시하는 이전 예제의 &lt;code&gt;GET_ALL_DOGS&lt;/code&gt; 쿼리로 돌아가 보자. 특정 개의 세부 페이지로 전환하고 싶다면 어떻게 해야 할까? 이미 각 개의 정보를 가져왔기 때문에 서버에서 동일한 정보를 다시 가져오고 싶지는 않을 것이다. Apollo Client의 캐시 정책 API 덕분에 우리는 두 쿼리 사이에 개를 연결할 수 있으므로 이미 사용 가능한 정보를 가져올 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개 한 마리에 대한 정보를 가져오는 쿼리는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;objectivec&quot;&gt;&lt;code&gt;const GET_DOG = gql`
  query GetDog {
    dog(id: &quot;abc&quot;) {
      id
      breed
      displayImage
    }
  }
`;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 캐시된 &lt;code&gt;Dog&lt;/code&gt; 데이터에 대한 참조를 반환하는 사용자 지정 &lt;code&gt;FieldPolicy&lt;/code&gt;를 정의한다. 이렇게 하면 쿼리에서 해당 데이터를 검색하는 데 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { ApolloClient, InMemoryCache } from '@apollo/client';

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        dog(_, { args, toReference }) {
          return toReference({
            __typename: 'Dog',
            id: args.id,
          });
        }
      }
    }
  }
});

const client = new ApolloClient({ cache });&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Web/GraphQL</category>
      <category>Apollo</category>
      <category>ApolloClient</category>
      <category>graphQL</category>
      <category>그래프큐엘</category>
      <category>아폴로</category>
      <category>아폴로클라인언트</category>
      <category>프론트엔드</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/249</guid>
      <comments>https://geonlee.tistory.com/249#entry249comment</comments>
      <pubDate>Mon, 27 Sep 2021 00:52:48 +0900</pubDate>
    </item>
    <item>
      <title>구글 애드센스로 1000달러 벌기까지의 여정  </title>
      <link>https://geonlee.tistory.com/248</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1000달러 번지는 조금 지났지만, 애드센스로 1000달러를 벌었다. 작년 초에 썼던 &lt;a href=&quot;https://geonlee.tistory.com/176&quot;&gt;애드센스로 100달러를 벌기까지의 여정  &lt;/a&gt;을 생각하며 이번에도 1000달러를 벌기까지의 여정을 공유해보려한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;  애드센스 vs 애드핏&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;&lt;a href=&quot;https://geonlee.tistory.com/17&quot;&gt;더 많이 더 적게 :   애드핏(Adfit) 광고로 수익내기&lt;/a&gt;&amp;nbsp;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #222222;&quot;&gt;&lt;a href=&quot;https://geonlee.tistory.com/18&quot;&gt;구글 애드센스(AdSense)와 다음 애드핏(AdFit) 비교하기&lt;/a&gt;에서도 비교를 했지만, 애드센스와 애드핏은 버는 수익의 차이가 너무 커서 일부 애드핏을 사용하는 곳을 제외하고는 애드센스 심사 이후에는 애드센스를 적용하고 있다. 심지어 애드핏은 카카오 애드핏으로 바뀌었는데, 애드센스는 사이사이 사용성 업데이트를 한 반면, 애드핏은 업데이트도 진행하고 있지 않다. 국내에서도 애드센스를 대항할 수 있는 좋은 광고 플랫폼이 나왔으면 좋겠다.&lt;/span&gt;&lt;/span&gt;&lt;/p&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-origin-width=&quot;1280&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DgllQ/btrfVsfQ8WY/Z3EUpbKDiV4DJLQqsYus10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DgllQ/btrfVsfQ8WY/Z3EUpbKDiV4DJLQqsYus10/img.png&quot; data-alt=&quot;애드센스 그래프 (2019.01 ~ 2020.01)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DgllQ/btrfVsfQ8WY/Z3EUpbKDiV4DJLQqsYus10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDgllQ%2FbtrfVsfQ8WY%2FZ3EUpbKDiV4DJLQqsYus10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;279&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;279&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;애드센스 그래프 (2019.01 ~ 2020.01)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4252&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FEdvQ/btrfU4fcX8B/8vhxPzRsEOneTC7skbXFkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FEdvQ/btrfU4fcX8B/8vhxPzRsEOneTC7skbXFkk/img.png&quot; data-alt=&quot;애드센스 그래프 (2019.01 ~ 2021.09)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FEdvQ/btrfU4fcX8B/8vhxPzRsEOneTC7skbXFkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFEdvQ%2FbtrfU4fcX8B%2F8vhxPzRsEOneTC7skbXFkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4252&quot; height=&quot;586&quot; data-origin-width=&quot;4252&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;애드센스 그래프 (2019.01 ~ 2021.09)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;위의 그래프는 이전 글에 사용했던 2019.01 ~ 2020.01까지의 애드센스 그래프이고, 아래 그래프는 2019.01 ~2021.09까지의 애드센스 그래프이다. 비교해서 보면, 2019.01 ~ 2020.01의 그래프에서 보였던 $4짜리 피뢰침은 이제 잘 안보일 정도이다. 2021년 들어오면서 사이드 프로젝트가 중박정도 치면서 이전 못지않은 수익을 가져다주고 있다. 가장 수익이 많이 난 날은 21년 8월 25일로 총 &lt;b&gt;$46.22&lt;/b&gt;이고, 총 수입은 &lt;b&gt;$1,378.89&lt;/b&gt; 로 환전하면 약 &lt;b&gt;162만원&lt;/b&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;1260&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BV1Tn/btrfMOMg4Ua/5MAr0aNZCpOyOs76K2xnN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BV1Tn/btrfMOMg4Ua/5MAr0aNZCpOyOs76K2xnN1/img.png&quot; data-alt=&quot;현재 환율 기준 총 수익&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BV1Tn/btrfMOMg4Ua/5MAr0aNZCpOyOs76K2xnN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBV1Tn%2FbtrfMOMg4Ua%2F5MAr0aNZCpOyOs76K2xnN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1260&quot; height=&quot;770&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;770&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;100달러에서 1000달러까지 가기까지 무엇이 바뀌었는 지 어떤 항목들이 그래프를 차지하고 있는 지 확인하면서 저 그래프가 만들어진 과정을 확인해보자.&lt;/p&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-origin-width=&quot;4258&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buBi1f/btrfU6RWfml/sUAc9LZxZnluWCANwtqxz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buBi1f/btrfU6RWfml/sUAc9LZxZnluWCANwtqxz1/img.png&quot; data-alt=&quot;블로그 빠리의 택시 운전사 애드센스 그래프 2019.01 ~ 2021.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buBi1f/btrfU6RWfml/sUAc9LZxZnluWCANwtqxz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuBi1f%2FbtrfU6RWfml%2FsUAc9LZxZnluWCANwtqxz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4258&quot; height=&quot;588&quot; data-origin-width=&quot;4258&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;블로그 빠리의 택시 운전사 애드센스 그래프 2019.01 ~ 2021.09&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글을 쓰고 있는 블로그이다. 2년 넘게 운영하고 있고 200개 넘는 글을 썼다. 과거에는 열심히 썼다가 올 해 한 달에 한 개 쓸까말까하는 수준이였는데, 최근에 다시 조금씩 쓰기 시작했다. 일간 방문자 수는 200~600명 정도로 들쑥날쑥이 크다. 위에 이야기했듯이 최근에 글을 안썼더니 점점 일간 방문자 수가 줄어들고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 주제 자체가 특정 기술과 관련되고, 화제성이 떨어지는 주제이다보니 한 방에 빵 오르는 일은 없다. 다만, 사람들이 자주 찾는 주제로 쓴 글인 &lt;a href=&quot;https://geonlee.tistory.com/9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;야,&amp;nbsp;너두&amp;nbsp;개발자&amp;nbsp;포트폴리오&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있어&amp;nbsp; &lt;/a&gt;나 &lt;a href=&quot;https://geonlee.tistory.com/171&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;당신의 클론 코딩은 왜 실패하는가?&lt;/a&gt; 같은 글은 꾸준히 찾아주면서 톡톡히 효자 노릇을 하고 있다. 이런 양질의 글을 쓰도록 더 노력해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그로 번 총 수익은 &lt;b&gt;$306.47&lt;/b&gt;이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;  사이드 프로젝트 더 많이 더 적게&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5018&quot; data-origin-height=&quot;2298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPwHsi/btrfO3CFpfM/mP3o1KNDd5Pm4XKZYLhrRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPwHsi/btrfO3CFpfM/mP3o1KNDd5Pm4XKZYLhrRK/img.png&quot; data-alt=&quot;더 많이 더 적게&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPwHsi/btrfO3CFpfM/mP3o1KNDd5Pm4XKZYLhrRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPwHsi%2FbtrfO3CFpfM%2FmP3o1KNDd5Pm4XKZYLhrRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;5018&quot; height=&quot;2298&quot; data-origin-width=&quot;5018&quot; data-origin-height=&quot;2298&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;가장 처음으로 성공했던 사이드 프로젝트이다. 해당 프로젝트를 하면서 사이드 프로젝트를 수익화하면서 겪을 수 있는 대부분의 시행착오는 다 겪은 것 같다. 기존에 애드핏에서 애드센스로 전환하고, SPA에서 애드센스를 좀 더 효율적으로 사용하는 방법도 깨우쳤다. 만든 지 좀 된 프로젝트이지만, 기존에 CRA로 된 프로젝트를 Next.js로 다시 짜거나, 기존에 GraphCMS로 가져왔던 데이터를 구글 스프레드 시트로 수정하는 등 지속적으로 수정도 진행하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4262&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TXLq3/btrfUNE3R1i/93ahFUAxXrogJtkQ1Gh8B1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TXLq3/btrfUNE3R1i/93ahFUAxXrogJtkQ1Gh8B1/img.png&quot; data-alt=&quot;사이드 프로젝트 더 많이 더 적게&amp;amp;nbsp;애드센스 그래프 2019.01 ~ 2021.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TXLq3/btrfUNE3R1i/93ahFUAxXrogJtkQ1Gh8B1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTXLq3%2FbtrfUNE3R1i%2F93ahFUAxXrogJtkQ1Gh8B1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4262&quot; height=&quot;590&quot; data-origin-width=&quot;4262&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사이드 프로젝트 더 많이 더 적게&amp;nbsp;애드센스 그래프 2019.01 ~ 2021.09&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프는 하위 도메인이 따로 잡혀서 같이 넣어놨다. 8월 20일 기준으로 www가 없는 사이트는 수익이 안 잡혔는데 호스팅 업체로 사용하고 있는 vercel에서 자동으로 리다이렉트 해준 것 같다.&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=gQ7oPpFRH3Y&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/cZDJYT/hyLHLDb41n/DfbQf8CG8KYkzl4SI1zNJ1/img.jpg?width=640&amp;amp;height=480&amp;amp;face=58_122_588_346&quot; data-video-width=&quot;640&quot; data-video-height=&quot;480&quot; data-video-origin-width=&quot;640&quot; data-video-origin-height=&quot;480&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/gQ7oPpFRH3Y&quot; width=&quot;640&quot; height=&quot;480&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;우주하마 - 더 많이 더 적게&lt;/figcaption&gt;
&lt;/figure&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=gUckM84W89Q&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/dqrfxx/hyLI6Mu02P/3uFazGQo1qumkl2kcs5C91/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=580_116_1158_464&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/gUckM84W89Q&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;p data-ke-size=&quot;size16&quot;&gt;더 많이 더 적게는 생각치도 않게 유명 스트리머들이 스트리밍하면서 최근에 가장 많은 수익을 내게 되었다. 오히려 좋아! 글을 쓰는 노력 외에는 돈이 들지 않는 블로그와는 달리, 사이드 프로젝트는 매달 트래픽 비용, 매년 도메인 비용이 들긴 하지만 그만큼 더 많은 수익으로 돌아온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 프로젝트 더 많이 더 적게에서 &lt;span style=&quot;color: #222222;&quot;&gt;가장 수익이 많이 난 날은 침착맨 영상이 올라온 다음 날인 21년 8월 25일로 총&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;$39.16&lt;/b&gt;&lt;span style=&quot;color: #222222;&quot;&gt;이고,&lt;/span&gt;&amp;nbsp;총 수익은 &lt;b&gt;$540.58&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.higherlowerkorea.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;  더 많이 더 적게 하러가기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1632470271689&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;www.higherlowerkorea.com&quot; data-og-source-url=&quot;https://www.higherlowerkorea.com/&quot; data-og-url=&quot;https://higherlowerkorea.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bv8v8t/hyLIUZCGyz/yZD01spSy2HC0tWyORkOEk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=335_69_1149_474&quot;&gt;&lt;a href=&quot;https://www.higherlowerkorea.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.higherlowerkorea.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bv8v8t/hyLIUZCGyz/yZD01spSy2HC0tWyORkOEk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=335_69_1149_474');&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;www.higherlowerkorea.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;  사이드 프로젝트 비공개&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직은 블로그에 공개하지 않았던 사이드 프로젝트이다. 해당 사이드 프로젝트에 대한 자세한 내용은 나중에 글로 작성할 예정이다.&amp;nbsp;기존에 더 많이 더 적게를 하면서 겪었던 시행착오를 통해 좀 더 빠르게 작업을 할 수 있었고, 애드센스 승인도 나름 빠르게 받을 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pJJUs/btrl6KuLvYI/RfVBSBVTqU0G9T8SmyVbLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pJJUs/btrl6KuLvYI/RfVBSBVTqU0G9T8SmyVbLk/img.png&quot; data-alt=&quot;사이드 프로젝트 애드센스 그래프 2019.01 ~ 2021.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pJJUs/btrl6KuLvYI/RfVBSBVTqU0G9T8SmyVbLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpJJUs%2Fbtrl6KuLvYI%2FRfVBSBVTqU0G9T8SmyVbLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;235&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사이드 프로젝트 애드센스 그래프 2019.01 ~ 2021.09&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프에서 특징적인 부분을 찾아보자면 주기적으로 크게 솟구치는 부분이 보인다. 해당 사이트는 모바일 게임의 팬 사이트이다보니, 주로 유저들이 해당 게임의 카페나 포럼에 몰려있는 업데이트일, 점검일에 유저들이 몰리게되었다. 해당 사이트에서 가장 수익이 많이 난 날은&amp;nbsp; 21년 9월 17일로 총 &lt;b&gt;$28.96&lt;/b&gt;이고, 총 수익은 &lt;b&gt;$526.82&lt;/b&gt;이다. 현재는 더 많이 더 적게보다 적게 벌었지만, 운영한 시간을 비교하면 굉장히 준수한 성적이다.&lt;/p&gt;</description>
      <category>Google Adsense</category>
      <category>구글</category>
      <category>달러</category>
      <category>더많이더적게</category>
      <category>블로그</category>
      <category>사이드프로젝트</category>
      <category>수익</category>
      <category>애드센스</category>
      <category>애드핏</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/248</guid>
      <comments>https://geonlee.tistory.com/248#entry248comment</comments>
      <pubDate>Fri, 24 Sep 2021 17:00:05 +0900</pubDate>
    </item>
    <item>
      <title>구글 애널리틱스 트래픽 채널을 해석해보자</title>
      <link>https://geonlee.tistory.com/247</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;구글 애널리틱스을 연동해놓고 데이터만 쌓고 있었는데, 문득 데이터의 의미가 무엇인지 정확하게 알고싶어졌다. 이번 글에서는 트래픽 채널을 확인하고, 각 용어가 어떤 것을 의미하는 지 알아보자.&lt;/p&gt;
&lt;h1&gt;구글 애널리틱스 &amp;gt; 획득 &amp;gt; 전체 트래픽&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;815&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIyG7M/btrfG3a03Ae/ybkY3siS1knXkXOeBWg910/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIyG7M/btrfG3a03Ae/ybkY3siS1knXkXOeBWg910/img.png&quot; data-alt=&quot;구글 애널리틱스 획득 &amp;amp;amp;gt; 전체 트래픽 탭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIyG7M/btrfG3a03Ae/ybkY3siS1knXkXOeBWg910/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIyG7M%2FbtrfG3a03Ae%2FybkY3siS1knXkXOeBWg910%2Fimg.png&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;815&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;figcaption&gt;구글 애널리틱스 획득 &amp;gt; 전체 트래픽 탭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;획득의 전체 트래픽 메뉴는 &lt;code&gt;채널&lt;/code&gt;, &lt;code&gt;트리맵&lt;/code&gt;, &lt;code&gt;소스/매체&lt;/code&gt;, &lt;code&gt;추천&lt;/code&gt; 네가지 서브 메뉴로 구성되어있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 트래픽 &amp;gt; 채널&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;883&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bdi3R/btrfBrKsvUd/HtRY64QGGxg1WKSPaTk9Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bdi3R/btrfBrKsvUd/HtRY64QGGxg1WKSPaTk9Zk/img.png&quot; data-alt=&quot;전체 트래픽 &amp;amp;amp;gt; 채널&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bdi3R/btrfBrKsvUd/HtRY64QGGxg1WKSPaTk9Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBdi3R%2FbtrfBrKsvUd%2FHtRY64QGGxg1WKSPaTk9Zk%2Fimg.png&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;883&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;figcaption&gt;전체 트래픽 &amp;gt; 채널&lt;/figcaption&gt;
&lt;/figure&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;b&gt;Direct&lt;/b&gt; : 직접 해당 사이트의 주소를 입력하여 방문한 트래픽&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Referral&lt;/b&gt; : 구글, 빙 등 대형 검색 엔진 사이트가 아닌 다른 사이트에서 링크를 타고 방문한 트래픽&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Organic&lt;/b&gt; : 구글, 빙 과 같은 대형 검색 엔진 사이트를 통해 방문한 트래픽으로, 광고 영역이 아닌 일반 검색 결과를 통해 방문했기 때문에 이를 뒤에 이야기할 &lt;code&gt;Paid Search&lt;/code&gt;와 비교하여, &lt;code&gt;Unpaid Search&lt;/code&gt;라고도 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Social&lt;/b&gt; : 트위터, 페이스북, 유튜브 등 소셜 미디어를 통해 들어온 트래픽, 특이하게 한국에서 주로 사용하는 검색 포털 사이트인 네이버는 구글 애널리틱스 상에서 &lt;code&gt;Social&lt;/code&gt;로 처리되어 데이터 해석에 유의해야한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Paid Search&lt;/b&gt; : 구글에서 검색을 하면, 상단에 검색어와 관련된 광고가 먼저 보이는 경우가 있는 데, 이 경우 구글 애즈의 검색어 광고를 사용한 것으로 이를 통해 유입한 트래픽은 이와 같이 따로 구분한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 트래픽 &amp;gt; 트리맵&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;872&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pi57f/btrfBsCFshz/0MP0xcmFQHPTWUhztG5RX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pi57f/btrfBsCFshz/0MP0xcmFQHPTWUhztG5RX0/img.png&quot; data-alt=&quot;전체 트래픽 &amp;amp;amp;gt; 트리맵&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pi57f/btrfBsCFshz/0MP0xcmFQHPTWUhztG5RX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpi57f%2FbtrfBsCFshz%2F0MP0xcmFQHPTWUhztG5RX0%2Fimg.png&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;872&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;figcaption&gt;전체 트래픽 &amp;gt; 트리맵&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전체 트래픽 &amp;gt; 채널&lt;/b&gt;과 보여주는 데이터는 같지만, 트리맵을 활용하여 시각적으로 한 눈에 파악할 수 있도록 한 페이지이다. 오른쪽 위의 색은 녹색에 가까울 수록 &lt;b&gt;세션당 페이지수&lt;/b&gt;가 높다는 뜻으로 즉, 해당 채널을 통해 들어온 트래픽의 사용자가 사이트에 더 많이 머문다는 것을 의미한다. 이를 통해 녹색에 가까운 트래픽을 높여야겠다는 결과를 도출할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Paid Search&lt;/code&gt;의 경우 비용이 들기 때문에 트래픽에 많은 부분은 차지하지 않지만, 의도성을 가진 유저가 검색을 해서 유입된 트래픽이기 때문에 &lt;b&gt;세션당 페이지수&lt;/b&gt;가 가장 높은 것을 확인할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 트래픽 &amp;gt; 소스/매체&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;872&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UYrss/btrfIYAyiF6/a0IDHocWT1EOHjfo4uJb2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UYrss/btrfIYAyiF6/a0IDHocWT1EOHjfo4uJb2K/img.png&quot; data-alt=&quot;전체 트래픽 &amp;amp;amp;gt; 소스/매체&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UYrss/btrfIYAyiF6/a0IDHocWT1EOHjfo4uJb2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUYrss%2FbtrfIYAyiF6%2Fa0IDHocWT1EOHjfo4uJb2K%2Fimg.png&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;872&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;figcaption&gt;전체 트래픽 &amp;gt; 소스/매체&lt;/figcaption&gt;
&lt;/figure&gt;
&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;b&gt;소스/매체&lt;/b&gt;에서는 &lt;b&gt;채널(매체)&lt;/b&gt; 뿐 아니라 자세한 소스까지 보여준다. 해당 페이지를 통해 어떤 사이트를 통해 내 사이트에 트래픽이 유입되었는 지 좀 더 자세히 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서도 마찬가지로 &lt;b&gt;세션당 페이지수&lt;/b&gt;가 높은 &lt;b&gt;소스/매체&lt;/b&gt;를 파악하여 해당 &lt;b&gt;소스/매체&lt;/b&gt;를 통해 들어오는 트래픽을 높여야한다는 결론을 낼 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 트래픽 &amp;gt; 추천&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;883&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/On3hA/btrfFGtCxE4/fKfEIkPZtiL9kdKN1sOTf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/On3hA/btrfFGtCxE4/fKfEIkPZtiL9kdKN1sOTf0/img.png&quot; data-alt=&quot;전체 트래픽 &amp;amp;amp;gt; 추천&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/On3hA/btrfFGtCxE4/fKfEIkPZtiL9kdKN1sOTf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOn3hA%2FbtrfFGtCxE4%2FfKfEIkPZtiL9kdKN1sOTf0%2Fimg.png&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;883&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;figcaption&gt;전체 트래픽 &amp;gt; 추천&lt;/figcaption&gt;
&lt;/figure&gt;
&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;code&gt;Referral&lt;/code&gt;에 해당하는 트래픽만 보여주는 페이지이다.&lt;/p&gt;</description>
      <category>Google Analytics</category>
      <category>구글</category>
      <category>소스</category>
      <category>애널리틱스</category>
      <category>오가닉</category>
      <category>트래픽 채널</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/247</guid>
      <comments>https://geonlee.tistory.com/247#entry247comment</comments>
      <pubDate>Thu, 23 Sep 2021 16:27:50 +0900</pubDate>
    </item>
    <item>
      <title>맥 터미널에서 code 명령어로 VS Code 열기</title>
      <link>https://geonlee.tistory.com/246</link>
      <description>&lt;h1&gt;맥 터미널에서 code 명령어로 VSCode 열기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맥으로 진행하는 웹 강의를 듣다보면 터미널에서 프로젝트 생성 후, &lt;code&gt;code .&lt;/code&gt; 명령어를 통해 바로 해당 디렉토리의 VS Code를 띄우는 모습을 볼 수 있다. 처음 봤을 때 굉장히 간지나고 편리한 방법이여서 방법을 정리해봤다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;VS Code 상에서 진행하기&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VSCode를 실행한다.&lt;/li&gt;
&lt;li&gt;커맨드 파레트(&lt;code&gt;Cmd+Shift+P&lt;/code&gt;)를 열고 'shell command'라고 친다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Shell Command: Install 'code' command in PATH&lt;/b&gt; 라는 명령어를 찾아서 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;100&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N3eLW/btrfjSzqV0A/GwyGxWXAtdhtwHHlox2QS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N3eLW/btrfjSzqV0A/GwyGxWXAtdhtwHHlox2QS1/img.png&quot; data-alt=&quot;Shell Command: Install &amp;amp;#39;code&amp;amp;#39; command in PATH&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N3eLW/btrfjSzqV0A/GwyGxWXAtdhtwHHlox2QS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN3eLW%2FbtrfjSzqV0A%2FGwyGxWXAtdhtwHHlox2QS1%2Fimg.png&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;100&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;figcaption&gt;Shell Command: Install 'code' command in PATH&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 &lt;code&gt;$PATH&lt;/code&gt;의 변경을 반형하기 위해 터미널을 재실행한다.&lt;/li&gt;
&lt;li&gt;이제 터미널에서 &lt;code&gt;code&lt;/code&gt; 명령어를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ 주의사항: 과거 VS Code 버전에서 쓰던 code 명령어 alias가 .bash_profile에 있다면 이를 삭제하고 위의 순서대로 진행한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;터미널 상에서 직접 추가하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS Code 상에서 커맨드를 실행하는 대신, 직접 VS Code를 경로에 추가할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;bash&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; ~/.bash_profile
# Add Visual Studio Code (code)
export PATH=&quot;\$PATH:/Applications/Visual Studio Code.app/Contents/Resources/app/bin&quot;
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;zsh&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;cat &amp;lt;&amp;lt; EOF &amp;gt;&amp;gt; ~/.zshrc
# Add Visual Studio Code (code)
export PATH=&quot;\$PATH:/Applications/Visual Studio Code.app/Contents/Resources/app/bin&quot;
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이렇게 했을 때 필자는 오류가 생겼는데 위의 코드를 다음과 같은 코드로 바꿔서 추가해서 해결했다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;code () { VSCODE_CWD=&quot;$PWD&quot; open -n -b &quot;com.microsoft.VSCode&quot; --args $* ;}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>IDE/VS Code</category>
      <category>bash</category>
      <category>Code</category>
      <category>vsCode</category>
      <category>zsh</category>
      <category>명령어</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/246</guid>
      <comments>https://geonlee.tistory.com/246#entry246comment</comments>
      <pubDate>Thu, 16 Sep 2021 21:34:27 +0900</pubDate>
    </item>
    <item>
      <title>맥에서 $PATH 설정이 오류 난 경우 해결 방법</title>
      <link>https://geonlee.tistory.com/245</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;bash&lt;/code&gt;나 &lt;code&gt;zsh&lt;/code&gt;의 설정값 &lt;code&gt;.bash_profile&lt;/code&gt;, &lt;code&gt;.zshrc&lt;/code&gt;를 &lt;code&gt;vi&lt;/code&gt;를 이용해 수정하다보면 코드를 잘못 입력해서 기존에 적용해두었던 PATH 설정 전체를 쓸 수 없는 경우가 생긴다. 필자의 경우 VSCode에서 &lt;code&gt;code&lt;/code&gt; 명령어를 통해 VSCode를 여는 경로를 추가하다가 문제가 생겼다. &lt;code&gt;$PATH&lt;/code&gt; 설정 전체가 오류나면 생각보다 당황스러운데 다음과 같은 문제가 생긴다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;$PATH&lt;/code&gt; 설정 전체가 오류나면 생기는 일&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;482&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEmZWe/btrfgYVzAH3/MQmZM4YWnELm8LOxNTTzLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEmZWe/btrfgYVzAH3/MQmZM4YWnELm8LOxNTTzLK/img.png&quot; data-alt=&quot;Shell Script 명령어를 사용할 수 없게 된 터미널&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEmZWe/btrfgYVzAH3/MQmZM4YWnELm8LOxNTTzLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEmZWe%2FbtrfgYVzAH3%2FMQmZM4YWnELm8LOxNTTzLK%2Fimg.png&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;482&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;figcaption&gt;Shell Script 명령어를 사용할 수 없게 된 터미널&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 명령어 실행 경로에 오류가 나서 가장 자주 쓰는 Shell Script 명령어도 사용할 수 없게 된다. 이 때쯤 머리에서는 악순환의 고리가 떠오른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shell Script 명령어를 사용할 수 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; &lt;code&gt;.zshrc&lt;/code&gt;에서 &lt;code&gt;$PATH&lt;/code&gt; 설정을 바꿔서 오류를 수정해야지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; &lt;code&gt;vi&lt;/code&gt; 명령어도 안돼서 &lt;code&gt;.zshrc&lt;/code&gt; 수정 못한다고요 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숨이 턱 막히면서 맥을 초기화해야하나 싶지만, 심호흡 한 번 하고 생각하면 해결책은 간단하다. 맥에서 파일을 수정할 수 있는게 &lt;code&gt;vi&lt;/code&gt;를 이용할 수 있는 방법만 있는게 아니다. 우리에게는 VSCode라는 최강의 텍스트 에디터가 있다.&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;code&gt;$PATH&lt;/code&gt; 경로를 확인해보자. (다행히도 echo 등의 기본 출력 명령어는 사용 가능하다)&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;echo $PATH&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 경로가 나올 수도 있는데, 그 중에 &lt;code&gt;/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin&lt;/code&gt;라는 경로가 포함되어 있어야한다. &lt;code&gt;$PATH&lt;/code&gt; 설정이 오류가 난 경우에는 해당 경로가 포함되지 않았고, 이를 수정해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정을 위해서 위에 언급한 VSCode를 이용해서 맥의 최상위 폴더를 프로젝트로 열면 &lt;code&gt;.zshrc&lt;/code&gt;가 보일 것이다. oh my zsh! 만약 &lt;code&gt;.zshrc&lt;/code&gt; 파일이 보이지 않는다면 환경설정에서 &quot;숨김 파일 보기&quot;를 클릭하고 다시 확인해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;48&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdAmZA/btrfhkjEnf6/nY7PxauHfqhlZX2DJrlrv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdAmZA/btrfhkjEnf6/nY7PxauHfqhlZX2DJrlrv1/img.png&quot; data-alt=&quot;VSCode에서 찾은 .zshrc&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdAmZA/btrfhkjEnf6/nY7PxauHfqhlZX2DJrlrv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdAmZA%2FbtrfhkjEnf6%2FnY7PxauHfqhlZX2DJrlrv1%2Fimg.png&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;48&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;figcaption&gt;VSCode에서 찾은 .zshrc&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 문제가 된 코드를 수정하고, 변경사항을 반영하자.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;source ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 후에 터미널을 완전히 껐다가 키면 &lt;code&gt;$PATH&lt;/code&gt; 설정이 원래대로 돌아올 것이다.&lt;/p&gt;</description>
      <category>Devlog</category>
      <category>MAC</category>
      <category>path</category>
      <category>zsh</category>
      <category>오류</category>
      <category>터미널</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/245</guid>
      <comments>https://geonlee.tistory.com/245#entry245comment</comments>
      <pubDate>Thu, 16 Sep 2021 21:17:20 +0900</pubDate>
    </item>
    <item>
      <title>네비게이션 영역 GNB, LNB, SNB, FNB에 대해 알아보자</title>
      <link>https://geonlee.tistory.com/244</link>
      <description>&lt;h1&gt;  들어가며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹사이트 개발, 기획 등을 하다보면 GNB, LNB 등의 단어를 듣게 된다. 필자도 실무를 하면서 알게되었고, 항상 들을때마다 헷갈려서 이번 기회에 한 번 정리해보려한다.&lt;/p&gt;
&lt;h1&gt;네비게이션 영역?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네비게이션 영역은 이름처럼 네비게이션 즉 특정 페이지 이동을 위해 필요한 영역을 의미한다. 또한, 이러한 영역들은 모든 페이지에서 항상 고정된 영역을 차지하는 특징을 가지게된다.&lt;/p&gt;
&lt;h1&gt;GNB(Global Navigation Bar)&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;1079&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yfkVH/btrerzCv2qT/1Vk1L8YwbjFBosMkdE2UQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yfkVH/btrerzCv2qT/1Vk1L8YwbjFBosMkdE2UQK/img.png&quot; data-alt=&quot;네이버 공식 홈페이지 GNB 영역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yfkVH/btrerzCv2qT/1Vk1L8YwbjFBosMkdE2UQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyfkVH%2FbtrerzCv2qT%2F1Vk1L8YwbjFBosMkdE2UQK%2Fimg.png&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;1079&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;figcaption&gt;네이버 공식 홈페이지 GNB 영역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;694&quot; data-filename=&quot;Screenshot 2021-09-09 at 00.25.19 복사본 3.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2yGpF/btrevPRPUiG/qWUkMDGKEICK8XjSlq66OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2yGpF/btrevPRPUiG/qWUkMDGKEICK8XjSlq66OK/img.png&quot; data-alt=&quot;네이버 뉴스 서비스 GNB 영역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2yGpF/btrevPRPUiG/qWUkMDGKEICK8XjSlq66OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2yGpF%2FbtrevPRPUiG%2FqWUkMDGKEICK8XjSlq66OK%2Fimg.png&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;694&quot; data-filename=&quot;Screenshot 2021-09-09 at 00.25.19 복사본 3.png&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;figcaption&gt;네이버 뉴스 서비스 GNB 영역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사이트 전체에 동일하게 적용되는 최상위 공통 네비게이션&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;포함되는 콘텐츠&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사이트 로고 - 홈페이지 링크&lt;/li&gt;
&lt;li&gt;메인 메뉴 - 대분류 메뉴라고도 하며, 주요 페이지 링크 버튼 역할을 하거나 호버시 서브 메뉴를 볼 수 있는 버튼 역할을 한다. 서브 메뉴가 있는 경우 클릭시 메인 서브 메뉴로 가는 링크 역할을 하기도 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;LNB(Local Navigation Bar)&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;1106&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oHR6H/btreqJk3cZ7/aTGDPScmOsOwvyVqvTEfx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oHR6H/btreqJk3cZ7/aTGDPScmOsOwvyVqvTEfx1/img.png&quot; data-alt=&quot;네이버 공식 홈페이지 LNB(Local Navigation Bar) 영역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oHR6H/btreqJk3cZ7/aTGDPScmOsOwvyVqvTEfx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoHR6H%2FbtreqJk3cZ7%2FaTGDPScmOsOwvyVqvTEfx1%2Fimg.png&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;1106&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;figcaption&gt;네이버 공식 홈페이지 LNB(Local Navigation Bar) 영역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;694&quot; data-filename=&quot;Screenshot 2021-09-09 at 00.25.19 복사본 2.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HRzNO/btrevZUshYc/Mtc7vlNuJlr0aZTnjClRJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HRzNO/btrevZUshYc/Mtc7vlNuJlr0aZTnjClRJ1/img.png&quot; data-alt=&quot;네이버 뉴스 서비스 LNB 영역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HRzNO/btrevZUshYc/Mtc7vlNuJlr0aZTnjClRJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHRzNO%2FbtrevZUshYc%2FMtc7vlNuJlr0aZTnjClRJ1%2Fimg.png&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;694&quot; data-filename=&quot;Screenshot 2021-09-09 at 00.25.19 복사본 2.png&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;figcaption&gt;네이버 뉴스 서비스 LNB 영역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GNB의 메인 메뉴의 서브 메뉴로 나오는 네비게이션 영역&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;포함되는 콘텐츠&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서브 메뉴 - 중분류 메뉴라고도 하며, GNB 메뉴의 하위 페이지 혹은 GNB 메뉴 페이지의 특정 영역의 링크 역할을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;SNB(Side Navigation Bar)&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;694&quot; data-filename=&quot;Screenshot 2021-09-09 at 00.25.19 복사본.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT72w6/btreqJk3D5S/pqSqaxB3gZ2J9TXUYKyPA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT72w6/btreqJk3D5S/pqSqaxB3gZ2J9TXUYKyPA1/img.png&quot; data-alt=&quot;네이버 뉴스 서비스 SNB 영역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT72w6/btreqJk3D5S/pqSqaxB3gZ2J9TXUYKyPA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT72w6%2FbtreqJk3D5S%2FpqSqaxB3gZ2J9TXUYKyPA1%2Fimg.png&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;694&quot; data-filename=&quot;Screenshot 2021-09-09 at 00.25.19 복사본.png&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;figcaption&gt;네이버 뉴스 서비스 SNB 영역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적으로 왼쪽에 위치해있으며, 메인 메유, 서브 메뉴를 제외한 나머지 사이드 메뉴를 포함하는 영역&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;FNB(Footer Navigation Bar)&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;520&quot; data-filename=&quot;Screenshot 2021-09-09 at 00.30.57.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d4HJ1w/btreww5AjZQ/Ysg54bVsJl9DDwvv7WAccK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d4HJ1w/btreww5AjZQ/Ysg54bVsJl9DDwvv7WAccK/img.png&quot; data-alt=&quot;네이버 뉴스 서비스 FNB 영역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d4HJ1w/btreww5AjZQ/Ysg54bVsJl9DDwvv7WAccK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd4HJ1w%2Fbtreww5AjZQ%2FYsg54bVsJl9DDwvv7WAccK%2Fimg.png&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;520&quot; data-filename=&quot;Screenshot 2021-09-09 at 00.30.57.png&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;figcaption&gt;네이버 뉴스 서비스 FNB 영역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하단메뉴, 하단로고, 회사의 주소, 카피라이팅 영역&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Web</category>
      <category>FnB</category>
      <category>gnb</category>
      <category>LNB</category>
      <category>SNB</category>
      <category>기획</category>
      <category>네비게이션바</category>
      <category>네이버</category>
      <category>웹</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/244</guid>
      <comments>https://geonlee.tistory.com/244#entry244comment</comments>
      <pubDate>Thu, 9 Sep 2021 00:44:27 +0900</pubDate>
    </item>
    <item>
      <title>VS Code 설정 및 확장 프로그램 동기화하기</title>
      <link>https://geonlee.tistory.com/243</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 Setting Sync라는 확장프로그램을 이용해서 VSCode를 동기화하는 &lt;a href=&quot;https://geonlee.tistory.com/237&quot;&gt;글&lt;/a&gt;을 작성했었는데, 댓글을 통해 알아보니 v1.48부터 VSCode에서 자체적으로 해당 기능을 제공하게 되어서 이에 맞게 글을 다시 작성한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;들어가며  &lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 환경에 들어갔을 때 IDE를 다운받아서 처음부터 환경설정을 다시 하는 작업은 굉장히 귀찮은 작업이다. 이는 웹에서 자주 사용하는 IDE인 VSCode에서도 마찬가지인데, 자신만의 단축키나 특히 VSCode의 장점인 확장프로그램을 사용하는 경우 하나하나 이를 입력 및 설치하는 건 정말 힘든 일이다. VSCode에서 이러한 동기화 작업을 할 수 있는 방법에 대해 알아보자!&lt;/p&gt;
&lt;h1&gt;준비사항&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 기능은 계정을 기반으로 연동 데이터를 저장하게 되는데, Github 계정이나 마이크로소프트 계정이 필요하다. 개발자라면 둘 중 하나의 계정은 가지고 있을 것이라 생각한다. 필자는 Github 계정을 이용해서 동기화를 해보려한다.&lt;/p&gt;
&lt;h1&gt;동기화&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 동기화를 위해 VSCode 왼쪽 아래 톱니바퀴(설정) 버튼을 누르고 &lt;b&gt;Turn on Setting Sync&lt;/b&gt;를 누르자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;806&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xNkCM/btreh1ztnjt/4Dhwtvd9GBHQRPlUPDk1yK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xNkCM/btreh1ztnjt/4Dhwtvd9GBHQRPlUPDk1yK/img.png&quot; data-alt=&quot;VSCode 톱니바퀴(설정)에서 Turn on Settings Sync 선택&amp;amp;amp;nbsp;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xNkCM/btreh1ztnjt/4Dhwtvd9GBHQRPlUPDk1yK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxNkCM%2Fbtreh1ztnjt%2F4Dhwtvd9GBHQRPlUPDk1yK%2Fimg.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;806&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;figcaption&gt;VSCode 톱니바퀴(설정)에서 Turn on Settings Sync 선택&amp;nbsp;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 버튼을 누르게되면 VSCode의 어떤 요소를 동기화할 것인지 선택하는 창이 뜨게 된다. 각 요소들은 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;354&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HKb9H/btrejVS0G1f/DMy4O1h2fCTyb6ofvkRtaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HKb9H/btrejVS0G1f/DMy4O1h2fCTyb6ofvkRtaK/img.png&quot; data-alt=&quot;동기화 할 설정 선택 및 동기화 계정 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HKb9H/btrejVS0G1f/DMy4O1h2fCTyb6ofvkRtaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHKb9H%2FbtrejVS0G1f%2FDMy4O1h2fCTyb6ofvkRtaK%2Fimg.png&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;354&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;figcaption&gt;동기화 할 설정 선택 및 동기화 계정 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설정&lt;/li&gt;
&lt;li&gt;키보드 단축키&lt;/li&gt;
&lt;li&gt;유저 스니펫&lt;/li&gt;
&lt;li&gt;확장 프로그램&lt;/li&gt;
&lt;li&gt;UI 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 &lt;b&gt;Sign in &amp;amp; Turn on&lt;/b&gt; 버튼을 누르면 Github 계정 혹은 마이크로소프트 계정에 연동하는 과정이 진행되고,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;84&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H2YWB/btrepvyZmK3/Z2OeeVdnGq6YOfiH5Mnbt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H2YWB/btrepvyZmK3/Z2OeeVdnGq6YOfiH5Mnbt1/img.png&quot; data-alt=&quot;VSCode 설정 연동 완료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H2YWB/btrepvyZmK3/Z2OeeVdnGq6YOfiH5Mnbt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH2YWB%2FbtrepvyZmK3%2FZ2OeeVdnGq6YOfiH5Mnbt1%2Fimg.png&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;84&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;figcaption&gt;VSCode 설정 연동 완료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 해당 계정을 통한 동기화가 마무리된다. 이제 다른 기기에서 똑같이 &lt;b&gt;Turn on Setting Sync&lt;/b&gt;를 누르고 동기화를 하면 이전에 동기화된 설정들을 다른 기기에서 사용할 수 있게된다.&lt;/p&gt;</description>
      <category>IDE/VS Code</category>
      <category>vsCode</category>
      <category>동기화</category>
      <category>설정</category>
      <category>코드 스니펫</category>
      <category>키보드 단축키</category>
      <category>확장프로그램</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/243</guid>
      <comments>https://geonlee.tistory.com/243#entry243comment</comments>
      <pubDate>Wed, 8 Sep 2021 02:40:07 +0900</pubDate>
    </item>
    <item>
      <title>트위터 클론 코딩 #0 Introduction ~ #1 Set up</title>
      <link>https://geonlee.tistory.com/242</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/di4l9t/btrfIZliaSr/sNp16kqDOYcuN5pUwOuYt1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/di4l9t/btrfIZliaSr/sNp16kqDOYcuN5pUwOuYt1/img.jpg&quot; data-alt=&quot;노마드 코더 트위터 클론 코딩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/di4l9t/btrfIZliaSr/sNp16kqDOYcuN5pUwOuYt1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdi4l9t%2FbtrfIZliaSr%2FsNp16kqDOYcuN5pUwOuYt1%2Fimg.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&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;figcaption&gt;노마드 코더 트위터 클론 코딩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노마드 코더의 &lt;a href=&quot;https://nomadcoders.co/nwitter&quot;&gt;트위터 클론 코딩 강의&lt;/a&gt;를 듣고 정리한 강의노트입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;0.0 Welcome! / 환영합니다!&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;강의에서 구현할 Firebase를 이용하여 가능한 기능&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이메일, 비밀번호을 이용한 인증&lt;/li&gt;
&lt;li&gt;회원가입&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;심지어 모든 기능을 백엔드 코드 작성없이 할 수 있다. 사이트를 만드는 데 가장 많이 드는 시간이 CSS 작업일 정도 ㅎㅎ&lt;/p&gt;
&lt;h1&gt;0.1 Requirements / 요구사항&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해당 강의를 듣기 전에 들어야 할 강의&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nomadcoders.co/kokoa-clone/lobby&quot;&gt;코코아톡 클론 코딩&lt;/a&gt; - &lt;code&gt;HTML&lt;/code&gt;, &lt;code&gt;CSS&lt;/code&gt;, &lt;code&gt;Github&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nomadcoders.co/react-fundamentals&quot;&gt;ReactJS로 영화 웹 서비스 만들기&lt;/a&gt; - &lt;code&gt;ReactJS&lt;/code&gt; Fundamantals&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nomadcoders.co/react-for-beginners/lobby&quot;&gt;초보를 위한 React JS&lt;/a&gt; - &lt;code&gt;React Router&lt;/code&gt;, &lt;code&gt;React Hooks&lt;/code&gt;, &lt;code&gt;TypeScript&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nomadcoders.co/react-hooks-introduction&quot;&gt;실전형 리액트 Hooks 10개&lt;/a&gt; - &lt;code&gt;React Hooks&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해당 강의에서 배우게 될 것&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React 컴포넌트를 만들고, 상태를 관리하는 새로운 방법 with React Hooks&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;0.2 What is Firebase / Firebase란?&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Firebase란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase는 고품질 앱을 빠르게 개발하고 비즈니스를 성장시키는 데 도움이 되는 Google의 모바일 플랫폼&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://firebase.google.com&quot;&gt;https://firebase.google.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;웹사이트를 통해 제공해주는 백엔드 서비스&lt;/li&gt;
&lt;li&gt;경쟁 서비스는 AWS의 &lt;a href=&quot;https://aws.amazon.com/ko/amplify/&quot;&gt;Amplify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Amplify에서 GraphQL API등 Firebase에는 없는 새로운 기능들을 제공해주지만, Firebase의 서비스가 오래되었기 때문에 관련 커뮤니티가 크고 질문/답변, 튜토리얼 등이 많기 때문에 Firebase를 사용하는 것을 추천한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Firebase의 초기 모델&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스로 시작했고, 이후에 구글에 인수되고 기능이 확장됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Firebase의 기능&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Build better apps / 더 나은 앱 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 작성하지 않고도 애플리케이션을 더 좋게 만들 수 있는 기능을 만들 수 있음&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Cloude FireStore&lt;/b&gt; - 데이터베이스로는 SQL같은 데이터베이스 관련 코드 없이 데이터베이스를 사용하게 해준다.&lt;/li&gt;
&lt;li&gt;Firebase ML - 머신러닝을 통한 작업을 할 수 있게 해준다.&lt;/li&gt;
&lt;li&gt;Cloud Functions - AWS lambda와 비슷한 Serverless function&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cloud Storage&lt;/b&gt; - AWS S3와 비슷한 파일 업로드 저장소, 강의에서는 사진을 업로드하는 데에 사용할 예정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hosting(호스팅)&lt;/b&gt; - Asset을 배포나 React 애플리케이션 배포에 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Authentication(인증)&lt;/b&gt; - 가장 중요한 기능으로 이미 구현된 인증을 통해서 애플리케이션에 10분도 안돼서 인증을 구현할 수 있음&lt;/li&gt;
&lt;li&gt;Realtime Database - Firebase의 원조 데이터베이스인 Realtime Database&lt;/li&gt;
&lt;li&gt;Analytics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Improve app quality / 앱 퀄리티 높이기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글의 전문분야인 Analytics를 통해 앱의 퀄리티를 높일 수 있음&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Performance Monitoring - 앱의 성능을 측정&lt;/li&gt;
&lt;li&gt;Test Lab - 각종 기기별로 앱을 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Grow your business / 사업 성장시키기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비즈니스 영역으로 확장된 형태의 Analytics&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Google Analytics - 우리가 아는 그 &quot;구글 애널리틱스&quot;&lt;/li&gt;
&lt;li&gt;Cloud Messaging - 푸쉬 알림을 사용하고자 하는 경우 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;0.3 When To Use Firebase / 언제 Firebase를 써야할까?&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;언제 Firebase를 써야할까?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내가 생각하고 있는 아이디어를 빠르게 구현해 보고싶을 때 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;언제 Firebase를 쓰면 안될까?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 프로젝트, 실제 프로젝트의 경우 규모가 커지게 되면 Firebase에 종속되지 않고, 기존에 구현된 Firebase의 기능을 구현하는 것이 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;0.4 Pricing in Firebase / Firebase의 요금제&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Firebase는 완전 무료가 아니다.&lt;/li&gt;
&lt;li&gt;적은 요청의 경우 무료로 사용 가능하지만 일정 사용량이 넘어가면 요금이 부과된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1.0 React + Firebase Setup / React와 Firebase로 준비하기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CRA(Create React App)으로 React 프로젝트 만들기&lt;/h2&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;npx create-react-app nwitter
...
code nwitter
git remote add origin {my nwitter github repo url}&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://geonlee.tistory.com/32&quot;&gt;npx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CRA(Create React App)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dl0312/nwitter&quot;&gt;내 nwitter Github 저장소&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nomadcoders/nwitter&quot;&gt;노마드코더 nwitter Github 저장소&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Firebase 프로젝트 생성하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;702&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rYRdD/btrfw51B5P6/YNWxVg7sMAkPwAUrqxKYN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rYRdD/btrfw51B5P6/YNWxVg7sMAkPwAUrqxKYN1/img.png&quot; data-alt=&quot;Firebase 홈페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rYRdD/btrfw51B5P6/YNWxVg7sMAkPwAUrqxKYN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrYRdD%2Fbtrfw51B5P6%2FYNWxVg7sMAkPwAUrqxKYN1%2Fimg.png&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;702&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;figcaption&gt;Firebase 홈페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://firebase.google.com/&quot;&gt;Firebase 공식 홈페이지&lt;/a&gt;에서 &quot;콘솔로 이동&quot; 클릭&lt;/li&gt;
&lt;li&gt;&quot;nwitter&quot;라는 이름으로 프로젝트 생성(기존에 애널리틱스 계정이 있다면 연동)&lt;/li&gt;
&lt;li&gt;&quot;nwitter&quot;라는 이름으로 웹 앱 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹 프로젝트에서 Firebase JavaScript SDK를 참조하는 방법&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CDN - script 태그로 CDN에서 불러오는 방법&lt;/li&gt;
&lt;li&gt;npm package - package.json에 Firebase 관련 패키지를 설치해서 사용&lt;/li&gt;
&lt;li&gt;Bower package&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 방법으로 Firebase를 사용할 예정 다음을 이용하여 firebase를 npm으로 설치&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;npm install --save firebase&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;yarn add firebase&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Firebase 설정&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// Import the functions you need from the SDKs you need
import { initializeApp } from &quot;firebase/app&quot;;
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_APP_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGIN_ID,
  appId: process.env.REACT_APP_APP_ID,
  measurementId: process.env.REACT_APP_MEASUREMENT_ID,
}

// Initialize Firebase
export default initializeApp(firebaseConfig);&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;1.1 Securing the Keys / 키 값 보호하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 최상단 폴더에 &lt;code&gt;.env&lt;/code&gt; 파일을 만들어서 firebaseConfig에 들어가는 설정값들을 환경변수로 관리하도록 수정&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;REACT_APP_API_KEY={REACT_APP_API_KEY}
REACT_APP_AUTH_DOMAIN={REACT_APP_AUTH_DOMAIN}
REACT_APP_PROJECT_ID={REACT_APP_PROJECT_ID}
REACT_APP_STORAGE_BUCKET={REACT_APP_STORAGE_BUCKET}
REACT_APP_MESSAGIN_ID={REACT_APP_MESSAGIN_ID}
REACT_APP_APP_ID={REACT_APP_APP_ID}
REACT_APP_MEASUREMENT_ID={REACT_APP_MEASUREMENT_ID}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRA로 만들어진 프로젝트의 경우 특별한 설정없이 &lt;code&gt;.env&lt;/code&gt;파일에 prefix로 &lt;code&gt;REACT_APP&lt;/code&gt;만 붙이면 프로젝트 내에서 &lt;code&gt;process.env.REACT_APP_API_KEY&lt;/code&gt; 이런 식으로 참조할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 이렇게 설정된 키 값이 담긴 &lt;code&gt;.env&lt;/code&gt; 파일은 Github에 올리지 않기 위해 &lt;code&gt;.gitignore&lt;/code&gt;파일에 &lt;code&gt;.env&lt;/code&gt; 파일을 추가한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;.env&lt;/code&gt;파일을 만들어서 키 값을 보호하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.env&lt;/code&gt; 파일을 만들어서 코드를 짠다해도 브라우저 상에서 어떤 키 값이 쓰였는지는 확인할 수 있기 때문에 이 과정이 &quot;보안&quot;을 위한 과정은 아니다. 다만, 해당 키값이 Github에 올라가는 건 너무 퍼블릭하게 키를 보여주는 것이 되기 때문에 이렇게 Github 상에서 숨기는 과정을 거친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 이야기한 것처럼 브라우저 상에서 어떤 키 값이 쓰였는 지 확인이 가능하기 때문에, 너무 키를 감추는 것에 몰두할 필요는 없다. 오히려 특정 도메인에서만 해당 키를 사용할 수 있게 하는 등 보안적인 부분에 시간을 쓰는게 좋을 것이다.&lt;/p&gt;
&lt;h1&gt;1.2 Router Setup / 라우터 설정하기&lt;/h1&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;npm install --save react-router-dom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;yarn add react-router-dom&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;React Hooks&lt;/code&gt;를 이용해서 로그인 여부를 확인하고, &lt;code&gt;react-router-dom&lt;/code&gt;의 기능을 이용하여 그에 따른 페이지를 보여주는 방식으로 구현&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useState } from 'react'
import { HashRouter as Router, Route, Switch } from 'react-router-dom'
import Auth from '../routes/Auth'
import Home from '../routes/Home'

const AppRouter = () =&amp;gt; {
  const [isLoggedIn, setIsLoggedIn] = useState(false)
  return (
    &amp;lt;Router&amp;gt;
      &amp;lt;Switch&amp;gt;
        {isLoggedIn ? (
          &amp;lt;&amp;gt;
            &amp;lt;Route exact path=&quot;/&quot;&amp;gt;
              &amp;lt;Home /&amp;gt;
            &amp;lt;/Route&amp;gt;
          &amp;lt;/&amp;gt;
        ) : (
          &amp;lt;&amp;gt;
            &amp;lt;Route exact path=&quot;/&quot;&amp;gt;
              &amp;lt;Auth /&amp;gt;{' '}
            &amp;lt;/Route&amp;gt;
          &amp;lt;/&amp;gt;
        )}
      &amp;lt;/Switch&amp;gt;
    &amp;lt;/Router&amp;gt;
  )
}

export default AppRouter&lt;/code&gt;&lt;/pre&gt;</description>
      <category>노마드코더/트위터 클론코딩</category>
      <category>Firebase</category>
      <category>react</category>
      <category>노마드 코더</category>
      <category>리액트</category>
      <category>트위터</category>
      <category>파이어베이스</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/242</guid>
      <comments>https://geonlee.tistory.com/242#entry242comment</comments>
      <pubDate>Wed, 8 Sep 2021 02:03:14 +0900</pubDate>
    </item>
    <item>
      <title>로컬 브렌치와 같은 이름의 리모트 브렌치에 푸쉬하게 설정하기</title>
      <link>https://geonlee.tistory.com/241</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;534&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXEkbs/btrd0XIRuNM/N64coTXjWYrfj9W5jNTE0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXEkbs/btrd0XIRuNM/N64coTXjWYrfj9W5jNTE0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXEkbs/btrd0XIRuNM/N64coTXjWYrfj9W5jNTE0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXEkbs%2Fbtrd0XIRuNM%2FN64coTXjWYrfj9W5jNTE0K%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;534&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;p data-ke-size=&quot;size16&quot;&gt;로컬에서 새로운 브랜치에 작업한 내용을 리모트 브랜치에 푸쉬하는 경우 해당 로컬 브랜치에 업스트림 브랜치를 설정하지 않았다면, 다음과 같은 Git 오류가 뜹니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;fatal: 현재 브랜치 add/join에 업스트림 브랜치가 없습니다. 현재 브랜치를 푸시하고 해당 리모트를 업스트림으로 지정하려면 다음과 같이 하십시오.

git push --set-upstream origin add/join&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 오류 메시지에 나온대로 현재 브렌치에 해당하는 리모트 저장소의 브렌치를 업스트림으로 설정해도 됩니다. 하지만, 매번 브렌치를 파서 작업할때마다 해당 작업을 하는 건 시간 낭비라고 느껴서 해결책을 검색해봤습니다. 해결책은 생각보다 간단했습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;git config --global push.default current&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 명령어를 통한 git 설정 한 번으로 업스트림 설정 없이 현재 브렌치와 같은 이름으로 리모트에 푸시할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://schacon.github.io/git/git-config.html&quot;&gt;git config&lt;/a&gt;을 확인하면 해당 설정과 관련된 다양한 옵션을 확인할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;push.default&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;refspec에 대한 설정이 어떤 경로로도 주어지지 않았을 때의 동작을 결정한다. 다음은 push.default로 지정 가능한 값들이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nothing - 어떤 커밋도 푸시하지 않는다.&lt;/li&gt;
&lt;li&gt;matching - 리모트/로컬 브랜치가 서로 일치할 경우에만 푸시한다. (기본)&lt;/li&gt;
&lt;li&gt;upstream - 현재 브랜치를 지정해둔 upstream 브랜치로 푸시한다.&lt;/li&gt;
&lt;li&gt;tracking - (deprecated) upstream과 동일&lt;/li&gt;
&lt;li&gt;current - 현재 브랜치와 같은 이름으로 리모트에 푸시한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Git</category>
      <category>Git</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/241</guid>
      <comments>https://geonlee.tistory.com/241#entry241comment</comments>
      <pubDate>Tue, 7 Sep 2021 00:37:15 +0900</pubDate>
    </item>
    <item>
      <title>가상 오피스: 폰출근의 시대</title>
      <link>https://geonlee.tistory.com/240</link>
      <description>&lt;h1&gt;출근 VS 재택근무&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;492&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQ7WHK/btrcrN26L09/zbdRL18htulwmfmK8hsP90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQ7WHK/btrcrN26L09/zbdRL18htulwmfmK8hsP90/img.png&quot; data-alt=&quot;재택근무&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQ7WHK/btrcrN26L09/zbdRL18htulwmfmK8hsP90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQ7WHK%2FbtrcrN26L09%2FzbdRL18htulwmfmK8hsP90%2Fimg.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;492&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;figcaption&gt;재택근무&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사회적 거리두기 단계가 점점 올라가면서 기존에 출근을 고수하던 회사들까지 재택을 고려하는 시대가 도래했다. 필자의 회사도 올해 한 번도 회사에 출근하지 않고 재택을 하고 있으며 올해까지는 재택 근무으로 진행하기로 결정했다. 필자야 사회 진출과 함께 코로나가 찾아오면서 회사 근무 = 재택 수준이 되었지만, 기존에 회사 근무 = 출근 이라는 공식을 몇 십년간 정설처럼 받아들인 이들에게는 재택이라는 환경은 많이 어색한 것 같다. 이렇듯 과거에 작성한 글 &lt;a href=&quot;https://geonlee.tistory.com/192&quot;&gt;코로나 시국 재택근무, 무조건 좋기만 할까?  &amp;zwj; &lt;/a&gt;에서도 언급했지만 재택이라는 상황은 출근과 여러 방면에서 다르다. &quot;다르다&quot;한 점이 중요한데, 출근 vs 재택근무 누가 더 좋다 말 할 수 없는 내용이고, 끝까지 결론이 안날 것이며 결국 각자의 장단점을 분석하고 최적의 근무 방식을 찾아나서는 것이 중요할 것이다.&lt;/p&gt;
&lt;h1&gt;가상 오피스에 폰출근하다.&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 움직임에서 실험적으로 나온 개념이 가상 오피스이다. 가상 오피스란 말 그대로 가상의 공간에 있는 오피스를 의미한다. &quot;가상&quot;이기 때문에 실제로 존재하지 않고 컴퓨터 상에서 웹을 통해 접속하고, &quot;오피스&quot;이기 때문에 우리가 오피스에 기대하는 작업공간, 휴식공간, 회의공간을 제공하고, 그 안에서 작업하고, 휴식하고, 회의를 진행한다. 물론 출근도 한다. 그저 웹에 접속하는 형태로도 하고, 가상의 오피스에 있는 아바타를 이용해 사원증을 태깅하기도 한다. 이러한 개념의 가상 오피스를 실제로 구현한 제품들이 등장하고 있는데 가장 유명한 게더타운을 소개해보고자한다.&lt;/p&gt;
&lt;h1&gt;&lt;a href=&quot;(https://www.gather.town/)&quot;&gt;게더타운(gather.town)&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;1514&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBUJOR/btrcs9x2zpY/T8ipLWZQGRpfpUjEvV8pX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBUJOR/btrcs9x2zpY/T8ipLWZQGRpfpUjEvV8pX0/img.png&quot; data-alt=&quot;게더타운&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBUJOR/btrcs9x2zpY/T8ipLWZQGRpfpUjEvV8pX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBUJOR%2Fbtrcs9x2zpY%2FT8ipLWZQGRpfpUjEvV8pX0%2Fimg.png&quot; data-origin-width=&quot;3456&quot; data-origin-height=&quot;1514&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;figcaption&gt;게더타운&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게더타운은 가상 오피스 제품 중에 현재 가장 인기있는 제품이다. 사용 방식은 간단하다. 로그인을 하고 기존에 템플릿으로 만들어진 맵 혹은 커스텀 맵 형태를 가진 방을 생성하고, 해당 방에 사람들을 초대하는 방식이다. 오피스 및 아바타는 마치 쯔꾸르 게임같은 2D 그래픽이고, 키보드를 이용해 맵 곳곳을 이동할 수 있다. 게더타운에서 내세우는 특징은 다음과 같다.&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;목적과 통합: 대화를 도울 수 있는 화이트보드, TV, 게임같은 다양한 도구들을 통해 대화를 브레인스토밍이나 친목도모를 위한 경쟁으로 이용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;커스터마이징: 전체 맵 및 내 주변의 공간을 자유롭게 꾸밀 수 있다. 게더타운에서는 우주 정거장에서 일하고 싶거나 루프탑 파티의 호스트도 될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;그래서 가상 오피스가 좋은가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 좋다 안좋다. 판단할 시점은 아닌 것 같다. 다만 &quot;가상 오피스&quot;라는 정확한 목적을 가진 제품이 있는 지가 의문이다. 게더타운의 경우에도 다른 아바타 주변에 있어야 대화가 가능하고, 카페 공간에 가면 백색소음이 들리는 등 &quot;출근의 경험&quot;을 느끼게 해주는 등 가상 오피스의 기능을 하지만, 가상 오피스는 사실 서브이고 언택트 상황에서의 새로운 커뮤니케이션 도구 정도로 느껴진다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;586&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vikuz/btrcs8FU9ac/zktoHlL7kIHPhAkOYFzlNk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vikuz/btrcs8FU9ac/zktoHlL7kIHPhAkOYFzlNk/img.jpg&quot; data-alt=&quot;메타버스를 달고 뜨고 있는 제페토&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vikuz/btrcs8FU9ac/zktoHlL7kIHPhAkOYFzlNk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVikuz%2Fbtrcs8FU9ac%2FzktoHlL7kIHPhAkOYFzlNk%2Fimg.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;586&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;figcaption&gt;메타버스를 달고 뜨고 있는 제페토&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼피레드라는 비슷한 개념의 게임이 몇 십년 전에 존재했음에도 제페토가 뜬금없이 &quot;메타버스&quot;라는 키워드를 달고 뜨고 있듯이. 게더타운도 &quot;가상 오피스&quot;라는 키워드를 달고 뜨고 있는 것 같다. 다만 메타버스보다 더 초기단계이다보니 &quot;가상 오피스&quot;를 구성하는 요소에 대한 정리가 안 된 느낌이고, &quot;가상 오피스&quot;만을 위한 제품들은 차차 만들어지고 있는 단계인 것 같다. 가상 오피스 개념을 더 발전시켜서 출근과 재택의 황금비를 찾은 궁극의 가상 오피스 제품이 나오길 기대하며 글을 마친다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;369&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cl5FYJ/btrci97hMGZ/lBQFnhSMy7RRVMkbwbqcd0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cl5FYJ/btrci97hMGZ/lBQFnhSMy7RRVMkbwbqcd0/img.jpg&quot; data-alt=&quot;3D 그래픽 형태의 직방 가상 오피스도 주목해볼만하다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cl5FYJ/btrci97hMGZ/lBQFnhSMy7RRVMkbwbqcd0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcl5FYJ%2Fbtrci97hMGZ%2FlBQFnhSMy7RRVMkbwbqcd0%2Fimg.jpg&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;369&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;figcaption&gt;3D 그래픽 형태의 직방 가상 오피스도 주목해볼만하다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Devlog</category>
      <category>가상 오피스</category>
      <category>재택</category>
      <category>재택근무</category>
      <category>출근</category>
      <category>폰출근</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/240</guid>
      <comments>https://geonlee.tistory.com/240#entry240comment</comments>
      <pubDate>Wed, 18 Aug 2021 02:02:48 +0900</pubDate>
    </item>
    <item>
      <title>지금 바로 사이트에 픽토그램 넣는 법 Font Awesome</title>
      <link>https://geonlee.tistory.com/239</link>
      <description>&lt;h1&gt;들어가며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹에서 앱과 같은 경험을 주고자하는 시도가 많아지면서, 웹사이트 제작시 아이콘은 필수적인 요소가 되었습니다. 아이콘 혹은 픽토그램은 사용자에게 전달하고자하는 복잡한 정보를 간단한 이미지로 보여줄 수 있습니다. &quot;메뉴모음&quot;이라는 단어가 아닌 햄버거 아이콘으로 모두가 이해하는 아이콘으로 보여줌으로써 사용자가 좀 더 깔끔한 웹사이트를 이용한다는 경험을 느끼게할 수 있습니다. 웹사이트에 픽토그램을 넣는 방법은 다양하게 있으나 주로 웹 아이콘 폰트를 이용하며, 일부 사용이 무료이면서 가장 유명한 것이 &lt;a href=&quot;https://fontawesome.com/&quot;&gt;Font Awesome&lt;/a&gt;입니다.&lt;/p&gt;
&lt;h1&gt;Font Awesome의 특징&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;3452&quot; data-origin-height=&quot;1306&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mQFx4/btra097lHWQ/ov8xGqNYENSqKNK0fmWag1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mQFx4/btra097lHWQ/ov8xGqNYENSqKNK0fmWag1/img.png&quot; data-alt=&quot;Font Awesome&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mQFx4/btra097lHWQ/ov8xGqNYENSqKNK0fmWag1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmQFx4%2Fbtra097lHWQ%2Fov8xGqNYENSqKNK0fmWag1%2Fimg.png&quot; data-origin-width=&quot;3452&quot; data-origin-height=&quot;1306&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;figcaption&gt;Font Awesome&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. HTML의 font 스타일 속성이 모두 사용 가능&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Font Awesome은 이미지가 아닌 웹 &lt;b&gt;폰트&lt;/b&gt;이기 때문에 HTML의 font 스타일을 모두 적용할 수 있습니다. 이게 무슨 장점이냐 할 수 있겠지만, 반대로 이미지였을 때를 생각해봅시다. 우리는 픽토그램이 웹사이트에서 다양한 방식으로 보여지길 원합니다. 크기가 크거나 작았으면 좋겠고, 색상도 내가 원하는 색으로 나왔으면 합니다. 주변 폰트하고의 조화도 무시할 수 없겠죠? 만약 이미지형식이라면 이 모든 것을 작업할 때 추가 작업이 필요하거나 심지어 비슷한 모양의 여러 색깔을 가진 이미지를 가지고 있어야하는 리소스 낭비를 할 수도 있습니다. 하지만, Font Awesome은 간단하게 스타일에 몇 줄 정도만 추가하면 크키는 물론이고 색상도 변경할 수 있습니다. 정말 Font Awesome이 아닐 수 없습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 브라우저 호환성&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;372&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuARvt/btra9WM04mE/yEX1JknKqLojQ9SbJRgoek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuARvt/btra9WM04mE/yEX1JknKqLojQ9SbJRgoek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuARvt/btra9WM04mE/yEX1JknKqLojQ9SbJRgoek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuARvt%2Fbtra9WM04mE%2FyEX1JknKqLojQ9SbJRgoek%2Fimg.png&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;372&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;p data-ke-size=&quot;size16&quot;&gt;한국에는 IE라는 악마가 살아남아서, 프론트엔드 개발자들의 소중한 시간을 잡아먹는다고 합니다. 그러나 Font Awesome은 JavaScript가 전혀 사용되지 않아서 호환성에 문제가 없기 때문에 Font Awesome에서는 악마의 장난질로부터 벗어날 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 무료&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취미로 사이트를 만드는데 아이콘에까지 돈을 쓰기에는 애매할 것입니다. Font Awesome의 아이콘은 모두 무료는 아니지만 기본적으로 쓰는 대부분의 아이콘들이 무료로 사용이 가능하기 때문에 아이콘 배포 목적이 아니라면 상업적인 용도로도 사용 가능합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 검색 용이성&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2600&quot; data-origin-height=&quot;1144&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/twF0e/btraWTR392A/QqlHOd73LvM7277xXxcFLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/twF0e/btraWTR392A/QqlHOd73LvM7277xXxcFLk/img.png&quot; data-alt=&quot;햄버거는 사실 햄버거가 아니였다는 사실 알고 계셨나요?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/twF0e/btraWTR392A/QqlHOd73LvM7277xXxcFLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtwF0e%2FbtraWTR392A%2FQqlHOd73LvM7277xXxcFLk%2Fimg.png&quot; data-origin-width=&quot;2600&quot; data-origin-height=&quot;1144&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;figcaption&gt;햄버거는 사실 햄버거가 아니였다는 사실 알고 계셨나요?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Font Awesome에서 사용할 수 있는 아이콘들은 Font Awesome 웹사이트에서 검색이 가능하며 비슷한 검색어로 추천도 해주기 때문에 원하는 아이콘을 쉽게 찾을 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 다양한 환경에서의 사용법&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2982&quot; data-origin-height=&quot;1560&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WNetH/btra6IuU97B/yVikgQMUo35iXX61eRZjCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WNetH/btra6IuU97B/yVikgQMUo35iXX61eRZjCK/img.png&quot; data-alt=&quot;디자인에서 개발까지 다양한 사용 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WNetH/btra6IuU97B/yVikgQMUo35iXX61eRZjCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWNetH%2Fbtra6IuU97B%2FyVikgQMUo35iXX61eRZjCK%2Fimg.png&quot; data-origin-width=&quot;2982&quot; data-origin-height=&quot;1560&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;figcaption&gt;디자인에서 개발까지 다양한 사용 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Font Awesome은 다양한 환경에서 사용할 수 있는 사용법들을 가지고 있습니다. 직접 다운로드 받아서 사용하는 것부터, npm을 이용하는 방법, React에서 사용하는 방법 등 다양한 웹 프로젝트 부터 Figma 같은 프로토타입 툴에서 사용하는 방법까지 웬만한 웹 디자인~개발 툴에서 두루두루 사용할 수 있게하여서 연속성을 높였습니다.&lt;/p&gt;</description>
      <category>Web/HTML</category>
      <category>Font Awesome</category>
      <category>개발자</category>
      <category>디자인</category>
      <category>아이콘</category>
      <category>웹폰트</category>
      <category>폰트어썸</category>
      <category>프론트엔드</category>
      <category>픽토그램</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/239</guid>
      <comments>https://geonlee.tistory.com/239#entry239comment</comments>
      <pubDate>Mon, 2 Aug 2021 22:52:36 +0900</pubDate>
    </item>
    <item>
      <title>더 많이 더 적게: 고장나버린 사이트 살려야한다</title>
      <link>https://geonlee.tistory.com/238</link>
      <description>&lt;h1&gt;고장나버린 사이트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거의 방치하고 있던 &lt;a href=&quot;https://www.higherlowerkorea.com/&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;1742&quot; data-origin-height=&quot;1005&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zJXSW/btran9VFqrI/pzpyj1pFI7x0Dd6AHMJi1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zJXSW/btran9VFqrI/pzpyj1pFI7x0Dd6AHMJi1k/img.png&quot; data-alt=&quot;?!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zJXSW/btran9VFqrI/pzpyj1pFI7x0Dd6AHMJi1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzJXSW%2Fbtran9VFqrI%2Fpzpyj1pFI7x0Dd6AHMJi1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1742&quot; height=&quot;1005&quot; data-origin-width=&quot;1742&quot; data-origin-height=&quot;1005&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;b&gt;&quot;문제가 생겼습니다.&quot;&lt;/b&gt;라는 저 문구 저건 분명히 GraphQL 요청이 실패했을 때 뜨는 화면이였다. 타임어택 기능 이후로 방치는 하고 있었지만, 처음으로 흥한 소중한 사이드 프로젝트라 문제의 원인 파악에 나섰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 내가 사용하고 있었던 &lt;b&gt;GraphCMS&lt;/b&gt;라는 서비스에 있었다. GraphCMS의 과거 Lagacy 버전 프로젝트를 그대로 사용하고 있었는데, GraphCMS 측에서 Lagacy 프로젝트를 다 지워버린 것이다. 덕분에 사이트에 오류가 뜬 건 물론이고 내가 어렵게 모은 키워드 + 사진 + 검색량 자료도 날라갔다. 억울하긴 하지만, Lagacy 프로젝트를 계속 사용해 온 내 잘못도 있고 GraphCMS 측에서 경고도 계속 했을 것이다. 그렇게 더 많이 더 적게 사이트는 오류가 난 채로 굴러가고 있었다.(내 서버비와 도메인 비용도 날라갔고...) 얼마나 방치되었는지 구글에 &quot;더 많이 더 적게&quot;를 검색하면 &quot;더 많이 더 적게 오류&quot;가 연관 검색어인 수준이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJSQst/btrav7QaiLg/H57OJmMom3pt0yGu94uDO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJSQst/btrav7QaiLg/H57OJmMom3pt0yGu94uDO1/img.png&quot; data-alt=&quot;그 동안 허탕치신 분들 죄송합니다...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJSQst/btrav7QaiLg/H57OJmMom3pt0yGu94uDO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJSQst%2Fbtrav7QaiLg%2FH57OJmMom3pt0yGu94uDO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1404&quot; height=&quot;964&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;964&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그 동안 허탕치신 분들 죄송합니다...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;살려야한다&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nGnY3/btraSZcA2cs/FbKqjAf1Z1kAFq48YhIQtk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nGnY3/btraSZcA2cs/FbKqjAf1Z1kAFq48YhIQtk/img.jpg&quot; data-alt=&quot;살려야한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nGnY3/btraSZcA2cs/FbKqjAf1Z1kAFq48YhIQtk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnGnY3%2FbtraSZcA2cs%2FFbKqjAf1Z1kAFq48YhIQtk%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;740&quot; height=&quot;317&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;317&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;이렇게 사이트를 방치할 수 없다고 생각해서 이 문제를 해결하기로 결심했다. 프론트 엔드 쪽 로직은 기존에 문제없이 잘 짜여져있으니 GraphQL로 가져오던 데이터를 어떻게 가져올 지가 문제였다. 기존에 사용했던 것처럼 CMS를 사용하는 방식은 이제 선택지에 없다. 이번 Lagacy를 없애버리는 케이스도 있고, 이전에 CMS를 조사하면서 느낀거지만 내가 원하는 목적 대비 고스펙에 취미 수준으로 사용하기에는 가격도 만만치 않았다. 그러다가 아주 간단하게 해결할 방법을 찾았다. &lt;b&gt;굳이 데이터를 외부에서 가져올 필요가 없었던 것이다.&lt;/b&gt; 프론트엔드 프로젝트에 static하게 데이터를 가지고 있다가 쓰는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 프론트엔드 프로젝트에 static하게 데이터를 가지고 있는 방법을 프로젝트 초창기에도 사용했었다. 하지만 정말 멍청하게 이미지파일까지 프로젝트에 넣으면서 무수한 트래픽의 요청으로 허겁지겁 CMS를 이용해서 트래픽을 줄였었다. 그렇게 관성적으로 CMS를 사용하다가 이번 문제까지 생긴 것이다. 그래서 이번에는 프론트엔드 내의 static 데이터를 사용하면서 사진은 프론트엔드 프로젝트에 넣지 않는 방식으로 구현해야했다. 그래서 이미지 서버를 사용하기로 결정을... 하려 했으나 과거 프로젝트의 데이터를 만들면서 들었던 노가다가 생각났다. 과거의 노가다는 이러했다. 키워드를 정하고, 구글에 검색, 검색량 복사, 이미지 찾아서 이미지 다운 &amp;amp; CMS 업로드 이렇게 모든 키워드 반복이다. 이미지 서버를 사용하게 된다해도 노가다는 사라지지 않을 것이다. 오히려 다운 &amp;amp; CMS 업로드에서 이미지 URL 복사 붙여넣기 작업까지 추가되는 꼴이라 그 노가다를 다시는 하고 싶지 않았다. 그래서 찾은 것이 &lt;b&gt;크롤링&lt;/b&gt;이다.&lt;/p&gt;
&lt;h1&gt;크롤링&lt;/h1&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;name: 키워드명&lt;/li&gt;
&lt;li&gt;&lt;b&gt;count: 검색량&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;categories: 카테고리 목록(신규추가)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;image.url: 이미지 주소&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;1176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn3wzQ/btraVpoxmMY/orlbF1yqKwOyE2ENY4oVw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn3wzQ/btraVpoxmMY/orlbF1yqKwOyE2ENY4oVw1/img.png&quot; data-alt=&quot;키워드 인터페이스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn3wzQ/btraVpoxmMY/orlbF1yqKwOyE2ENY4oVw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn3wzQ%2FbtraVpoxmMY%2ForlbF1yqKwOyE2ENY4oVw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1390&quot; height=&quot;1176&quot; data-origin-width=&quot;1390&quot; data-origin-height=&quot;1176&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;여기에서 키워드명과 카테고리 목록은 수작업으로 넣고 검색량(count)과 이미지 주소(image.url)는 크롤링을 통해 해결할 예정이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1724&quot; data-origin-height=&quot;456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxsLOh/btraQq2PM8j/hGiQWjCnd1JRRmK23rsz3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxsLOh/btraQq2PM8j/hGiQWjCnd1JRRmK23rsz3k/img.png&quot; data-alt=&quot;업데이트 전 키워드 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxsLOh/btraQq2PM8j/hGiQWjCnd1JRRmK23rsz3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxsLOh%2FbtraQq2PM8j%2FhGiQWjCnd1JRRmK23rsz3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1724&quot; height=&quot;456&quot; data-origin-width=&quot;1724&quot; data-origin-height=&quot;456&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;크롤링의 대상은 기존에 구글 데이터를 사용했기 때문에 구글을 대상으로 하였고, 검색량과 이미지 주소가 비어있는 전체 json를 넣으면 크롤링을 통해 해당하는 값을 업데이트해서 최종적으로는 해당 값들이 포함된 json을 얻게 되는 것이다. 크롤링 코드는 그나마 익숙한 파이썬으로 짰고, 인터넷에 올라온 여러 코드들을 짜깁기했다. 최종적으로 나온 코드는 다음과 같다. 시간이 여유롭지 않아서 코드 최적화나 논리에 이상한 부분이 생길 것 같아 코드는 안올리려했는데 누군가 도움이 되지 않을까 싶어서 올려본다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from urllib.parse import quote_plus
from selenium import webdriver
import urllib.request
import re
import json
from time import sleep
import time
import os
from datetime import datetime


GOOGLE_SEARCH_BASE_URL = 'https://www.google.com/search?q='
GOOGLE_SEARCH_IMAGE_BASE_URL = 'https://www.google.co.kr/imghp?hl=ko'
TARGET_JSON_FILE = 'keywords.json'


def get_keywords():
    with open(TARGET_JSON_FILE) as json_file:
        json_data = json.load(json_file)
    return json_data


def initWebDriver():
    driver = webdriver.Chrome(executable_path='./chromedriver')
    return driver


def createFolder(directory):
    try:
        if not os.path.exists(directory):
            os.makedirs(directory)
    except OSError:
        print('Error: Creating directory. ' + directory)


def getKeywordCount(keywordName, driver):

    searchUrl = GOOGLE_SEARCH_BASE_URL + quote_plus(keywordName)

    keywordCount = 0

    while True:
        driver.get(searchUrl)

        # 검색어 횟수 획득
        try:
            keywordCount = int(driver.find_element_by_xpath(
                '//*[@id = &quot;result-stats&quot;]').text.split(' ')[2][0:-1].replace(',', ''))
            return keywordCount
        except:
            continue


def getImageUrls(keywordName, driver):

    driver.implicitly_wait(3)

    print(f'[{keywordName}] 검색')

    driver.get(GOOGLE_SEARCH_IMAGE_BASE_URL)

    Keyword = driver.find_element_by_xpath('//*[@id=&quot;sbtc&quot;]/div/div[2]/input')
    Keyword.send_keys(keywordName)

    driver.find_element_by_xpath('//*[@id=&quot;sbtc&quot;]/button').click()

    imageUrls = []

    i = 1
    try:
        while True:
            # 썸네일 이미지 클릭
            driver.find_element_by_xpath(
                '//*[@id=&quot;islrg&quot;]/div[1]/div['+str(i)+']/a[1]/div[1]/img').click()

            # 원본 이미지 기다리기
            sleep(2)

            # 원본 이미지 url 획득
            imageUrl = driver.find_element_by_xpath(
                '//*[@id=&quot;Sva75c&quot;]/div/div/div[3]/div[2]/c-wiz/div/div[1]/div[1]/div[2]/div[1]/a/img').get_attribute('src')

            # 이미지로딩이 완전히 되지 않는 경우가 있어서 이미지 url이 1000보다 낮아야 획득
            if len(imageUrl) &amp;lt; 1000:
                imageUrls.append(imageUrl)
                break
            driver.find_element_by_xpath(
                '//*[@id=&quot;Sva75c&quot;]/div/div/div[2]/a').click()
            i += 1

    except:
        print(f'[{keywordName}] 링크 수집 실패')

    return imageUrls


def app():

    # chrome driver 시작
    driver = initWebDriver()

    keywords = get_keywords()

    print(f'키워드: 총 {str(len(keywords))}개')

    for index, keyword in enumerate(keywords):
        # 키워드
        keywordName = keyword['name']

        # 검색어 횟수 획득
        keywordCount = getKeywordCount(keywordName, driver)

        # 원본 이미지 링크들 획득
        imageUrls = getImageUrls(keywordName, driver)

        # 기존 키워드 배열 수정
        keywords[index]['image']['url'] = imageUrls[0]
        keywords[index]['count'] = keywordCount

        print(f'[{keywordName}] 업데이트 완료')

        sleep(1)

    with open(datetime.now().strftime('%Y-%m-%d %H:%M:%S') + &quot;_keywords.json&quot;, &quot;w&quot;, encoding='utf8') as json_file:
        json.dump(keywords, json_file, ensure_ascii=False)
    print('키워드 전체 업데이트 완료')

    driver.close()


app()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSpBEO/btraNIv7EW7/CCC87TfQAkzXQZ8OzGQdx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSpBEO/btraNIv7EW7/CCC87TfQAkzXQZ8OzGQdx0/img.png&quot; data-alt=&quot;업데이트 후 키워드 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSpBEO/btraNIv7EW7/CCC87TfQAkzXQZ8OzGQdx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSpBEO%2FbtraNIv7EW7%2FCCC87TfQAkzXQZ8OzGQdx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1456&quot; height=&quot;420&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;업데이트 후 키워드 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;추후 개선사항&lt;/h1&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;API를 안타다보니 키워드를 업데이트 할 때마다 항상 프로젝트를 배포해야한다. vercel를 이용하면 CI/CD가 자동으로 적용되어서, push하면 자동으로 배포되긴 하지만, 키워드 업데이트로 commit history가 오염된다. 해결 방법은 API 서버를 하나 두는 건데 서버 구축하기는 귀찮고... 구글 스프레드 시트 API를 사용을 고려해보고 있다. 다만 조회량의 제한이 있고 복잡한 오브젝트를 쓰기 힘들다는 점이 좀 아쉽다. 추가적으로 아무리 텍스트만으로 이루어진 스테틱데이터도 요청이 많아지면 트래픽이 꽤 쌓여서 금전적인 문제도 생긴다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구글 이미지의 첫번째 이미지는 가장 좋은 이미지인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크롤링을 통해 구글 이미지 검색의 가장 첫번째 이미지를 가져오도록 했는데, 크기가 매우 작은 이미지나 가끔 구토를 유발하는 비율을 가진 이미지를 가져오는 경우가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1ZvBv/btraVU2ViX3/piO0sjO2ZvvnSNzV1pMov1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1ZvBv/btraVU2ViX3/piO0sjO2ZvvnSNzV1pMov1/img.jpg&quot; data-alt=&quot;블리츠 키워드로 가져온 이미지인데 비율이 쉣이다...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1ZvBv/btraVU2ViX3/piO0sjO2ZvvnSNzV1pMov1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1ZvBv%2FbtraVU2ViX3%2FpiO0sjO2ZvvnSNzV1pMov1%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;940&quot; height=&quot;940&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;블리츠 키워드로 가져온 이미지인데 비율이 쉣이다...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;마무리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 카테고리 필드를 추가했는데 카테고리별 더 많이 더 적게로 이용할 예정이다. 업데이트는 언제할지는 나도 모르겠다 ㅎㅎ 그때까지 많은 이용 부탁드린다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.higherlowerkorea.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.higherlowerkorea.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1627637479594&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;higherlowerkorea.com&quot; data-og-source-url=&quot;https://www.higherlowerkorea.com/&quot; data-og-url=&quot;https://higherlowerkorea.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/csuR4f/hyK3KDYTVx/yvkwjpkNcAcrOBW3h3RHWk/img.png?width=1280&amp;amp;height=1280&amp;amp;face=224_428_538_834,https://scrap.kakaocdn.net/dn/l7Dmq/hyK3EqbbxI/xkPq51ksjdr4xlcy0kvXkK/img.png?width=506&amp;amp;height=281&amp;amp;face=0_0_506_281,https://scrap.kakaocdn.net/dn/i0lnz/hyK3KKJGcH/cRjXmYGl4u0nMyJf2lrki0/img.png?width=456&amp;amp;height=282&amp;amp;face=0_0_456_282&quot;&gt;&lt;a href=&quot;https://www.higherlowerkorea.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.higherlowerkorea.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/csuR4f/hyK3KDYTVx/yvkwjpkNcAcrOBW3h3RHWk/img.png?width=1280&amp;amp;height=1280&amp;amp;face=224_428_538_834,https://scrap.kakaocdn.net/dn/l7Dmq/hyK3EqbbxI/xkPq51ksjdr4xlcy0kvXkK/img.png?width=506&amp;amp;height=281&amp;amp;face=0_0_506_281,https://scrap.kakaocdn.net/dn/i0lnz/hyK3KKJGcH/cRjXmYGl4u0nMyJf2lrki0/img.png?width=456&amp;amp;height=282&amp;amp;face=0_0_456_282');&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;higherlowerkorea.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Side Project</category>
      <category>개선</category>
      <category>고장난시계</category>
      <category>더 많이 더 적게</category>
      <category>더많이더적게</category>
      <category>문제해결</category>
      <category>사이드프로젝트</category>
      <category>침착맨</category>
      <category>토이프로젝트</category>
      <category>프론트엔드</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/238</guid>
      <comments>https://geonlee.tistory.com/238#entry238comment</comments>
      <pubDate>Fri, 30 Jul 2021 18:33:36 +0900</pubDate>
    </item>
    <item>
      <title>VSCode 설정 및 확장 프로그램 동기화하기 (feat. Settings Sync)</title>
      <link>https://geonlee.tistory.com/237</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;⚠️ 현재는 확장프로그램인 Setting Sync가 아닌 VSCode에서 자체적으로 동기화 기능을 제공하고 있기 때문에 더이상 Setting Sync를 통해 동기화할 필요가 없습니다.&lt;/blockquote&gt;
&lt;h1&gt;들어가며  &lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 환경에 들어갔을 때 IDE를 다운받아서 처음부터 환경설정을 다시 하는 작업은 굉장히 귀찮은 작업이다. 이는 웹에서 자주 사용하는 IDE인 VSCode에서도 마찬가지인데, 자신만의 단축키나 특히 VSCode의 장점인 확장프로그램을 사용하는 경우 하나하나 이를 입력 및 설치하는 건 정말 힘든 일이다. VSCode에서 이러한 동기화 작업을 할 수 있는 방법에 대해 알아보자!&lt;/p&gt;
&lt;h1&gt;Settings Sync ♻️&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2318&quot; data-origin-height=&quot;1518&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ocD74/btqVnX82eIz/KZCdh1KZmCNGDtjWSrAoNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ocD74/btqVnX82eIz/KZCdh1KZmCNGDtjWSrAoNK/img.png&quot; data-alt=&quot;Settings Sync&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ocD74/btqVnX82eIz/KZCdh1KZmCNGDtjWSrAoNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FocD74%2FbtqVnX82eIz%2FKZCdh1KZmCNGDtjWSrAoNK%2Fimg.png&quot; data-origin-width=&quot;2318&quot; data-origin-height=&quot;1518&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Settings Sync&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1612179771488&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Settings Sync - Visual Studio Marketplace&quot; data-og-description=&quot;Extension for Visual Studio Code - Synchronize Settings, Snippets, Themes, File Icons, Launch, Keybindings, Workspaces and Extensions Across Multiple Machines Using GitHub Gist.&quot; data-og-host=&quot;marketplace.visualstudio.com&quot; data-og-source-url=&quot;https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync&quot; data-og-url=&quot;https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FYIfl/hyI83EL61D/akcRKf6qk5fRZ7xmHfYbAk/img.png?width=128&amp;amp;height=128&amp;amp;face=0_0_128_128&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FYIfl/hyI83EL61D/akcRKf6qk5fRZ7xmHfYbAk/img.png?width=128&amp;amp;height=128&amp;amp;face=0_0_128_128');&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;Settings Sync - Visual Studio Marketplace&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Extension for Visual Studio Code - Synchronize Settings, Snippets, Themes, File Icons, Launch, Keybindings, Workspaces and Extensions Across Multiple Machines Using GitHub Gist.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;marketplace.visualstudio.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode에서 환경 동기화를 위해 필요한 확장 프로그램이다. 해당 확장프로그램을 이용하면 다음에 대한 설정을 동기화할 수 있다.&lt;/p&gt;
&lt;ol 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;VSCode 확장 프로그램 &amp;amp; 확장 프로그램 설정들&lt;/li&gt;
&lt;li&gt;워크 스페이스 폴더&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 페이지에 가서 설치하거나 VSCode 내의 확장프로그램 검색에서 Settings Sync를 찾아서 설치해보자. 설치가 완료되었다면 &quot;show all command&quot; (Ctrl + Shift + P)를 누르고 &quot;sync&quot; 를 입력한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;610&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TO5v0/btqVtaNhJum/4QTJMmQs1Xq0PSa6iKBEk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TO5v0/btqVtaNhJum/4QTJMmQs1Xq0PSa6iKBEk0/img.png&quot; data-alt=&quot;Sync: Reset Extension Setting&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TO5v0/btqVtaNhJum/4QTJMmQs1Xq0PSa6iKBEk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTO5v0%2FbtqVtaNhJum%2F4QTJMmQs1Xq0PSa6iKBEk0%2Fimg.png&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;610&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sync: Reset Extension Setting&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 기존에 VSCode에 정보가 있다면 &quot;Sync: Reset Extension Settings&quot; 를 통해 삭제하도록 한다.&lt;/p&gt;
&lt;h1&gt;gist 생성을 위한 토큰 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Settings Sync는 VSCode의 설정 정보들을 Github의 gist에 저장한다. 따라서 나의 Github에서 gist를 생성할 수 있는 토큰을 만들어야한다. 토큰을 생성하기 위해서는 &quot;Github &amp;gt; Settings &amp;gt; Developer Settings &amp;gt; Personal access tokens&quot; 로 들어간다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;1555&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cs2v6L/btqVsw35AZc/JobWV8kr58EQ2htOSkAvp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cs2v6L/btqVsw35AZc/JobWV8kr58EQ2htOSkAvp0/img.png&quot; data-alt=&quot;Access Token 생성하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cs2v6L/btqVsw35AZc/JobWV8kr58EQ2htOSkAvp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcs2v6L%2FbtqVsw35AZc%2FJobWV8kr58EQ2htOSkAvp0%2Fimg.png&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;1555&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Access Token 생성하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 우리가 필요한 토큰인 gist 생성에 대한 권한만 체크하고 &quot;Generate token&quot; 버튼을 누른다. 이제 업로드를 위한 준비는 마무리되었다.&lt;/p&gt;
&lt;h1&gt;현재 설정 업로드하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 설정을 업로드(Upload Key)하기 위해 다음과 같은 커맨드를 입력한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;윈도우: Shift + Alt + U&lt;/li&gt;
&lt;li&gt;macOS: Shift + Option + U&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 토큰이 없기 때문에 Settings Sync에서는 토큰을 등록하고 해당 토큰을 이용하여 gist를 만드는 과정을 거친 후 자동으로 해당 세팅에 토큰과 gist id가 저장된다. 최종적으로 Settings Sync 설정을 보면 Gist ID와 Access Token이 등록된 모습이 보인다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2332&quot; data-origin-height=&quot;1762&quot; data-filename=&quot;스크린샷 2021-02-01 오후 8.21.51 1.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE9NAj/btqVkKozClh/9bY57jNJLSCg2sS2X4ANBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE9NAj/btqVkKozClh/9bY57jNJLSCg2sS2X4ANBk/img.png&quot; data-alt=&quot;Settings Sync&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE9NAj/btqVkKozClh/9bY57jNJLSCg2sS2X4ANBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE9NAj%2FbtqVkKozClh%2F9bY57jNJLSCg2sS2X4ANBk%2Fimg.png&quot; data-origin-width=&quot;2332&quot; data-origin-height=&quot;1762&quot; data-filename=&quot;스크린샷 2021-02-01 오후 8.21.51 1.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Settings Sync&lt;/figcaption&gt;
&lt;/figure&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;Auto Download - VSCode가 시작될 때마다 자동으로 설정을 다운로드하여 동기화된다.&lt;/li&gt;
&lt;li&gt;Auto Upload - VSCode의 설정이 바뀔 때마다 자동으로 설정을 업로드한다.&lt;/li&gt;
&lt;li&gt;Force Download - 다른 최신 세팅이 있어도 항상 설정을 다운로드하여 동기화한다.&lt;/li&gt;
&lt;li&gt;Force Upload - Gist에 새로운 세팅이 있어도 항성 현재 설정을 업로드한다.&lt;/li&gt;
&lt;li&gt;Quiet Sync - 요약 페이지 대신에 상태바를 보여준다.&lt;/li&gt;
&lt;li&gt;Remove Extensions - 다운로드 중에 확장 프로그램을 삭제할 수 있게 해준다.&lt;/li&gt;
&lt;li&gt;Sync Extensions - 확장 프로그램을 업로드/다운로드하여 동기화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 이 중 Force ~ 로 시작하는 것 이외에는 다 체크해서 사용 중이다.&lt;/p&gt;
&lt;h1&gt;다른 환경에서 동기화하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 환경에서도 위에 언급한 방법으로 Settings Sync 확장 프로그램을 설치한다. 그 뒤, gist에 있는 설정을 다운로드(Download Key)하기 위해 다음과 같은 커맨드를 입력한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;윈도우: Shift + Alt + D&lt;/li&gt;
&lt;li&gt;macOS: Shift + Option + D&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;똑같이 Settings Sync와 Github 연동페이지가 나오고 해당 연동이 마무리되면 gist에 올라간 설정을 다운로드 받아서 동기화가 마무리된다.&lt;/p&gt;</description>
      <category>IDE/VS Code</category>
      <category>GIST</category>
      <category>github</category>
      <category>IDE</category>
      <category>Settings Sync</category>
      <category>sync</category>
      <category>vsCode</category>
      <category>동기화</category>
      <category>맥</category>
      <category>윈도우</category>
      <category>환경</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/237</guid>
      <comments>https://geonlee.tistory.com/237#entry237comment</comments>
      <pubDate>Mon, 1 Feb 2021 20:44:25 +0900</pubDate>
    </item>
    <item>
      <title>공개 API를 이용한 공개 API를 이용한 공개 API 검색 서비스 만들기</title>
      <link>https://geonlee.tistory.com/236</link>
      <description>&lt;h1&gt;내가 데이터 고자라니!  &lt;/h1&gt;
&lt;p&gt;2020년 아무런 사이드 프로젝트도 안했다는 &lt;b&gt;자책감&lt;/b&gt;에 2021년부터 이것저것 만들어보자고 생각을 했다. 회사에서는 백/프론트 가리지 않고 하고는 있지만 그래도 나는 프론트엔드를 더 잘 알고 좋아하는 프론트엔드 가이이다. 그러나 모든 프론트엔드 개발자들이 알고 있듯, 프론트엔드 개발자가 사이드 프로젝트 하는데에는 한 가지 문제가 있다! 그것은 바로 &lt;b&gt;&quot;보여줄 데이터가 없다는것&quot;&lt;/b&gt;  &lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJsjiR/btqVkLHvMfV/K5n9SprL80EGgQIc0WdDGk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJsjiR/btqVkLHvMfV/K5n9SprL80EGgQIc0WdDGk/img.jpg&quot; data-alt=&quot;내가 데이터 고자라니!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJsjiR/btqVkLHvMfV/K5n9SprL80EGgQIc0WdDGk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJsjiR%2FbtqVkLHvMfV%2FK5n9SprL80EGgQIc0WdDGk%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내가 데이터 고자라니!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;Open API를 찾아나서는 여정  &lt;/h1&gt;
&lt;p&gt;이전에도 정말 정말 간단한 백엔드를 구축해서 사용하거나, &lt;a href=&quot;https://graphcms.com/&quot;&gt;GraphCMS&lt;/a&gt; 같은 CMS를 사용해서&lt;a href=&quot;https://higherlowerkorea.com/&quot;&gt;더 많이 더 적게&lt;/a&gt;같은 프로젝트도 진행했다. 그마저 안되면 넷상에 공개된 API Open Api(혹은 Public Api)를 사용하곤 해왔다.그런데 Open API를 찾는 것도 쉽지가 않았다. 계속 서칭을 해보다가 &lt;a href=&quot;https://github.com/public-apis/public-apis&quot;&gt;public-apis&lt;/a&gt; 라는 Open API를 문서화해서 잘 정리한 Github 프로젝트를 찾아서 잘 사용하고 있었다.&lt;/p&gt;
&lt;h1&gt;없으면 만들자 한글로 된 공개 API 저장소  &lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/public-apis/public-apis&quot;&gt;public-apis&lt;/a&gt; 는 좋았지만 한가지 문제가 있었다. 바로 내용이 모두 영어로 되어있었다는 것... 누군가 한글로 정리했겠지 싶었는데 그마저도 없었다. &lt;b&gt;그래서 만들었다.&lt;/b&gt; 그 뒤로&lt;a href=&quot;https://github.com/dl0312/open-apis-korea&quot;&gt;open-apis-korea&lt;/a&gt; 라는 프로젝트로 기존 프로젝트를 클론하여 하나 하나 번역했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2JaI2/btqVh8bVMS7/UymFYJfkhaAQeJZiZcmynk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2JaI2/btqVh8bVMS7/UymFYJfkhaAQeJZiZcmynk/img.png&quot; data-alt=&quot;open-apis-korea&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2JaI2/btqVh8bVMS7/UymFYJfkhaAQeJZiZcmynk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2JaI2%2FbtqVh8bVMS7%2FUymFYJfkhaAQeJZiZcmynk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;open-apis-korea&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1612163118379&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;object&quot; data-og-title=&quot;dl0312/open-apis-korea&quot; data-og-description=&quot;  한국어 사용자를 위한 서비스에 사용하기 위한 오픈 API 모음. Contribute to dl0312/open-apis-korea development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/dl0312/open-apis-korea&quot; data-og-url=&quot;https://github.com/dl0312/open-apis-korea&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1ZvaC/hyI8ZWxEfS/TSc1zCGsn24FQyAs2wj0f0/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://github.com/dl0312/open-apis-korea&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/dl0312/open-apis-korea&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1ZvaC/hyI8ZWxEfS/TSc1zCGsn24FQyAs2wj0f0/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;dl0312/open-apis-korea&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;  한국어 사용자를 위한 서비스에 사용하기 위한 오픈 API 모음. Contribute to dl0312/open-apis-korea development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;초반 몇 달동안 매일매일 번역하는 고생을 해서 현재는 모든 API들이 번역되어있는 상태이다. 혹시 번역상 이상한 부분이 있다면 이슈를 보내도록하자!&lt;/p&gt;
&lt;h1&gt;인간의 욕심은 끝이 없고, 같은 사이드 프로젝트를 반복한다.  &lt;/h1&gt;
&lt;p&gt;&lt;b&gt;그래서 위 저장소로 한글로 다 정리하니 행복해졌나?&lt;/b&gt; 인간의 욕심은 끝이 없고 같은 사이드 프로젝트를 반복한다했는가?(이런 말은 없다) 다 번역하고 보니 &lt;b&gt;README.md&lt;/b&gt;에 정보를 모두 때려 넣어서 찾기도 힘들고 무엇보다 깃헙 저장소라 그런지 정말 못생겼다는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OwBtU/btqVsytOvRU/IFGDYDvEkwCJbvQokmRShK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OwBtU/btqVsytOvRU/IFGDYDvEkwCJbvQokmRShK/img.png&quot; data-alt=&quot;지금 봐도 너무 안 이쁘다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OwBtU/btqVsytOvRU/IFGDYDvEkwCJbvQokmRShK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOwBtU%2FbtqVsytOvRU%2FIFGDYDvEkwCJbvQokmRShK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;지금 봐도 너무 안 이쁘다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그래서 사이드 프로젝트도 할 겸 또 만들었다. 사이트로 기존에 존재하는 Public APIs를 위한 Public API인 &lt;a href=&quot;https://api.publicapis.org/&quot;&gt;Public API for Public APIs&lt;/a&gt; 라는 Open API를 이용하여 &lt;a href=&quot;https://open-apis.dev/&quot;&gt;open-apis.dev&lt;/a&gt; 웹사이트를 만들었다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1F9mc/btqVh7KOZS6/T48yTRpuL8970lYLkQCaW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1F9mc/btqVh7KOZS6/T48yTRpuL8970lYLkQCaW1/img.png&quot; data-alt=&quot;open-apis.dev&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1F9mc/btqVh7KOZS6/T48yTRpuL8970lYLkQCaW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1F9mc%2FbtqVh7KOZS6%2FT48yTRpuL8970lYLkQCaW1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;open-apis.dev&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1612163142392&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Open APIs&quot; data-og-description=&quot;Find some open APIs for your next projects&quot; data-og-host=&quot;open-apis.dev&quot; data-og-source-url=&quot;https://open-apis.dev/&quot; data-og-url=&quot;https://open-apis.dev/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QkfIu/hyI63TNsVw/vnRu8djU1EGKqK1FcwMRs0/img.jpg?width=2552&amp;amp;height=1699&amp;amp;face=0_0_2552_1699&quot;&gt;&lt;a href=&quot;https://open-apis.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open-apis.dev/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QkfIu/hyI63TNsVw/vnRu8djU1EGKqK1FcwMRs0/img.jpg?width=2552&amp;amp;height=1699&amp;amp;face=0_0_2552_1699');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Open APIs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Find some open APIs for your next projects&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;open-apis.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;API상에 구현된 기능이 필터링 검색, 랜덤 API 요청밖에 없어서 데이터 관련된 부분은 정말 간단하게 만들었다. 대신 다른 부분에 좀 더 노력을 투자했다.&lt;/p&gt;
&lt;h2&gt;다크모드&lt;/h2&gt;
&lt;p&gt;그 첫번째가 다크모드이다. 처음으로 개발자의 필수 기능 &lt;b&gt;다크모드&lt;/b&gt;도 시행착오 끝에 적용해보았는데, styled-components + next.js 조합으로 생각보다 하드 난이도로 구현이 되었다.&lt;/p&gt;
&lt;h2&gt;디자인&lt;/h2&gt;
&lt;p&gt;엄청 화려한 디자인은 아니지만, 최대한 정보를 가시적으로 볼 수 있도록 만드는 데에 초점을 두었다. 기본 컴포넌트들은 Ant Design을 통해서 만들었고, 필요한 CSS를 styled-components로 구현했다. 가장 마음에 드는 부분은 사이트 왼쪽에 favicon이 보여지는 부분이다. 해당 사이트의 정보를 미리 확인 할 수 있어서 정말 마음에 든다.&lt;/p&gt;
&lt;h2&gt;배포&lt;/h2&gt;
&lt;p&gt;회사에서 Next.js를 사용하고 있고, 보일러 플레이트도 만들어 놨지만 이번에 처음으로 Next.js로 배포와 서비스 작업까지 하게 되었다. 청므이라 우여곡절이 있지 않을까 싶었는데 의외로 스무스하게 진행되었다. Next.js를 만든 회사와 같은 기존에 써왔던 vercel을 통해 배포 및 도메인 구매까지 진행했는데 Github과도 통합성이 좋아서 배포 쪽으로 머리 아프게 고민할 부분이 없어서 정말 좋았다. 사용된 스택 및 라이브러리는 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Next.js - SSR이 가능한 React 프레임워크 규격화의 힘은 대단했다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TypeScript - 이제 이거 없으면 코딩 못한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ant Design - 1시간 걸려서 만들 컴포넌트 1분안에 만들어 낼 수 있는 마법의 UI 프레임워크&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;styled-components - 컴포넌트형으로 스타일링을 하게해주는 라이브러리&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;vercel - 배포가 이렇게 쉬워도 되나 싶을 정도로 쉽게 배포 가능한 서비스&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;TO DO ✍️&lt;/h1&gt;
&lt;p&gt;아쉽게도 해당 Public API 역시 영어로 된 데이터라 설명이 영어로 나오는데 간단한 서버를 구축해서 한글로도 데이터를 서빙할 수 있도록 추가 구현하려한다.&lt;/p&gt;</description>
      <category>Side Project</category>
      <category>API</category>
      <category>Next.js</category>
      <category>open API</category>
      <category>public api</category>
      <category>react</category>
      <category>vercel</category>
      <category>개방형 API</category>
      <category>공개 api</category>
      <category>사이드 프로젝트</category>
      <category>프론트엔드</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/236</guid>
      <comments>https://geonlee.tistory.com/236#entry236comment</comments>
      <pubDate>Mon, 1 Feb 2021 16:02:18 +0900</pubDate>
    </item>
    <item>
      <title>Next.js에 Tailwind CSS 적용하기</title>
      <link>https://geonlee.tistory.com/235</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSeRtB/btqT91y2DMh/Yly6cPJ2AQ2lFeWZKU1Lp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSeRtB/btqT91y2DMh/Yly6cPJ2AQ2lFeWZKU1Lp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSeRtB/btqT91y2DMh/Yly6cPJ2AQ2lFeWZKU1Lp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSeRtB%2FbtqT91y2DMh%2FYly6cPJ2AQ2lFeWZKU1Lp0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;Tailwind CSS&lt;/h1&gt;
&lt;p&gt;Tailwind CSS는 Bootstrap과 흡사한 UI 프레임워크 입니다. Tailwind CSS는 커스터마이징이 가능한 저수준 CSS 프레임 워크로, 맞춤형 디자인을 작성하는 데 필요한 모든 빌딩 블록을 제공합니다. 대부분의 UI 프레임워크의 경우 미리 디자인 되어 있는 구성요소가 있어 처음에는 빠른 장점이 있습니다. 하지만, 사이트를 사용자가 지정한 디자인으로 제작하고 싶은 경우에는 많은 제약이 따릅니다. Tailwind CSS는 HTML을 떠나지 않고도 커스터마이징 가능한 디자인을 만들 수 있는 저수준 유틸리티 클래스를 제공합니다.&lt;/p&gt;
&lt;h1&gt;npm/yarn을 통해 Tailwind 의존성 설치하기&lt;/h1&gt;
&lt;p&gt;Tailwind CSS 패키지를 프로젝트에 설치합니다.&lt;/p&gt;
&lt;h2&gt;npm을 사용하는 경우&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# Next.js v10 버전인 경우
npm install tailwindcss@latest postcss@latest autoprefixer@latest

# Next.js v9 버전 이하인 경우
npm install tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;yarn을 사용하는 경우&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# Next.js v10 버전인 경우
yarn add tailwindcss@latest postcss@latest autoprefixer@latest

# Next.js v9 버전 이하인 경우
yarn add tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;설정 파일 생성하기&lt;/h1&gt;
&lt;p&gt;다음으로 &lt;code&gt;tailwind.config.js&lt;/code&gt; 와 &lt;code&gt;postcss.config.js&lt;/code&gt; 파일을 생성합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx tailwindcss init -p&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;해당 커맨드를 입력하면 최소 구조의 &lt;code&gt;tailwind.config.js&lt;/code&gt; 파일을 프로젝트 최상단에 생성합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// tailwind.config.js
module.exports = {
  purge: [],
  darkMode: false, // or &amp;#39;media&amp;#39; or &amp;#39;class&amp;#39;
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또한, &lt;code&gt;tailwindcss&lt;/code&gt;와 &lt;code&gt;autoprefixer&lt;/code&gt;가 이미 설정된 &lt;code&gt;postcss.config.js&lt;/code&gt; 파일도 생성됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;사용하지 않는 스타일을 프로덕션에서 제거하기 위해 Tailwind 설정하기&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;tailwind.config.js&lt;/code&gt; 파일에 &lt;code&gt;purge&lt;/code&gt; 옵션에 Tailwind를 사용할 모든 페이지와 컴포넌트를 설정합니다. 이렇게 하면 사용하지 않는 스타일을 프로덕션 빌드에서 트리-세이킹 해서 빌드 크기를 최적화할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// tailwind.config.js
module.exports = {
  purge: [&amp;#39;./pages/**/*.js&amp;#39;, &amp;#39;./components/**/*.js&amp;#39;],
  darkMode: false, // or &amp;#39;media&amp;#39; or &amp;#39;class&amp;#39;
  theme: {
      extend: {},
  },
  variants: {
      extend: {},
  },
  plugins: [],
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;CSS에 Tailwind 추가하기&lt;/h1&gt;
&lt;p&gt;Next.js 프로젝트의 CSS에 Tailwind를 추가할 때에는 2가지 방법이 있습니다.&lt;/p&gt;
&lt;h2&gt;Tailwind를 JS에 직접 추가하기&lt;/h2&gt;
&lt;p&gt;만약 프로젝트에 커스텀 CSS를 추가할 예정이 없다면, 가장 빠르게 Tailwind를 추가하는 방법은 &lt;code&gt;pages/\_app.js&lt;/code&gt;에 직접 &lt;code&gt;import&lt;/code&gt;하는 것입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// pages/_app.js
import &amp;quot;tailwindcss/tailwind.css&amp;quot;;

function MyApp({ Component, pageProps }) {
    return &amp;lt;Component {...pageProps} /&amp;gt;
}

export default MyApp&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Tailwind를 CSS에 추가하기&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;./styles/globals.css&lt;/code&gt;를 열어서 Tailwind의 &lt;code&gt;base&lt;/code&gt;, &lt;code&gt;components&lt;/code&gt;, &lt;code&gt;utilities&lt;/code&gt; 스타일을 &lt;code&gt;@tailwind&lt;/code&gt; directive를 이용해서 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;/* ./styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tailwind는 빌드 시간에 @tailwind directive를 모두 디자인 시스템에 설정된 대로 스타일을 변경합니다. 이와 같이 했을 때 CSS에서는 @tailwind directive에 대한 정보가 없기 때문에 &lt;code&gt;Unknown at rule @tailwind&lt;/code&gt; 라는 경고 메시지가 뜨게됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biHd46/btqUeVStpeo/BT6f8xIwy2SH2YqZG5i6G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biHd46/btqUeVStpeo/BT6f8xIwy2SH2YqZG5i6G0/img.png&quot; data-alt=&quot;Unknown at rule @tailwind&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biHd46/btqUeVStpeo/BT6f8xIwy2SH2YqZG5i6G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiHd46%2FbtqUeVStpeo%2FBT6f8xIwy2SH2YqZG5i6G0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Unknown at rule @tailwind&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;VSCode 기준으로 해당 문제를 해결하려면 우선 &lt;code&gt;.vscode/setting.json&lt;/code&gt;에 다음을 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;{
  &amp;quot;css.customData&amp;quot;: [&amp;quot;.vscode/css_custom_data.json&amp;quot;]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 &lt;code&gt;.vscode/css\_custom\_data.json&lt;/code&gt;에 다음을 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;{
  &amp;quot;atDirectives&amp;quot;: [
    {
      &amp;quot;name&amp;quot;: &amp;quot;@tailwind&amp;quot;,
      &amp;quot;description&amp;quot;: &amp;quot;Use the @tailwind directive to insert Tailwind’s `base`, `components`, `utilities`, and `screens` styles into your CSS.&amp;quot;,
      &amp;quot;references&amp;quot;: [
        {
          &amp;quot;name&amp;quot;: &amp;quot;Tailwind’s “Functions &amp;amp; Directives” documentation&amp;quot;,
          &amp;quot;url&amp;quot;: &amp;quot;https://tailwindcss.com/docs/functions-and-directives/#tailwind&amp;quot;
        }
      ]
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;해당 설정을 저장하고 VSCode를 재실행하면 다음과 같이 @tailwind directive에 경고 메시지가 사라진 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boCK8G/btqUhAUxkNU/6gxCRiyXpDVJKRyfCEF1iK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boCK8G/btqUhAUxkNU/6gxCRiyXpDVJKRyfCEF1iK/img.png&quot; data-alt=&quot;@tailwind directive에 경고 메시지가 사라진 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boCK8G/btqUhAUxkNU/6gxCRiyXpDVJKRyfCEF1iK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboCK8G%2FbtqUhAUxkNU%2F6gxCRiyXpDVJKRyfCEF1iK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;@tailwind directive에 경고 메시지가 사라진 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이제 최종적으로 해당 CSS 파일을 `pages/_app.js` 컴포넌트에 추가하여 작업을 마무리합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import &amp;#39;../styles/globals.css&amp;#39;

function MyApp({ Component, pageProps }) {
  return &amp;lt;Component {...pageProps} /&amp;gt;
}

export default MyApp&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Web/Next.js</category>
      <category>bootstrap</category>
      <category>CSS</category>
      <category>Next.js</category>
      <category>react</category>
      <category>Tailwind</category>
      <category>tailwindcss</category>
      <category>UI 프레임워크</category>
      <category>리액트</category>
      <category>부트스트랩</category>
      <category>테일윈드</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/235</guid>
      <comments>https://geonlee.tistory.com/235#entry235comment</comments>
      <pubDate>Thu, 21 Jan 2021 18:22:14 +0900</pubDate>
    </item>
    <item>
      <title>input 태그의 type 속성 종류와 예제</title>
      <link>https://geonlee.tistory.com/234</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;HTML에서 양식을 만들 때, &lt;code&gt;form&lt;/code&gt; 태그를 사용하고, &lt;code&gt;input&lt;/code&gt; 태그를 통해 입력 영역을 만듭니다. 입력 영역의 종류는 다양하며 &lt;code&gt;type&lt;/code&gt; 속성의 값에 따라 동작 방식이 현격히 달라집니다. 특성을 지정하지 않는 경우 기본값은 &lt;code&gt;text&lt;/code&gt;입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;input 태그의 type 속성값&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;code&gt;text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;기본값&lt;/b&gt;으로 한 줄의 텍스트 입력 칸을 만듭니다. (기본 너비 문자는 20입니다.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;password&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;text&lt;/code&gt; 속성과 같지만, 입력 문자를 별표(*)로 대체해서 표시합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;checkbox&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;선택 항목 중 여러개를 선택할 수 있는 체크박스를 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;radio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;선택 항목 중 1가지만 선택 가능한 라디오 버튼을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;button&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;누를 수 있는 기본 버튼을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;submit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;전송 버튼을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;재설정 버튼을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;파일 선택창을 여는 버튼을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hidden&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사용자에게 보이지 않는 숨김 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;이미지로 된 전송 버튼을 만듭니다. (&lt;code&gt;src&lt;/code&gt; 속성을 통해 이미지 주소을 지정합니다.)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이하는 HTML5에 새로 추가된 속성값입니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&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;code&gt;color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;색상 선택 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;date&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;년, 월, 일을 입력할 수 있는 날짜 입력 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;datetime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;년, 월, 일, 시, 분, 초, 초의 분할까지 입력할 수 있는 표준시간날짜시간 입력 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;datetime-local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;년, 월, 일, 시, 분, 초, 초의 분할까지 입력할 수 있는 표준시간이 아닌 날짜시간 입력 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;이메일 주소 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;month&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;년, 월 입력 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;숫자 입력을 위한 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;range&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;슬라이더같은 정확한 값이 중요하지 않는 숫자를 입력하는 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;검색창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;전화번호 입력창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;표준시간이 아닌 시간 입력 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;인터넷 주소 입력창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;week&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;표준시간이 아닌 년, 주 입력 창을 만듭니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;input type 속성별 예제&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;input type=&quot;text&quot;, type=&quot;password&quot;&lt;/h3&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;&amp;lt;form&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;text&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;id&quot;&amp;gt;아이디&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;text&quot; id=&quot;id&quot; /&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;password&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;password&quot;&amp;gt;비밀번호&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;password&quot; id=&quot;password&quot; /&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;326&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GMZ3q/btqT8I6Jpkk/oRwrdFyDPS9iDduaDptWo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GMZ3q/btqT8I6Jpkk/oRwrdFyDPS9iDduaDptWo1/img.png&quot; data-alt=&quot;input type=&amp;amp;quot;text&amp;amp;quot;, type=&amp;amp;quot;password&amp;amp;quot;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GMZ3q/btqT8I6Jpkk/oRwrdFyDPS9iDduaDptWo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGMZ3q%2FbtqT8I6Jpkk%2FoRwrdFyDPS9iDduaDptWo1%2Fimg.png&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;326&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;input type=&quot;text&quot;, type=&quot;password&quot;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입 혹은 로그인 기능에 필수인 필드입니다. 특별하게 type=&quot;password&quot;는 입력의 결과물을 마스킹해서보여줍니다. 따라서 겉으로 볼 때에는 ***으로 보이지만 실제 input 필드의 값은 입력한 값이 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;input type=&quot;checkbox&quot;&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;&amp;lt;form&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;checkbox&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;input type=&quot;checkbox&quot; name=&quot;todo&quot; value=&quot;study&quot;&amp;gt;study&amp;lt;br&amp;gt;
  &amp;lt;input type=&quot;checkbox&quot; name=&quot;todo&quot; value=&quot;sleep&quot;&amp;gt;sleep&amp;lt;br&amp;gt;
  &amp;lt;input type=&quot;checkbox&quot; name=&quot;todo&quot; value=&quot;eat&quot;&amp;gt;eat
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;258&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buSQ5c/btqT9Z8fzvl/GpoPLzGEUVotskKwEWlAJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buSQ5c/btqT9Z8fzvl/GpoPLzGEUVotskKwEWlAJK/img.png&quot; data-alt=&quot;input type=&amp;amp;quot;checkbox&amp;amp;quot; &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buSQ5c/btqT9Z8fzvl/GpoPLzGEUVotskKwEWlAJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuSQ5c%2FbtqT9Z8fzvl%2FGpoPLzGEUVotskKwEWlAJK%2Fimg.png&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;258&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;input type=&quot;checkbox&quot; &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터 체크 박스 혹은 할 일의 처리와 관련된 작업을 하고자 할 때 사용하는 타입입니다. &quot;체크&quot;와 &quot;언체크&quot; 작업을 할 수 있으며 name은 여러 checkbox input을 통일하는 이름을 할당하고 value에는 해당 체크박스에 해당하는 값을 할당합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;input type=&quot;radio&quot;&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;&amp;lt;form&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;radio&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;input type=&quot;radio&quot; name=&quot;company&quot; value=&quot;google&quot;&amp;gt;google&amp;lt;br&amp;gt;
  &amp;lt;input type=&quot;radio&quot; name=&quot;company&quot; value=&quot;naver&quot;&amp;gt;naver&amp;lt;br&amp;gt;
  &amp;lt;input type=&quot;radio&quot; name=&quot;company&quot; value=&quot;kakao&quot;&amp;gt;kakao
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;288&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbDdx8/btqUhAlOg6x/zKRm9jSnK66R0blx5cRnH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbDdx8/btqUhAlOg6x/zKRm9jSnK66R0blx5cRnH1/img.png&quot; data-alt=&quot;input type=&amp;amp;quot;radio&amp;amp;quot; &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbDdx8/btqUhAlOg6x/zKRm9jSnK66R0blx5cRnH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbDdx8%2FbtqUhAlOg6x%2FzKRm9jSnK66R0blx5cRnH1%2Fimg.png&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;288&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;input type=&quot;radio&quot; &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;checkbox 타입과 비슷하지만 다른 radio 타입은 하나의 관심사의 여러개의 선택지중 하나를 선택하고자 할 때 쓰이는 타입입니다. checkbox 타입과 비슷하게 name에는 여러 radio 타입을 통일하는 이름이 value에는 선택하고자하는 값이 할당됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;input type=&quot;button&quot;, type=&quot;submit&quot;, type=&quot;reset&quot;&lt;/h3&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;&amp;lt;form&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;submit&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;input type=&quot;submit&quot;&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;reset&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;input type=&quot;reset&quot;&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;button&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;input type=&quot;button&quot; value=&quot;click&quot;&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;536&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYL3v1/btqT91LJDhM/sIl7rGT8d5zMLepCyO34u1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYL3v1/btqT91LJDhM/sIl7rGT8d5zMLepCyO34u1/img.png&quot; data-alt=&quot;input type=&amp;amp;quot;button&amp;amp;quot;, type=&amp;amp;quot;submit&amp;amp;quot;, type=&amp;amp;quot;reset&amp;amp;quot; &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYL3v1/btqT91LJDhM/sIl7rGT8d5zMLepCyO34u1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYL3v1%2FbtqT91LJDhM%2FsIl7rGT8d5zMLepCyO34u1%2Fimg.png&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;536&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;input type=&quot;button&quot;, type=&quot;submit&quot;, type=&quot;reset&quot; &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 form tag와 함께 쓰이는 타입들입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;input type=&quot;file&quot;, type=&quot;search&quot;, type=&quot;url&quot;, type=&quot;email&quot;, type=&quot;tel&quot;&lt;/h3&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;&amp;lt;form&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;file&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;file&quot;&amp;gt;파일&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;file&quot; value=&quot;file&quot; id=&quot;file&quot; /&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;search&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;search&quot;&amp;gt;검색&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;search&quot; id=&quot;search&quot; /&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;url&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;url&quot;&amp;gt;URL&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;url&quot; id=&quot;url&quot; /&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;email&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;email&quot;&amp;gt;이메일&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;email&quot; id=&quot;email&quot; /&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;tel&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;tel&quot;&amp;gt;전화번호&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;tel&quot; id=&quot;tel&quot; /&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;848&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ecg4FK/btqT8IezNna/6BV6UO3AOJr3eBJ5B5jDN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ecg4FK/btqT8IezNna/6BV6UO3AOJr3eBJ5B5jDN0/img.png&quot; data-alt=&quot;input type=&amp;amp;quot;file&amp;amp;quot;, type=&amp;amp;quot;search&amp;amp;quot;, type=&amp;amp;quot;url&amp;amp;quot;, type=&amp;amp;quot;email&amp;amp;quot;, type=&amp;amp;quot;tel&amp;amp;quot; &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ecg4FK/btqT8IezNna/6BV6UO3AOJr3eBJ5B5jDN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fecg4FK%2FbtqT8IezNna%2F6BV6UO3AOJr3eBJ5B5jDN0%2Fimg.png&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;848&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;input type=&quot;file&quot;, type=&quot;search&quot;, type=&quot;url&quot;, type=&quot;email&quot;, type=&quot;tel&quot; &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;input type=&quot;datetime-local&quot;, type=&quot;month&quot;, type=&quot;week&quot;&lt;/h3&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;&amp;lt;form action=&quot;url&quot; method=&quot;post&quot;&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;datetime-local&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;datetime-local&quot;&amp;gt;날짜/시간&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;datetime-local&quot; id=&quot;datetime-local&quot; /&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;month&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;month&quot;&amp;gt;월&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;month&quot; id=&quot;month&quot; /&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;week&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;week&quot;&amp;gt;주&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;week&quot; id=&quot;week&quot; /&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;546&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VlBLs/btqUeXu1bEG/twvjMuXOMGk0MYxSsCZai0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VlBLs/btqUeXu1bEG/twvjMuXOMGk0MYxSsCZai0/img.png&quot; data-alt=&quot;input type=&amp;amp;quot;datetime-local&amp;amp;quot;, type=&amp;amp;quot;month&amp;amp;quot;, type=&amp;amp;quot;week&amp;amp;quot; &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VlBLs/btqUeXu1bEG/twvjMuXOMGk0MYxSsCZai0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVlBLs%2FbtqUeXu1bEG%2FtwvjMuXOMGk0MYxSsCZai0%2Fimg.png&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;546&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;input type=&quot;datetime-local&quot;, type=&quot;month&quot;, type=&quot;week&quot; &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;input type=&quot;color&quot;, type=&quot;range&quot;, type=&quot;number&quot;&lt;/h3&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;&amp;lt;form action=&quot;url&quot; method=&quot;post&quot;&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;color&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;color&quot;&amp;gt;색상&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;color&quot; id=&quot;color&quot; /&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;range&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;input type=&quot;range&quot; min=&quot;10&quot; max=&quot;20&quot; /&amp;gt;
  &amp;lt;h3&amp;gt;type=&quot;number&quot;&amp;lt;/h3&amp;gt;
  &amp;lt;label for=&quot;number&quot;&amp;gt;숫자(1~10)&amp;lt;/label&amp;gt;&amp;lt;input type=&quot;number&quot; min=&quot;1&quot; max=&quot;10&quot; id=&quot;number&quot; /&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;538&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d746Rw/btqT90TFW48/K59eHQn7Aa47kSJVk6eBH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d746Rw/btqT90TFW48/K59eHQn7Aa47kSJVk6eBH0/img.png&quot; data-alt=&quot;input type=&amp;amp;quot;color&amp;amp;quot;, type=&amp;amp;quot;range&amp;amp;quot;, type=&amp;amp;quot;number&amp;amp;quot; &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d746Rw/btqT90TFW48/K59eHQn7Aa47kSJVk6eBH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd746Rw%2FbtqT90TFW48%2FK59eHQn7Aa47kSJVk6eBH0%2Fimg.png&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;538&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;input type=&quot;color&quot;, type=&quot;range&quot;, type=&quot;number&quot; &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Web/HTML</category>
      <category>Color</category>
      <category>datetime</category>
      <category>form</category>
      <category>HTML</category>
      <category>html5</category>
      <category>input</category>
      <category>number</category>
      <category>web</category>
      <category>웹</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/234</guid>
      <comments>https://geonlee.tistory.com/234#entry234comment</comments>
      <pubDate>Thu, 21 Jan 2021 01:09:42 +0900</pubDate>
    </item>
    <item>
      <title>  2020  </title>
      <link>https://geonlee.tistory.com/233</link>
      <description>&lt;h1&gt;들어가며  &lt;/h1&gt;
&lt;p&gt;2020년이 지나갔다. 분명 2020년에 회고글을 쓰려했는데 멍때리다보니 내년이 와버렸다. 2020 개발자 회고와 2021의 목표를 중심으로 빠르게 적어보려한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;960&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KdJod/btqSgLQvp71/sQcvlHlfRcIbf9UbW423ZK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KdJod/btqSgLQvp71/sQcvlHlfRcIbf9UbW423ZK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KdJod/btqSgLQvp71/sQcvlHlfRcIbf9UbW423ZK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKdJod%2FbtqSgLQvp71%2FsQcvlHlfRcIbf9UbW423ZK%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;960&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;h1&gt;입사&lt;/h1&gt;
&lt;p&gt;가장 컷던 이벤트가 아닐까 싶다. 나도 내가 이렇게 빨리 내가 원하던 회사에 입사하게 될 줄은 몰랐다. 핵데이를 통해 입사했는데, 입사후에 감사하게도 연수를 모두 받게해주셔서 프론트엔드 전반과 사내 플랫폼들에 대해 많이 배울 수 있는 시간이 되었다. 물론, 같은 프론트엔드 교육을 들은 동기들과도 인연이 생기게 된 좋은 시간이 였다. 그렇게 그렇게 남들과 같은 신입 생활이 시작되는 줄 알았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ozT2J/btqRXH3yYcw/YqyfmMF9SDynAUXXqoxcvK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ozT2J/btqRXH3yYcw/YqyfmMF9SDynAUXXqoxcvK/img.jpg&quot; data-alt=&quot;평화로웠던 2020년 1월&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ozT2J/btqRXH3yYcw/YqyfmMF9SDynAUXXqoxcvK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FozT2J%2FbtqRXH3yYcw%2FYqyfmMF9SDynAUXXqoxcvK%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;평화로웠던 2020년 1월&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;관련 블로그 글&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/175&quot;&gt;2020 네이버 신입 입문 프로그램 시작  &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;코로나와 재택&lt;/h1&gt;
&lt;p&gt;코로나가 갑자기 터지면서 연수 스케줄부터 바뀌었다. 연수동안 계획되었던 일본 라인 방문이 취소되었다. 당시 스케줄을 담당하시는 분은 얼마나 당황했을지 엄청 고생하셨을 것 같다... 연수 이후에 계획된 부트캠프도 상황이 심각해지면서 모두 재택으로 전환되었다. 이게 될까 싶었지만, 성공적으로 부트캠프를 마무리하고 연수까지 끝났다. 이후에도 출근 / 2일출근 + 3일재택 / 완전 재택을 넘나들며 출근 방식이 바뀌어갔고 현재는 상황이 심각해서 계속 완전 재택으로 일을 하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v22fA/btqRVqgD9b2/WQ9FSoTU6NB5Dzc3bOm0MK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v22fA/btqRVqgD9b2/WQ9FSoTU6NB5Dzc3bOm0MK/img.gif&quot; data-alt=&quot;주니어 + 재택이라니...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v22fA/btqRVqgD9b2/WQ9FSoTU6NB5Dzc3bOm0MK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/v22fA/btqRVqgD9b2/WQ9FSoTU6NB5Dzc3bOm0MK/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주니어 + 재택이라니...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;관련 블로그 글&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/188&quot;&gt;✍️ 코로나19 실시간 현황 사이트를 모아보자&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/192&quot;&gt;코로나 시국 재택근무, 무조건 좋기만 할까?  &amp;zwj; &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;서비스 출시  &lt;/h1&gt;
&lt;p&gt;입사 1년도 안되었는데 보이스뉴스라는 서비스를 출시했다. 당연히 큰 규모의 프로젝트는 아니였고, 기존에 부서에서 나왔던 아이디어에 신입 2명이서 한 번 구현해봐라~ 정도로 시작했던 프로젝트인데 결국 서비스로 출시까지 했다. 나는 프론트엔드로 해당 서비스에 필요한 어드민 페이지를 만들었다. 기술은 리액트와 타입스크립트 그리고 빠른 스타일링 및 기능구현을 위해 &lt;a href=&quot;https://ant.design/&quot;&gt;ant design&lt;/a&gt;을 사용하였다. 처음에는 전체적인 음원의 구성요소를 정하는데 오랜 시간이 걸렸고, 이후에는 백엔드의 배치 작업에서 오랜 작업이 걸렸다. 최종적으로 서비스는 문제없이 잘 나왔고, 계속 서비스되고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdECyt/btqR3RdsmjH/8wDtMUx142ayzvUNnAH7sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdECyt/btqR3RdsmjH/8wDtMUx142ayzvUNnAH7sk/img.png&quot; data-alt=&quot;보이스 뉴스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdECyt/btqR3RdsmjH/8wDtMUx142ayzvUNnAH7sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdECyt%2FbtqR3RdsmjH%2F8wDtMUx142ayzvUNnAH7sk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;보이스 뉴스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;관련 뉴스&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://news.naver.com/main/read.nhn?mode=LSD&amp;amp;mid=sec&amp;amp;sid1=105&amp;amp;oid=366&amp;amp;aid=0000564081&quot;&gt;네이버, 주요 뉴스 요약해 읽어주는 '보이스 뉴스' 서비스&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;자취  &lt;/h1&gt;
&lt;p&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/Iig3h/btqRVqA5PXK/hfBgMF6NkBlHDCssjTflz1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Iig3h/btqRVqA5PXK/hfBgMF6NkBlHDCssjTflz1/img.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Iig3h/btqRVqA5PXK/hfBgMF6NkBlHDCssjTflz1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIig3h%2FbtqRVqA5PXK%2FhfBgMF6NkBlHDCssjTflz1%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;0&quot; height=&quot;0&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyAjct/btqR1EesyIO/eGBFVdBUhKAAUH90gd5tHk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyAjct/btqR1EesyIO/eGBFVdBUhKAAUH90gd5tHk/img.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyAjct/btqR1EesyIO/eGBFVdBUhKAAUH90gd5tHk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyAjct%2FbtqR1EesyIO%2FeGBFVdBUhKAAUH90gd5tHk%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;0&quot; height=&quot;0&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;늘어난 건 뱃살과 요리식력 뿐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;블로그&lt;/h1&gt;
&lt;p&gt;블로그는 열심히 했는지 모르겠다. 작년의 과거의 나는 분명히 이렇게 적었다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;글을 쓸 시간은 좀 더 줄어들겠지만, 글을 쓸 경우 좀 더 전문적인 글, 나만이 쓸 수 있는 글을 썼으면 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw3L6S/btqRXIuD6w5/NVhwkbE9FVqZeKcmsq6GC0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw3L6S/btqRXIuD6w5/NVhwkbE9FVqZeKcmsq6GC0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw3L6S/btqRXIuD6w5/NVhwkbE9FVqZeKcmsq6GC0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw3L6S%2FbtqRXIuD6w5%2FNVhwkbE9FVqZeKcmsq6GC0%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;2019년에는 알고리즘 문제도 정리하고 기술 글도 쓰고 무엇보다 많이 썼는데 2020년 초에는 같이 블로그 글을 쓰는 모임이 있어서 하다가 그 이후에는 흐지부지 생각날 때만 몇몇 글자 적고 있다. 회사일에 바빠서라기보다는 회사일 끝나고 또 일 같은걸 하려니 뭔가 손에 안 잡히는 느낌이다. 그래도 몇몇 내가 생각하기에도 괜찮은 글들이 있어서 좋았다. 블로그 글 하나하나가 소중하고 적을 때마다 성장하는 걸 느껴서 내년에는 올해보다 좀 더 좋은 글을 썼으면한다.&lt;/p&gt;
&lt;h2&gt;열심히 쓴 글&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/184&quot;&gt;하루 아침에 코드가 꼴도 보기 싫어졌다 : 개발자 번아웃&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/190&quot;&gt;git 커밋 어떻게 써야하는가?  &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/192&quot;&gt;코로나 시국 재택근무, 무조건 좋기만 할까?  &amp;zwj; &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/205&quot;&gt;내가 메인 브라우저를 크롬에서 네이버 웨일로 갈아 탄 이유  &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/206&quot;&gt;당신의 Github을 매력적으로 보일 수 있는 방법들 ✨&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/208&quot;&gt;코로나에 대처하는 지도앱의 자세  &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;사이드 프로젝트&lt;/h1&gt;
&lt;p&gt;또 과거의 내가 무슨 말을 했는 지 확인해보자&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;사이드 프로젝트도 계속 병행하며 새로운 기술에 대한 감을 잃지 않았으면 좋겠다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;결론적으로 말하자면 2020년에 사이드 프로젝트로 무언가를 만들지는 못했다! 다만 그 기반이 되는 프로젝트를 몇 개 작업했다. 우선 예전부터 보일러플레이트로 계속 유지보수하고 있는 &lt;a href=&quot;https://github.com/dl0312/react-typescript-boilerplate&quot;&gt;react-typescript-boilerplate&lt;/a&gt;를 좀 더 바로 쓸 수 있게 작업했다. 라이브러리들도 업데이트하고 기본으로 다크모드를 지원할 수 있도록 적용하고 각종 lint들도 수정 및 파비콘도 바로 적용할 수 있게 만들었다. 유지보수해놓은 덕분에 실제 업무에서도 해당 프로젝트를 이용해서 시작을 할 수 있었다. 그리고 프로젝트의 유지를 받들어 최근에 사용하고 있는 프로젝트인 Next.js의 보일러플레이트인 &lt;a href=&quot;https://github.com/dl0312/nextjs_boilerplate&quot;&gt;nextjs_boilerplate&lt;/a&gt;를 만들고 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 2020년초에는 &lt;a href=&quot;https://github.com/dl0312/open-apis-korea&quot;&gt;open-apis-korea&lt;/a&gt;라는 프로젝트를 만들었는데, 프론트엔드를 주로 하다보니 백엔드 서버 만드는 게 귀찮을 때는 public api들을 사용하는 편이였다. 그런데 github 프로젝트중에 public api를 한글로 알려주는 프로젝트가 하나도 없어서 &lt;a href=&quot;https://github.com/public-apis/public-apis&quot;&gt;public-apis&lt;/a&gt;라는 기존의 외국 프로젝트를 포크받아서 매일 한땀한땀 번역을 하고 이모지들은 넣는 작업을 진행했다. 2월정도에 마무리가 된 프로젝트인데 현재는 star가 300개가 넘는 효자 프로젝트이다. 내가 언제 이런 스타수를 받아볼까 싶다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzzWx0/btqSjBz4Q5S/pObfM3i0UFL8uSZUxPjDu0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzzWx0/btqSjBz4Q5S/pObfM3i0UFL8uSZUxPjDu0/img.jpg&quot; data-alt=&quot;이 모든건 내 년 사이드 프로젝트를 위한 추진력을 얻기 위함이었다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzzWx0/btqSjBz4Q5S/pObfM3i0UFL8uSZUxPjDu0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzzWx0%2FbtqSjBz4Q5S%2FpObfM3i0UFL8uSZUxPjDu0%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이 모든건 내 년 사이드 프로젝트를 위한 추진력을 얻기 위함이었다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;내년에는 해당 프로젝트를 기반으로 하나라도 재미있는 프로젝트를 만들어보고 싶다.&lt;/p&gt;
&lt;h2&gt;관련 프로젝트 링크&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dl0312/react-typescript-boilerplate&quot;&gt;react-typescript-boilerplate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dl0312/nextjs_boilerplate&quot;&gt;nextjs_boilerplate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dl0312/open-apis-korea&quot;&gt;open-apis-korea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/public-apis/public-apis&quot;&gt;public-apis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;관련 블로그 글&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://geonlee.tistory.com/187&quot;&gt;내가 쓰고 싶은 데이터 오픈 API에서 찾아보자!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;  21년을 바라보며&lt;/h1&gt;
&lt;p&gt;일단 2021년에는 코로나가 좀 사라졌으면 좋겠다. 집돌이라 집에 있는게 익숙하지만 집돌이도 가끔 바깥 공기가 그리운 법이다. 그리고 2020년 이루지 못했던 목표들을 21년에는 조금 더 열심히 살아서 이루어서 계속 성장하는 한 해가 되었으면 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nDKyX/btqR9lZnwu1/KuNj5jwlxcBNrx69xQznwk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nDKyX/btqR9lZnwu1/KuNj5jwlxcBNrx69xQznwk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nDKyX/btqR9lZnwu1/KuNj5jwlxcBNrx69xQznwk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnDKyX%2FbtqR9lZnwu1%2FKuNj5jwlxcBNrx69xQznwk%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Devlog/회고록</category>
      <category>2020</category>
      <category>2021</category>
      <category>개발자</category>
      <category>개발자 회고</category>
      <category>블로그</category>
      <category>재택근무</category>
      <category>코로나</category>
      <category>회고</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/233</guid>
      <comments>https://geonlee.tistory.com/233#entry233comment</comments>
      <pubDate>Fri, 1 Jan 2021 15:32:59 +0900</pubDate>
    </item>
    <item>
      <title>돌고 도는 가트너의 하이프 사이클</title>
      <link>https://geonlee.tistory.com/232</link>
      <description>&lt;p&gt;최근 &lt;a href=&quot;https://news.naver.com/main/read.nhn?mode=LSD&amp;amp;mid=sec&amp;amp;sid1=105&amp;amp;oid=009&amp;amp;aid=0004712044&quot;&gt;블록체인 기사&lt;/a&gt;를 읽던 도중 당최 모르는 용어가 나왔다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;최근 블록체인 프로젝트가 다시 조명을 받고 있는데요. 블록체인 기술은 어떻게 보세요?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;저희가 매년 1000팀에서 1500팀의 기술 스타트업을 만납니다. 지금까지 한 6000~7000팀 정도 만났는데요. 이렇게 만나다 보면 기술 스타트업에 대한 직관이 생깁니다.&lt;/p&gt;
&lt;p&gt;기술에는 사이클이 있어요. 어떤 기술이 나오면 많은 사람이 그 기술을 찬양하고, 개발해 시장에 내놓으면서 혁신이라 주장하는 과정이 존재하죠. AI와 가상현실(VR)이 그랬고, 블록체인도 &lt;b&gt;가트너의 하이프 사이클&lt;/b&gt;을 따르고 있는 것 같아요.&lt;/p&gt;
&lt;p&gt;초기에 시장의 기대치가 높을 때는 기술 상용화 가능성이 검증되지 않아 불확실성이 높습니다. 실패 사례가 늘어남에 따라 사람들의 관심이 잠잠해지고, 수면 아래에서 성숙기를 거치는 동안 기술 상용화 가능성에 근접하게 되죠. 블록체인 기술도 이런 성숙기 과정을 거치고 있다고 생각해요.&amp;rdquo;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;출처: &lt;a href=&quot;https://news.naver.com/main/read.nhn?mode=LSD&amp;amp;mid=sec&amp;amp;sid1=105&amp;amp;oid=009&amp;amp;aid=0004712044&quot;&gt;네이버가 블록체인 스타트업에 관심을 가진 이유는?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;'가트너의 하이프 사이클? IT업계와 관련된 용어같은데 설명도 없을 정도면 나만 모르는건가...'&lt;/p&gt;
&lt;p&gt;이런 궁금증을 가지며 해당 용어에 대해 찾아보았다.&lt;/p&gt;
&lt;h2&gt;가트너의 하이프 사이클&lt;/h2&gt;
&lt;p&gt;가트너(Gartner)는 1979년 설립된 미국의 정보 기술 연구 자문회사 혹은 IT 컨설팅 회사로 컨설팅을 주로 하다보니 IT 업계의 시장조사, 연구조사에서도 세계적인 인지도를 가지고 있다. 그 &lt;b&gt;가트너&lt;/b&gt;사에서 만든 개념이 바로 하이프 사이클이다.&lt;/p&gt;
&lt;h2&gt;하이프 사이클(Hype Cycle)&lt;/h2&gt;
&lt;p&gt;하이프 사이클은 가트너사에서 처음으로 만든 개념으로 기술의 성숙도를 표현하기 위한 시각적 도구이다. &lt;b&gt;하이프(Hype)&lt;/b&gt; 는 &lt;b&gt;과장된, 부풀려진&lt;/b&gt; 이라는 뜻으로, 하이프 사이클은 &lt;b&gt;과장 주기&lt;/b&gt; 정도로 번역된다. 즉, 기술에 대한 &lt;b&gt;시장의 과도한 기대&lt;/b&gt;가 어떤 주기로 반복되는 지 &lt;b&gt;경험적&lt;/b&gt;으로 정리한 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;실험을 통해 과학적으로 증명된 것이 아니기 때문에 지나보면 빗나간 예측도 많지만, 거시적으로 봤을 때는 거의 일치하기 때문에 전략수립시 많이 참고되는 자료이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&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/DPCVW/btqPKuZexS8/hq0tJDQ1Zzpf4d7cPvfvE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DPCVW/btqPKuZexS8/hq0tJDQ1Zzpf4d7cPvfvE1/img.png&quot; data-alt=&quot;가트너의 하이프 사이클&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DPCVW/btqPKuZexS8/hq0tJDQ1Zzpf4d7cPvfvE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDPCVW%2FbtqPKuZexS8%2Fhq0tJDQ1Zzpf4d7cPvfvE1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;가트너의 하이프 사이클&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;하이프 사이클은 시간에 따른 시장의 기대치를 보여주는 그래프로 5단계의 기술 성장 주기로 이루어져있다.&lt;/p&gt;
&lt;h3&gt;기술 촉발(Technology Trigger)&lt;/h3&gt;
&lt;p&gt;잠재적 기술이 관심을 받기 시작하는 시기이다. 하지만, 초기 단계의 가설과 소설같은 개념적 모델과 미디어의 관심이 대중의 관심을 불러 일으켜 시장의 기대는 과도해지지만 아직 상용화된 제품은 없고, 상업적 가치도 증명되지 않은 상태이다. 즉, 미디어에서 신기술이 세상을 바꿀 것이라고 이야기하고 사람들이 관심을 가지는 시기이다.&lt;/p&gt;
&lt;h3&gt;부풀려진 기대의 절정(Peak of Inflated Expectations)&lt;/h3&gt;
&lt;p&gt;초기의 대중성을 통해 나온 선도 업체에서 일부의 성공적 사례와 다수의 실패 사례를 양산해 내는 시기이다. 일부 기업이 실제 사업에 착수하지만, 대부분의 기업은 관망만 한다.&lt;/p&gt;
&lt;h3&gt;환멸의 계곡(Trough of Disillusionment)&lt;/h3&gt;
&lt;p&gt;대부분의 실험 및 구현이 결과물을 내놓는 데 실패함에 따라 관심이 시들시들해지는 시기이다. 제품화를 시도한 기업들은 포기하거나 실패하고, 살아남은 사업 주체들이 소비자를 만족시킬만한 제품의 향상에 성공한 경우에만 투자가 계속된다.&lt;/p&gt;
&lt;h3&gt;계몽 단계(Slope of Enlightment)&lt;/h3&gt;
&lt;p&gt;기술의 수익 모델(BM, Business Model)이 보여주는 좋은 사례들이 늘어나고 더 잘 이해되기 시작하는 시기이다. 2~3세대에 해당하는 제품들이 출시되고, 더 많은 기업들이 본격적으로 사업에 투자를 시작한다. 그러나 보수적인 기업들은 여전히 투자에 유보적인 입장을 취한다.&lt;/p&gt;
&lt;h3&gt;생산성 안정 단계(Plateau of Productivity)&lt;/h3&gt;
&lt;p&gt;기술이 시장의 &lt;b&gt;주류&lt;/b&gt;로 자리잡기 시작하는 시기이다. 사업자의 생존 가능성을 평가하기 위한 명확한 기준이 생기고, 시장에서도 성과를 거두기 시작한다.&lt;/p&gt;
&lt;p&gt;하이프 사이클이라는 용어와 이를 구성하는 기술 성장 주기 5단계는 신기술 마케팅에서 점차 더 널리 사용되고 있다.&lt;/p&gt;
&lt;h2&gt;떠오르는 기술 하이프 사이클, 2020&lt;/h2&gt;
&lt;p&gt;2020년 올해에도 가트너에서는 기술에 대한 하이프 사이클 보고서를 발표했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&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/W0GWc/btqPIJvOiVA/hcLJ5a2Y3ts1pybhOGetok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W0GWc/btqPIJvOiVA/hcLJ5a2Y3ts1pybhOGetok/img.png&quot; data-alt=&quot;떠오르는 기술 하이프 사이클, 2020&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W0GWc/btqPIJvOiVA/hcLJ5a2Y3ts1pybhOGetok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW0GWc%2FbtqPIJvOiVA%2FhcLJ5a2Y3ts1pybhOGetok%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;떠오르는 기술 하이프 사이클, 2020&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;떠오르는 기술들을 하이프 사이클의 알맞은 주기 위에 위치하였고, 각 기술별로 마지막 단계인 생산성 안정 단계에 도달하기까지 얼마나 걸릴 지에 대해서도 표기를 해놓았다.&amp;nbsp;&lt;/span&gt;역시 대세인 AI 관련 기술이 주를 이루고 있으며 시장 기대의 가장 정점에 있는 기술은 보안 서비스 기술인 새시(SASE, Secure Access Service Edge)와 사회적 거리두기 기술(Social Distancing Technologies)가 차지했다.&lt;/p&gt;
&lt;h2&gt;하이프 사이클이 개발자 시장에 미치는 영향&lt;/h2&gt;
&lt;p&gt;하이프 사이클은 통해 개발자는 본인이 가진 기술에 따라 개발자 시장의 수요를 예측할 수 있다. 크게 다음 4가지 케이스를 통해 시장에서 기술자나 기술회사를 찾는 수요가 높아진다. 즉, 이직기회가 높아지고, 기술 가격 혹은 연봉이 높아지는 것이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시스템을 신규로 구축하려는 사람은 가능하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;뜨는 기술&lt;/b&gt;을 써보고 싶어한다.&lt;/li&gt;
&lt;li&gt;일반 조직은 해당 조직이 당면한 문제를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;신기술&lt;/b&gt;의 도입을 통해 풀어내고자한다.&lt;/li&gt;
&lt;li&gt;정부는 혁신 견인 차원에서 같은 값이면 신기술을 도입해보고자하고, 사회 경쟁력 강화 차원에서 &lt;b&gt;신기술&lt;/b&gt; 연구개발을 지원하고자 한다.&lt;/li&gt;
&lt;li&gt;기술 창업자는 가능하면 &lt;b&gt;신기술&lt;/b&gt;을 사용하여 시장의 주목도를 높이고자 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;하이프 사이클에 대한 비판&lt;/h2&gt;
&lt;p&gt;만능일 것 같은 하이프 사이클도 여러가지 측면에서 비판을 받아 왔다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;과학적, 실험적 데이터가 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;경험적&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;li&gt;유사한 성격의 방법론인 기술 준비 지수(TRL, Technology Readiness Level)와 비교했을 때 나타나는 문제점들이 있다.
&lt;ul&gt;
&lt;li&gt;&amp;lsquo;환멸&amp;rsquo;, &amp;lsquo;계몽&amp;rsquo;, &amp;lsquo;기대&amp;rsquo;와 같은 주관적인 용어로는 대상을 객관적으로 설명하거나 기술의 현재 위치를 제대로 나타낼 수 없다.&lt;/li&gt;
&lt;li&gt;용어가 잘못된 이해를 불러 오는 면이 있다. '사용자는 실망하기를 원치 않으므로 계몽 단계에는 해당 기술을 멀리 해야 하는 것인가?'&lt;/li&gt;
&lt;li&gt;다음 단계로 넘어 가기 위해 어떤 것이 충족되어야 하는지에 대한 자세한 설명이 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Devlog</category>
      <category>Gartner</category>
      <category>hype cycle</category>
      <category>가트너</category>
      <category>기술 준비 지수</category>
      <category>블록체인</category>
      <category>인공지능</category>
      <category>하이프 사이클</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/232</guid>
      <comments>https://geonlee.tistory.com/232#entry232comment</comments>
      <pubDate>Thu, 10 Dec 2020 21:30:52 +0900</pubDate>
    </item>
    <item>
      <title>package.json의 패키지들을 모두 버전 업 하고 싶다면</title>
      <link>https://geonlee.tistory.com/231</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;package.json의 패키지들을 모두 버전 업 하고 싶다면&lt;/h3&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;a href=&quot;https://classic.yarnpkg.com/en/docs/cli/upgrade-interactive/&quot;&gt;yarn upgrade-interactive&lt;/a&gt; 커맨드를 입력하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;yarn upgrade-interactive --latest&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthContent&quot; data-origin-width=&quot;2574&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duPEXI/btqPnazQtRY/hfGoKaoPXILMke5wBEyGp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duPEXI/btqPnazQtRY/hfGoKaoPXILMke5wBEyGp0/img.png&quot; data-alt=&quot;yarn upgrade-interactive&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duPEXI/btqPnazQtRY/hfGoKaoPXILMke5wBEyGp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduPEXI%2FbtqPnazQtRY%2FhfGoKaoPXILMke5wBEyGp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2574&quot; height=&quot;330&quot; data-origin-width=&quot;2574&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;yarn upgrade-interactive&lt;/figcaption&gt;
&lt;/figure&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;red : 이전 버전 호환이 안될 수도 있는 메이저 업데이트&lt;/li&gt;
&lt;li&gt;yello : 이전 버전 호환이 되는 마이너 업데이트&lt;/li&gt;
&lt;li&gt;green : 이전 버전 호환이 되는 버그 수정을 위한 패치 업데이트&lt;/li&gt;
&lt;/ul&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://classic.yarnpkg.com/en/docs/cli/upgrade-interactive/&quot;&gt;https://classic.yarnpkg.com/en/docs/cli/upgrade-interactive/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Web/JavaScript</category>
      <category>javascript</category>
      <category>npm</category>
      <category>package.json</category>
      <category>Yarn</category>
      <category>패키지</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/231</guid>
      <comments>https://geonlee.tistory.com/231#entry231comment</comments>
      <pubDate>Mon, 7 Dec 2020 16:30:04 +0900</pubDate>
    </item>
    <item>
      <title>HTML5 Canvas Tutorial : 화면에 튕기는 공 만들기</title>
      <link>https://geonlee.tistory.com/230</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&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/b6kgPm/btqPiaTNa5e/RkCdc8iOkfS4BWOW1K9sFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6kgPm/btqPiaTNa5e/RkCdc8iOkfS4BWOW1K9sFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6kgPm/btqPiaTNa5e/RkCdc8iOkfS4BWOW1K9sFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6kgPm%2FbtqPiaTNa5e%2FRkCdc8iOkfS4BWOW1K9sFk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;iframe src=&quot;https://www.youtube.com/embed/sLCiI6d5vTM&quot; width=&quot;560&quot; height=&quot;315&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;iframe src=&quot;https://codesandbox.io/embed/html5-canvas-tutorial-hwamyeone-twinggineun-gong-mandeulgi-krll3?autoresize=1&amp;fontsize=14&amp;hidenavigation=1&amp;theme=dark&quot;
     style=&quot;width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;&quot;
     title=&quot;HTML5 Canvas Tutorial : 화면에 튕기는 공 만들기&quot;
     allow=&quot;accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking&quot;
     sandbox=&quot;allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts&quot;
   &gt;&lt;/iframe&gt;</description>
      <category>Web/JavaScript</category>
      <category>canvas</category>
      <category>Creative Coding</category>
      <category>html5</category>
      <category>javascript</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/230</guid>
      <comments>https://geonlee.tistory.com/230#entry230comment</comments>
      <pubDate>Sun, 6 Dec 2020 17:27:50 +0900</pubDate>
    </item>
    <item>
      <title>React 개발이 이렇게 쉬웠나? (Feat. Next.js)</title>
      <link>https://geonlee.tistory.com/229</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 글은 제가 DEVIEW 2020의 발표 영상 &lt;a href=&quot;https://tv.naver.com/v/16970967&quot;&gt;DEVIEW 2020 - React 개발이 이렇게 쉬웠나? (Feat. Next.js)&lt;/a&gt;을 보면서 문서화 및 제 의견을 추가한 글입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;0.React 개발이 이렇게 쉬웠나? (Feat. Next.js)&lt;/h1&gt;
&lt;h1&gt;1. UI Library React / UI 라이브러리 리액트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트를 이용한 개발은 처음에는 모든 것을 해결해줄 만능 해결책을 가진 것 같이 느껴진다. 그러나 프로젝트의 규모가 커지고, 계속 사용할 수록 리액트를 이용한 개발의 어려운 점들이 등장한다. 1. UI Library React에서는 현재 리액트 개발이 어려운 이유와 이를 설명하기 위해 리액트와 리액트 개발에 대해 소개하려한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.1 커져가는 React 생태계&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;868&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ucq7E/btqO6hZRjeQ/elbRD6iJ94jj2Cf3SWusIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ucq7E/btqO6hZRjeQ/elbRD6iJ94jj2Cf3SWusIK/img.png&quot; data-alt=&quot;npm trends - react vs. vue vs. angular vs. @angular/core&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ucq7E/btqO6hZRjeQ/elbRD6iJ94jj2Cf3SWusIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fucq7E%2FbtqO6hZRjeQ%2FelbRD6iJ94jj2Cf3SWusIK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;868&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;npm trends - react vs. vue vs. angular vs. @angular/core&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 꽤나 과거부터 2020년 현재까지, 주로 비교가 되는 다른 기술 스택들인 &lt;b&gt;Vue.js&lt;/b&gt;, &lt;b&gt;Angular.js&lt;/b&gt;와 비교해서, 프론트엔드 개발을 위한 기술 스택으로 &lt;b&gt;압도적인 선두&lt;/b&gt;를 유지하고 있다. Vue.js의 스타 수가 더 많지만 이는 실제 사용보다 인기의 척도로 본다. 결국 중요한 건 얼마나 사용했는지 즉 &lt;b&gt;다운로드 수&lt;/b&gt;이기 때문에 이를 기준으로 판단하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1664&quot; data-origin-height=&quot;548&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EXm7q/btqO0gHFQ2S/CUmc1SJbwLtK31UwEjLan0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EXm7q/btqO0gHFQ2S/CUmc1SJbwLtK31UwEjLan0/img.png&quot; data-alt=&quot;The State of JavaScript 2019&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EXm7q/btqO0gHFQ2S/CUmc1SJbwLtK31UwEjLan0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEXm7q%2FbtqO0gHFQ2S%2FCUmc1SJbwLtK31UwEjLan0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1664&quot; data-origin-height=&quot;548&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;The State of JavaScript 2019&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 그 해에 개발자들의 자바스크립트에 대한 생각을 조사하여 보여주는 &lt;a href=&quot;https://2019.stateofjs.com/&quot;&gt;The State of JavaScript 2019&lt;/a&gt;에서는 무려 71.7%의 사용자가 &lt;b&gt;&quot;이전에도 사용해왔고, 앞으로도 사용할 것&quot;&lt;/b&gt;(I've USED it before, and WOULD use it again)이라는 응답을 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 언급한 동일 선상에서 비교되는 기술 스택인 다른 기술 스택들인 &lt;b&gt;Vue.js&lt;/b&gt;, &lt;b&gt;Angular.js&lt;/b&gt;를 표를 통해 비교해보면 비교해보면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 138px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Angular&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;React&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;Vue&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 25px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;UI/DOM 조작&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 25px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;상태 관리&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 25px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;라우팅&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;❎&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 25px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;양식 검증 &amp;amp; 핸들링&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;❎&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 25px; text-align: center;&quot;&gt;❎&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 18px; text-align: center;&quot;&gt;Http 클라이언트&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 18px; text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 18px; text-align: center;&quot;&gt;❎&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 18px; text-align: center;&quot;&gt;❎&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;View 레이어 초점을 맞춰 개발된다는 점이 리액트의 장점이자 단점이다.&lt;/li&gt;
&lt;li&gt;리액트는 UI라는 작은 영역만 담당하여 빠른 릴리즈가 가능하지만, Angular나 Vue 같은 Framework과 달리 전역에서의 상태관리나 클라이어트 사이드 라우팅(CSR, Client Side Routing)을 하려면 &lt;a href=&quot;https://github.com/reduxjs/redux&quot;&gt;redux&lt;/a&gt;나 &lt;a href=&quot;https://github.com/remix-run/react-router&quot;&gt;react-router-dom&lt;/a&gt;같은 추가 라이브러리 설치가 필수적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.2 React의 한계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 언급한 것처럼 리액트는 UI라는 작은 영역만 담당하기 때문에 프로젝트에 필요한 다양한 기능들을 사용하기에는 리액트가 가진 한계가 명확하다. 아래는 리액트가 가진 한계에 대한 내용들이다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSR(Server Side Rendering) / SSG(Static Site Generation)&lt;/li&gt;
&lt;li&gt;Type Checker&lt;/li&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;State management&lt;/li&gt;
&lt;li&gt;Styling&lt;/li&gt;
&lt;li&gt;Bundling&lt;/li&gt;
&lt;li&gt;Lazy loading&lt;/li&gt;
&lt;li&gt;Code splitting&lt;/li&gt;
&lt;li&gt;Static file serve&lt;/li&gt;
&lt;li&gt;Scroll restoration&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 React Developer Roadmap 그림에서도 볼 수 있듯이 리액트 프로젝트를 제대로 공부하기 위해서는 다양한 분야를 알아야하고, 한 기능을 담당하는 라이브러리만해도 여러가지이기 때문에 이들의 장단점을 비교 분석해보기에는 시간이 너무 부족하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;5276&quot; data-origin-height=&quot;6496&quot; data-filename=&quot;2020-12-02-00-19-29.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cB49Ub/btqO12oAcPT/QZnsSkvs5SF8hUaK0OWZhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cB49Ub/btqO12oAcPT/QZnsSkvs5SF8hUaK0OWZhK/img.png&quot; data-alt=&quot;React Developer Roadmap: Learn to become a react developer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cB49Ub/btqO12oAcPT/QZnsSkvs5SF8hUaK0OWZhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcB49Ub%2FbtqO12oAcPT%2FQZnsSkvs5SF8hUaK0OWZhK%2Fimg.png&quot; data-origin-width=&quot;5276&quot; data-origin-height=&quot;6496&quot; data-filename=&quot;2020-12-02-00-19-29.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;React Developer Roadmap: Learn to become a react developer&lt;/figcaption&gt;
&lt;/figure&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;현재 속도 개선을 위한 기능들인 Bundling, Lazy Loading, Code Splitting 과 같은 기능을 지원하기 위한 Webpack과 Babel의 plugin, loader는 계속해서 늘면서 현재는 모두 약 5,000개로 이는 모두를 검증하여 사용할 대상을 결정할 수 있는 수준을 넘어선 정도까지 와버렸다. 이러한 &lt;b&gt;지속적 복잡도 증가&lt;/b&gt;는 파편화된 생태계에서 프로젝트에 알맞는 라이브러리를 고르고, 이를 설정하고 유지보수하는 것 모두 일정이 촉박한 서비스 개발자에게는 매우 어려운 문제로 자리잡고 있다. 이러한 상황에서 서비스 개발자들은 &lt;b&gt;리액트 빌드 툴체인 마스터가&lt;/b&gt; 되기보다는 &lt;b&gt;규격화되어있는 프레임워크&lt;/b&gt;를 쓰는 방향으로 가고있다.&lt;/p&gt;
&lt;h1&gt;2. React Framework Next.js&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.1 Web Framework가 필요한 이유&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;250&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rVpxP/btqOVKo0JdP/CIk25cnJynSj4eczXHZmhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rVpxP/btqOVKo0JdP/CIk25cnJynSj4eczXHZmhK/img.png&quot; data-alt=&quot;UX(User Experience) vs. DX(Developer Experience)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rVpxP/btqOVKo0JdP/CIk25cnJynSj4eczXHZmhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrVpxP%2FbtqOVKo0JdP%2FCIk25cnJynSj4eczXHZmhK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;250&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UX(User Experience) vs. DX(Developer Experience)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;사용자 경험(UX) &amp;gt; 개발자 경험(DX) &amp;gt; 구현 용이성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 경험, 개발자 경험, 구현 용의성 중에서 결국 서비스를 사용하는 것은 사용자이기 때문에 사용자 경험이 가장 중요한 것으로 여겨지곤한다. 그러나 다른 한편으로는 &lt;b&gt;개발자 경험&lt;/b&gt; 개선은 &lt;b&gt;사용자 경험&lt;/b&gt;을 개선하는 가장 좋은 방법이라는 이야기도 될 수 있다. 예를 들어 빌드 시간 개선, HMR 제공등을 통해 DX가 개선된다면 UX의 영역에 속하는 버그 수정과 기능 개발에 걸리는 시간을 개선할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 반대로 개발자 경험은 낮아지지만, 사용자 경험이 높아지는 경우들도 존재하는데, SSR, Lazy Loading, Code Splitting등은 구현이 복잡하기 때문에 개발자 경험은 낮아지지만, UX인 성능 향상을 기대할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Web Framework의 구성 요소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 프레임워크의 구성 요소들은 모두 UX를 개선하거나, DX를 개선해서 UX를 개선하는 요소들로 구성되었다. 해당 요소들은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UI Component Model / UI 컴포넌트 모델링&lt;/li&gt;
&lt;li&gt;Pre-rendering HTML / 프리렌더링 HTML&lt;/li&gt;
&lt;li&gt;Routing / 라우팅&lt;/li&gt;
&lt;li&gt;CSS Encapsulation / CSS 캡슐화&lt;/li&gt;
&lt;li&gt;Data Flow / 데이터 플로우&lt;/li&gt;
&lt;li&gt;Deployment/Releases / 배포/릴리즈&lt;/li&gt;
&lt;li&gt;Compilation / 컴파일&lt;/li&gt;
&lt;li&gt;Security / 보안&lt;/li&gt;
&lt;li&gt;Bundling / 번들링&lt;/li&gt;
&lt;li&gt;Backend/Server logic / 백엔드/서버 로직&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.2 Next.js vs. Gatsby vs. Create React App&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 가장 대표적으로 사용되는 3가지 리액트 프레임워크는 다음과 같다. 이들의 장단점을 표로서 정리해보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1709&quot; data-origin-height=&quot;719&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RukRH/btqO0eXruW5/18KsSXMg1NNKfyX8WRdbp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RukRH/btqO0eXruW5/18KsSXMg1NNKfyX8WRdbp1/img.png&quot; data-alt=&quot;Next.js vs. Gatsby vs. Create React App&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RukRH/btqO0eXruW5/18KsSXMg1NNKfyX8WRdbp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRukRH%2FbtqO0eXruW5%2F18KsSXMg1NNKfyX8WRdbp1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1709&quot; data-origin-height=&quot;719&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Next.js vs. Gatsby vs. Create React App&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;Next.js&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;Gatsby&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;Create React App&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;장점&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;- 페이지마다 data-fetch, pre-rendering 전략을 다르게 가능&lt;br /&gt;- 증분 정적 페이지 생성&lt;br /&gt;- 다양한 상태관리, 스타일링 라이브러리 지원&lt;br /&gt;- 파일 시스템 라우팅&lt;br /&gt;- API route&lt;br /&gt;- Webpack 5 지원&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;- GraphQL이 기본&lt;br /&gt;- 플러그인 생태계&lt;br /&gt;- 이미지 최적화&lt;br /&gt;- 정적 페이지 성능이 next.js 보다 약간 좋음&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;- &amp;nbsp;가장 배우기 쉬움(추가 프레임 워크 없음)&lt;br /&gt;- React 팀에서 생성 및 유지 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;단점&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;- 프레임워크 지식 필요(_app.js, _document.js, getServerSideProps)&lt;br /&gt;- 플러그인 시스템이 없음&lt;br /&gt;- 이미지 최적화&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;- GraphQL이 기본&lt;br /&gt;- 이미지 최적화와 정적 페이지 생성으로 빌드시간이 오래걸림(3000페이지 기준 30분)&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;- 서버 측 렌더링 없음(react-router 같은 서드파티 라우팅 라이브러리 필요)&lt;br /&gt;- Code Splitting과 같은 기능을 직접 구현해야함&lt;br /&gt;- eject시 모든 종속성을 관리해야해서 복잡성이 매우 증가함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;554&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mWPk9/btqO73mvLMq/EeQbkIBo7C30I0ntZEEc80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mWPk9/btqO73mvLMq/EeQbkIBo7C30I0ntZEEc80/img.png&quot; data-alt=&quot;Next.js vs. Gatsby vs. Create React App&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mWPk9/btqO73mvLMq/EeQbkIBo7C30I0ntZEEc80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmWPk9%2FbtqO73mvLMq%2FEeQbkIBo7C30I0ntZEEc80%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;554&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Next.js vs. Gatsby vs. Create React App&lt;/figcaption&gt;
&lt;/figure&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;b&gt;Create React App&lt;/b&gt; - CRA는 기본적으로 싱글 페이지 어플리케이션으로 CSR 방식으로 렌더링을 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Gatsby&lt;/b&gt; - Gatsby는 Site Generator (정적 페이지)를 생성하여 SSG 방식으로 렌더링을 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Next.js&lt;/b&gt; - Next.js는 SSG와 SSR 둘 다 사용 가능한 하이브리드 형태로 렌더링을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 팀에서는 공식문서를 통해 각각에 목적에 따른 리액트 프레임워크를 제공하는 데 그 내용은 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;293&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/70yDN/btqO13nupCU/sQLN72VU7Yvimd9GVdq21K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/70yDN/btqO13nupCU/sQLN72VU7Yvimd9GVdq21K/img.png&quot; data-alt=&quot;React 팀 추천 툴체인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/70yDN/btqO13nupCU/sQLN72VU7Yvimd9GVdq21K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F70yDN%2FbtqO13nupCU%2FsQLN72VU7Yvimd9GVdq21K%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;293&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;React 팀 추천 툴체인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/create-a-new-react-app.html#recommended-toolchains&quot;&gt;https://ko.reactjs.org/docs/create-a-new-react-app.html#recommended-toolchains&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1633254685972&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;새로운 React 앱 만들기 &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/create-a-new-react-app.html#recommended-toolchains&quot; data-og-url=&quot;https://ko.reactjs.org/docs/create-a-new-react-app.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bZpCxP/hyLPlCOhMJ/jUHKa25Va2aaBqMKK810x0/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/create-a-new-react-app.html#recommended-toolchains&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.reactjs.org/docs/create-a-new-react-app.html#recommended-toolchains&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bZpCxP/hyLPlCOhMJ/jUHKa25Va2aaBqMKK810x0/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;새로운 React 앱 만들기 &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A JavaScript library for building user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.reactjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Dan Abramov도 &quot;&lt;b&gt;static(SSG)&lt;/b&gt;&quot;, &quot;&lt;b&gt;server rendered(SSR)&lt;/b&gt;&quot;, &quot;&lt;b&gt;client rendered(CSR)&lt;/b&gt;&quot; 셋을 혼합하는 현재 Next.js의 방식이 리액트 프레임워크가 나아가야 할 방향이라고 언급하기도 하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;445&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qz0rX/btqO3z7zKJl/08guKd4mbnkZoFLEVNSKlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qz0rX/btqO3z7zKJl/08guKd4mbnkZoFLEVNSKlk/img.png&quot; data-alt=&quot;Dan Abramov 트위터&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qz0rX/btqO3z7zKJl/08guKd4mbnkZoFLEVNSKlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqz0rX%2FbtqO3z7zKJl%2F08guKd4mbnkZoFLEVNSKlk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;445&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Dan Abramov 트위터&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이버의 쇼핑 검색 서비스의 경우 처음에는 &lt;b&gt;CRA(Create React App)&lt;/b&gt;을 &lt;b&gt;eject&lt;/b&gt;하여 라이브러리를 적용하여 &lt;b&gt;SSR&lt;/b&gt;이나 &lt;b&gt;Code Splitting&lt;/b&gt; 같은 기능을 직접 구축하여 사용하다가 유지보수 측면과 여러가지 이슈로 인해 기존 프로젝트를 &lt;b&gt;Next.js&lt;/b&gt;로 마이그레이션 작업을 진행하였다고 한다. 네이버 쇼핑 검색 서비스에서 리액트 프레임워크로 Next.js를 선택한 이유는 다음과 같다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.3 Next.js를 선택한 이유&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Pre-rendering HTML&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 Pre-rendering HTML이라는 이름으로, &lt;b&gt;SSR with (Re)hydration&lt;/b&gt; 과 &lt;b&gt;CSR with Prerendering&lt;/b&gt; 방식을 혼합해서 사용하고 있다. 이러한 특성이 쇼핑 검색 서비스에 적합했던 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터넷 쇼핑이라는 전자 상거래의 특성상 자주 컨텐츠가 바뀐다.&lt;/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;각 영역별로 SSR이 사용된 부분과 SSG가 사용된 부분은 다음과 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSR&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR이 사용된 페이지는 쇼핑 검색 페이지, 쇼핑 카탈로그 페이지로 컨텐츠가 자주 바뀌는 대부분의 페이지에 사용된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;453&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wzvs6/btqOYuMY7g6/rdbvjrefFpUTsHxkBOBk4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wzvs6/btqOYuMY7g6/rdbvjrefFpUTsHxkBOBk4K/img.png&quot; data-alt=&quot;쇼핑 검색 페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wzvs6/btqOYuMY7g6/rdbvjrefFpUTsHxkBOBk4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWzvs6%2FbtqOYuMY7g6%2FrdbvjrefFpUTsHxkBOBk4K%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;453&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;쇼핑 검색 페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSG&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG가 사용되는 페이지는 쇼핑 카테고리 페이지, unsupport 페이지, 에러 페이지로 페이지 콘텐츠의 변화가 거의 없는 페이지에 사용된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;389&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Oih15/btqO0foqtgt/KTLXy9ihkkIRo9Rc83sSA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Oih15/btqO0foqtgt/KTLXy9ihkkIRo9Rc83sSA1/img.png&quot; data-alt=&quot;쇼핑 카테고리 페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Oih15/btqO0foqtgt/KTLXy9ihkkIRo9Rc83sSA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOih15%2FbtqO0foqtgt%2FKTLXy9ihkkIRo9Rc83sSA1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;389&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;쇼핑 카테고리 페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js의 Pre-rendering 기능은 정적 페이지 자동화, 정적 페이지 생성, 증분 정적 재생성까지 가능한 현태이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1149&quot; data-origin-height=&quot;638&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qKooa/btqO3x20gBq/SRG6myklFuk1CYrqSGdkO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qKooa/btqO3x20gBq/SRG6myklFuk1CYrqSGdkO1/img.png&quot; data-alt=&quot;Next.js의 사전 렌더링 히스토리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qKooa/btqO3x20gBq/SRG6myklFuk1CYrqSGdkO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqKooa%2FbtqO3x20gBq%2FSRG6myklFuk1CYrqSGdkO1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1149&quot; data-origin-height=&quot;638&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Next.js의 사전 렌더링 히스토리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 사전 렌더링은 초기에 로딩 화면 없이 페이지를 보여줌으로서 초기 로딩 성능과 SEO의 향상을 가져다주게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js를 통해 구현할 수 있는 사전 렌더링의 두가지 형태인 SSR과 SSG를 표로 비교하면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;SSR(Server Side Rendering)&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;SSG(Static Site Generation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;- 페이지가 요청되었을 때 렌더링된다.&lt;br /&gt;- 런타임 실행이 필요하다.&lt;br /&gt;- 자바스크립트와 CSS 파일를 미리 가져오는 형태(Prefetch)&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;- 빌드타임에 렌더링된다.&lt;br /&gt;- HTML이 CDN을 통해 제공된다.&lt;br /&gt;- 자바스크립트와 CSS 파일 뿐만 아니라 데이터까지 미리 가져오는 형태(Prefetch)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-size: 1.44em; letter-spacing: -1px; font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Prefetch&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prefetch는 뷰포트에 나타나는 링크만 Prefetch(Intersection Observer API)하는 형태로 제공되며, 데이터를 동적으로 주입하게 된다. 네트워크 속도가 느리거나 Save-data가 on일 경우 disable되는 특성을 가지고 있다. Next.js는 Link 컴포넌트나 router의 prefetch 메소드를 통해 Prefetch 기능을 제공한고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1633254918874&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Link prefetch
&amp;lt;Link href=&quot;/search/category?catId=50000167&quot; prefetch&amp;gt;
	&amp;lt;a&amp;gt;Blog Post&amp;lt;/a&amp;gt;
&amp;lt;/Link&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1633254932657&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// router method
const router = useRouter( )
router.prefetch(`/catalog/[nvMid]`, `/catalog/${modelProduct.adId || modelProduct.id}`)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;580&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/veb3F/btqO6gs9a0r/T0mtA23mSIdKv0tC9cvR0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/veb3F/btqO6gs9a0r/T0mtA23mSIdKv0tC9cvR0K/img.png&quot; data-alt=&quot;Prefetch의 예시 - 자동완성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/veb3F/btqO6gs9a0r/T0mtA23mSIdKv0tC9cvR0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fveb3F%2FbtqO6gs9a0r%2FT0mtA23mSIdKv0tC9cvR0K%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;580&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Prefetch의 예시 - 자동완성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;getStaticProps, getStaticPaths (SSG)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getStaticProps와 getStaticPaths는 Next.js에서 SSG를 구하기 위한 메소드들이다. 해당 메소드들은 CDN에 캐싱할 수 있는 HTML과 JSON을 생성하게된다. 혹은 해당 프로젝트의 파일 시스템에서 직접 데이터를 읽어서 가져올 수도 있는데 이 경우에는 process.cwd()를 이용해서 데이터를 가져오게 된다. 또한, 위에서 언급했듯 렌더링에 필요한 해당 메소드들이 빌드 타임에 실행되며, 서버 측으로 번들린된다. 또한 최종적으로 결론적으로, Viewport 내에 링크가 있을 경우 링크에 해당하는 CSS, 자바스크립트, json 데이터를 미리 가져오게된다.&lt;/p&gt;
&lt;pre id=&quot;code_1633257366685&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export async function getStaticProps() {
	const res = await fetch('http://.../posts')
    const posts = await res.json()
    return {
    	props: {
        	posts
        }, revalidate: 1 // In seconds 
    }
}

export default Blog&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://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation&quot;&gt;https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation&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;1072&quot; data-origin-height=&quot;230&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhTM4r/btqO71hXAk5/t4dhc812ARRUGeNnPfwgMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhTM4r/btqO71hXAk5/t4dhc812ARRUGeNnPfwgMK/img.png&quot; data-alt=&quot;Viewport내에 링크가 있는 경우 CSS, JS, JSON을 prefetch&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhTM4r/btqO71hXAk5/t4dhc812ARRUGeNnPfwgMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhTM4r%2FbtqO71hXAk5%2Ft4dhc812ARRUGeNnPfwgMK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;230&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Viewport내에 링크가 있는 경우 CSS, JS, JSON을 prefetch&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Incremental Static Regeneration&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점진적 정적 재생성 기능은 Next.js에 20년 07년 도입되었으며, SSG의 단점인 초기 빌드 시간과 SSR의 단점인 초기 로딩 속도를 해결하기 위해 도입되었다. 트래픽이 들어올 때 백그라운드에서 다시 렌더링하여 기존의 페이지 정보를 업데이트하는 방식으로 요청 바로 이전에 데이터를 렌더링하는 특성을 가지고 있다. 만약 fallback: true를 주게 되면 로딩 후 실시간 렌더링하게 된다. 점진적 정적 재생성의 장점은 &lt;b&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;h3 data-ke-size=&quot;size23&quot;&gt;getServerSideProps(SSR)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getServerSideProps는 런타임에 실행되는 Next.js의 SSR 메소드이다. 서버 측에 번들링되며, Viewport 내에 getServerSideProps를 가진 페이지 컴포넌트에 대한 Link가 있을 경우, CSS와 JS를 prefetch 하고, 링크 클릭 시 getServerSideProps를 실행하여 json 데이터를 받아온다. 네이버 쇼핑 검새은 쇼핑 카테고리 영역에 해당 메소드를 사용하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;506&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEvxT5/btqO12hT4lf/nQHeafnS9iPa8oHiRsD461/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEvxT5/btqO12hT4lf/nQHeafnS9iPa8oHiRsD461/img.png&quot; data-alt=&quot;쇼핑 카테고리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEvxT5/btqO12hT4lf/nQHeafnS9iPa8oHiRsD461/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEvxT5%2FbtqO12hT4lf%2FnQHeafnS9iPa8oHiRsD461%2Fimg.png&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;506&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;쇼핑 카테고리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;142&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ct0Bv3/btqO0SGxLKD/VkvHkYgqLVV7WlFNQds6Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ct0Bv3/btqO0SGxLKD/VkvHkYgqLVV7WlFNQds6Uk/img.png&quot; data-alt=&quot;CSS와 JS를 prefetch&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ct0Bv3/btqO0SGxLKD/VkvHkYgqLVV7WlFNQds6Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fct0Bv3%2FbtqO0SGxLKD%2FVkvHkYgqLVV7WlFNQds6Uk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;142&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CSS와 JS를 prefetch&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cumkh/btqO7h6rmFP/jKOBhV5I1aeiDFCwVBFAL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cumkh/btqO7h6rmFP/jKOBhV5I1aeiDFCwVBFAL0/img.png&quot; style=&quot;width: 49.9593%; margin-right: 10px;&quot; width=&quot;100%&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;931&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cumkh/btqO7h6rmFP/jKOBhV5I1aeiDFCwVBFAL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCumkh%2FbtqO7h6rmFP%2FjKOBhV5I1aeiDFCwVBFAL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;931&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dT1vde/btqO6hyNrpV/dkKquf9pJtZVMDm7FU0Mw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dT1vde/btqO6hyNrpV/dkKquf9pJtZVMDm7FU0Mw0/img.png&quot; style=&quot;width: 48.87791222130634%;&quot; width=&quot;100%&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;946&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dT1vde/btqO6hyNrpV/dkKquf9pJtZVMDm7FU0Mw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdT1vde%2FbtqO6hyNrpV%2FdkKquf9pJtZVMDm7FU0Mw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1352&quot; height=&quot;946&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쇼핑 검색의 경우 과거 CRA를 사용했을 때도 SSR을 구현했는데, 현재 Next.js를 이용한 구현과 비교하면 더욱 추상화 되고 간단해진 것을 확인할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Routing&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 파일 시스템 기반의 라우팅을 제공한다. 이러한 특징을 통해 라우팅 구축을 위해 react-router-dom같은 추가 라이브러리를 설치하거나 코드 작업을 할 필요가 없어지고, 정적, 동적, 중첩 라우팅 같은 복잡한 형태의 라우팅도 구현할 수 있도록 지원하고 있다. 또한, Shallow Routing을 이용하면 data-fetching 없이도 url을 변경할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dynamic import&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 Chrome Webpack 코어팀과 협업하여 복잡한 구성없이 Dynamic Loading과 라우트 기반의 Code Splitting을 제공한다. 이를 통해 ES2020에 추가된 기능 중 하나인 &lt;a href=&quot;https://github.com/tc39/proposal-dynamic-import&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Dynamic Import&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;&lt;a href=&quot;https://nextjs.org/docs/advanced-features/dynamic-import&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/docs/advanced-features/dynamic-import&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1633255162513&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;Advanced Features: Dynamic Import | Next.js&quot; data-og-description=&quot;Dynamically import JavaScript modules and React Components and split your code into manageable chunks.&quot; data-og-host=&quot;nextjs.org&quot; data-og-source-url=&quot;https://nextjs.org/docs/advanced-features/dynamic-import&quot; data-og-url=&quot;https://nextjs.org/docs/advanced-features/dynamic-import&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/N7mZg/hyLPjd0wyt/rY1WMUXH7gAz0imrZbFY41/img.png?width=2048&amp;amp;height=1170&amp;amp;face=0_0_2048_1170&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/advanced-features/dynamic-import&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nextjs.org/docs/advanced-features/dynamic-import&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/N7mZg/hyLPjd0wyt/rY1WMUXH7gAz0imrZbFY41/img.png?width=2048&amp;amp;height=1170&amp;amp;face=0_0_2048_1170');&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;Advanced Features: Dynamic Import | Next.js&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Dynamically import JavaScript modules and React Components and split your code into manageable chunks.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nextjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Code Splitting&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 페이지별 청크 분리를 통해 Code Splitting을 구현하고 있다. 개발자가 필요한만큼 common 청크를 생성(최대 25개)할 수 있으며, 자주 쓰이는 프레임워크(react, react-dom)의 청크를 둠으로써 캐시가 힛되는 비율을 높인다. Next.js의 Code Splitting 관련 내부 코드는 다음과 같은데 이렇게 복잡한 부분을 서비스 개발자가 일일히 다 찾아서 최적화하는 작업은 쉽지 않을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;851&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CFRo8/btqO7hZBWbf/KX30M1u36r6djkZ3Z0k3KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CFRo8/btqO7hZBWbf/KX30M1u36r6djkZ3Z0k3KK/img.png&quot; data-alt=&quot;Next.js Code Splitting 내부 구현 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CFRo8/btqO7hZBWbf/KX30M1u36r6djkZ3Z0k3KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCFRo8%2FbtqO7hZBWbf%2FKX30M1u36r6djkZ3Z0k3KK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;851&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Next.js Code Splitting 내부 구현 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Differential Loading&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;579&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BlUv5/btqO7ixr57E/dv8k1CzZVtDu6NBf2UxlgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BlUv5/btqO7ixr57E/dv8k1CzZVtDu6NBf2UxlgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BlUv5/btqO7ixr57E/dv8k1CzZVtDu6NBf2UxlgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBlUv5%2FbtqO7ixr57E%2Fdv8k1CzZVtDu6NBf2UxlgK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;579&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 data-ke-size=&quot;size16&quot;&gt;DifferentialLoading은 IE와 같은 레거시 브라우저와 Chrome 같은 현대 브라우저에 따라 번들링을 다르게 하는 방법이다. 해당 기능을 통해 브라우저에 맞는 번들을 보냄으로써 번들 사이즈와 불필요한 네트워크 비용을 줄여준다. 레거시 브라우저를 위한 폴리필도 설치 없이 기본적으로 제공되며 트랜스파일의 결과물도 분기처리를 통해 보여지게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개발 서버 최적화&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;429&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NauPp/btqO13ODTYX/QMTOUZYcUkjqddffULeEg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NauPp/btqO13ODTYX/QMTOUZYcUkjqddffULeEg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NauPp/btqO13ODTYX/QMTOUZYcUkjqddffULeEg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNauPp%2FbtqO13ODTYX%2FQMTOUZYcUkjqddffULeEg0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;429&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 data-ke-size=&quot;size16&quot;&gt;DX를 높이는 기능중의 하나로, 개발자가 개발 서버를 띄울 때, 모든 페이지를 컴파일하는 것이 아닌 페이지에 액세스할 때 컴파일하는 기능이다. 웹소켓을 기반으로 5초마다 체크를 하며, 25초 동안 접근하지 않게 되면 캐시를 제거한다. 이를 통해 개발 서버를 띄우는 시간이 줄어들면서 개발자 경험이 좋아지게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Fast Refresh&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fast Refresh는 next.js, facebook 내부 프로젝트로 리액트 컴포넌트에 대한 변화로 인한 즉각적인 피드백을 준다. Next.js에서 기본적으로 사용 가능하며, Fast Refresh를 사용하면, 컴포넌트의 상태를 잃지 않고도 코드에서의 변화가 즉각적으로 보여질 것이다. 그러나 현재 클래스 컴포넌트나 HOC에서는 상태유지가 되지 않는다. 또한, 소스맵 자동 적용으로 IDE가 연동되어서 브라우저에서 에러를 클릭시 자동으로 IDE내의 에러 발생 부분으로 이동하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Webpack 5 지원&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Webpack5는 이전 버전 호환성을 지니며 Webpack 4와 아무 비용없이 마이그레이션이 가능하다. 뿐만 아니라 향상된 tree-shaking과 지속 캐싱, Deterministic chunk and module ids을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 nextjs.org와 vercel.com에서 사용중이다. CRA에서는 eject후 Webpack 3에서 Webpack 4 마이그레이션했을 때, webpack plugin/loader 관리, babel polyfill/plugin 관리, ts loader, style loader 모두를 웹팩에서 설정해야하는 등 리소스 낭비가 심했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ETC&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내장된 Styling(css module, css in js, sass, less, stylus) 지원&lt;/li&gt;
&lt;li&gt;Data flow(redux, mobx, swr, apollo, recoil) 연동 등 210여개의 예제&lt;/li&gt;
&lt;li&gt;Preview, Production, Serverless 배포 플랫폼&lt;/li&gt;
&lt;li&gt;내장된 TypeScript 지원&lt;/li&gt;
&lt;li&gt;정적 파일 서빙&lt;/li&gt;
&lt;li&gt;API Routes&lt;/li&gt;
&lt;li&gt;절대 경로와 alias&lt;/li&gt;
&lt;li&gt;Persistent Caching for Page Bundles&lt;/li&gt;
&lt;li&gt;Rewrite, Redirect, Header&lt;/li&gt;
&lt;li&gt;AMP 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Security &amp;amp; community&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TypeScript 코드베이스&lt;/li&gt;
&lt;li&gt;2606 개의 통합 테스트, 191 개의 테스트 스위트&lt;/li&gt;
&lt;li&gt;Google Chrome, React, Webpack 팀과 협업&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;399&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnYc2S/btqO0SGxP68/NsZ2icXlBCoXbLsOzuEvmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnYc2S/btqO0SGxP68/NsZ2icXlBCoXbLsOzuEvmk/img.png&quot; data-alt=&quot;Next.js in production&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnYc2S/btqO0SGxP68/NsZ2icXlBCoXbLsOzuEvmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnYc2S%2FbtqO0SGxP68%2FNsZ2icXlBCoXbLsOzuEvmk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;399&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Next.js in production&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RFC&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 최적화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재는 외부 플러그인을 통해서 가능&lt;/li&gt;
&lt;li&gt;WebP, AVIF 같은 최신 형식으로 자동 변환&lt;/li&gt;
&lt;li&gt;이미지 사이즈 자동 조절&lt;/li&gt;
&lt;li&gt;로컬호스팅, CDN 이미지 경로 지원&lt;/li&gt;
&lt;li&gt;preconnect, preload&lt;/li&gt;
&lt;li&gt;적은 시간 설치 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;3. Troubleshooting &amp;amp; Tips&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.1 Troubleshooting&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;959&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/olmyE/btqO71hXEHQ/PyJIodicktGO5USKaqxsuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/olmyE/btqO71hXEHQ/PyJIodicktGO5USKaqxsuK/img.png&quot; data-alt=&quot;Mobile/PC Lighthouse 비교&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/olmyE/btqO71hXEHQ/PyJIodicktGO5USKaqxsuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FolmyE%2FbtqO71hXEHQ%2FPyJIodicktGO5USKaqxsuK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;959&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Mobile/PC Lighthouse 비교&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lighthouse를 사용해서 점수를 측정했을 때, SSG 시에는 문제가 없지만, SSR 시 모바일에서 점수가 낮은 경향을 보인다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Query 기반 라우팅 불가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query에 기반한 라우팅이 불가한 문제가 있었는데, 커스텀 서버를 사용하거나 9.5 버전에 추가된 redirect, rewrite을 사용해서 해결할 수 있었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Custom app에서 getInitialProps만 사용가능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;automatic static optimization X&lt;/li&gt;
&lt;li&gt;Redux 사용시 모든 페이지 reducer가 app.js에 번들됨 =&amp;gt; code splitting 필요 혹은 다른 상태 관리 라이브러리를 사용해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전역 css import 가 _app.js에서만 가능함&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;npm에 올려 사용하거나&lt;/li&gt;
&lt;li&gt;styledjsx를 사용하면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TypeScript barrel files에서 tree shaking 안 됨&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sideEffects: false로 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CSS module dynamic import&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;v9.5.2 에서 CSS 모듈을 이용한 component dynamic import 에 FOUC 이슈&lt;/li&gt;
&lt;li&gt;v9.5.3 으로 업그레이드 후에는 CSS preload CORS 이슈로 수정중&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Shallow: true 옵션으로 페이지 이동 후 뒤로가기 시에 다시 데이터를 패칭하지 않음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this.props.router.beforePopState(({as, options}) =&amp;gt; { if(options.shallow){ this.props.router.replace(as) return false } return true })&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;node_modules, monorepo의 local packages경우 transpile하지 않음&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;next-transpile-modules를 사용해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IE11에서 개발서버가 실행이 안 됨(production에서는 가능)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ShadowDOM polyfill 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;react-router와 다르게 history pushState기능이 없음&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;query를 사용하거나 브라우저의 Storage를 사용함&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.2 Custom Server&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 서버를 사용하는 건 비추천한다. HMR을 .next를 제외하고 새로 구성해야 해야하는 문제가 있고, 서버에 맞는 tsconfig.json을 따로 설정해야했다. 또한, vercel로 배포를 원하는 경우에도, 커스텀 서버를 사용하는 경우 vercel을 통한 배포가 불가능한 문제가 있었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.3 next.config.js&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next.config.js는 Next.js에서 프로젝트 설정상 추가 구성이 필요할 경우 사용되는 설정 파일이다. 해당 파일은 서버에서만 실행되어 트랜스파일되지 않으며, &lt;b&gt;phase-export&lt;/b&gt;, &lt;b&gt;phase-production-build&lt;/b&gt;, &lt;b&gt;phase-production-server&lt;/b&gt;, &lt;b&gt;phase-development-server&lt;/b&gt;라는 총 4단계의 Phase로 구분된다. Webpack은 클라이언트와 서버에서 두 번 실행된다.&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;Modern: 트랜스파일 차등 로딩&lt;/li&gt;
&lt;li&gt;Profiling: 프로덕션에서 react profiling&lt;/li&gt;
&lt;li&gt;productionBrowserSourceMaps: 소스맵 생성&lt;/li&gt;
&lt;li&gt;scrollRestoration: 내부 링크에서만 가능&lt;/li&gt;
&lt;li&gt;optimizeImages, Fonts: preload, preconnect&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.4 plugins&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;311&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blD4Er/btqO7iK0B7B/NarR8c3U0MnkWrAvTJ7a80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blD4Er/btqO7iK0B7B/NarR8c3U0MnkWrAvTJ7a80/img.png&quot; data-alt=&quot;next-compose-plugins&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blD4Er/btqO7iK0B7B/NarR8c3U0MnkWrAvTJ7a80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblD4Er%2FbtqO7iK0B7B%2FNarR8c3U0MnkWrAvTJ7a80%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;311&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;next-compose-plugins&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인을 사용할 수 있지만, 플러그인을 추가할 수록 깊이가 깊어져 코드를 제대로 읽기 힘들어지게 된다. 이 경우 next-compose-plugins라는 라이브러리를 사용하면 배열 형태로 plugin 을 추가할 수 있게 된다. 또한 optional로도 plugin을 주입할 수도 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;next-optimized-images&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gatsby.js와 달리 Next.js에 내장된 이미지 최적화 기능이 존재하지 않는다. 따라서 이를 위한 라이브러리가 필요한데 이 때 사용하는 것이 next-optimized-images이다. gatsby-image의 Next.js 버전으로 이미지 url에 쿼리를 추가하여 사용한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;478&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1r954/btqO138V6cA/WOzzAFGXr4U1PSklSXlJ2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1r954/btqO138V6cA/WOzzAFGXr4U1PSklSXlJ2K/img.png&quot; data-alt=&quot;next-optimized-images&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1r954/btqO138V6cA/WOzzAFGXr4U1PSklSXlJ2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1r954%2FbtqO138V6cA%2FWOzzAFGXr4U1PSklSXlJ2K%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;478&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;next-optimized-images&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@next/bundle-analyzer&lt;/h3&gt;
&lt;pre id=&quot;code_1633257387021&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const withBundleAnalyzer = require('@next/bundle-analyzer')({
	enable: process.env.ANALYZE === 'true', 
})
module.exports = withBundleAnalyzer({})​&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 라이브러리를 이용하여 번들을 분석한 이후 .next/analyze에서 분석한 html 파일을 확인 가능하다.&lt;/p&gt;
&lt;h1&gt;Review / 세 줄 요약&lt;/h1&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;리액트 프레임워크인 Next.js가 현재 선택할 수 있는 가장 최선의 선택&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Web/Next.js</category>
      <category>DEVIEW</category>
      <category>DEVIEW 2020</category>
      <category>Next.js</category>
      <category>react</category>
      <category>SSG</category>
      <category>SSR</category>
      <category>네이버쇼핑</category>
      <category>리액트</category>
      <category>프레임워크</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/229</guid>
      <comments>https://geonlee.tistory.com/229#entry229comment</comments>
      <pubDate>Thu, 3 Dec 2020 20:37:23 +0900</pubDate>
    </item>
    <item>
      <title>DEVIEW 2020 정리 - 묻고 한 벌로 가</title>
      <link>https://geonlee.tistory.com/228</link>
      <description>&lt;blockquote&gt;
&lt;p&gt;해당 글은 제가 DEVIEW 2020의 발표 영상을 보면서&amp;nbsp;&lt;b&gt;문서화&lt;/b&gt;한 글입니다. 발표 영상을 보시려면 아래 링크를 확인해주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tv.naver.com/v/16971863/list/657024&quot;&gt;DEVIEW 2020 - 묻고 한 벌로 가&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h1&gt;묻고 한 벌로 가&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;한 벌의 코드로 모바일 웹/모바일 앱/PC 웹 서비스 확장 개발기&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;- 여러 환경으로 서비스를 확장하면서 겪었던 시행착오를 공유&lt;/p&gt;
&lt;p&gt;- 네이버 플레이스 서비스의 기술 스택: JavaScript 웹 fullstack&lt;/p&gt;
&lt;h2&gt;1. 왜 코드 한 벌을 여러 서비스에 넣었을까?&lt;/h2&gt;
&lt;h3&gt;1.1 플레이스와 지도&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;플레이스&lt;/b&gt;와 &lt;b&gt;지도&lt;/b&gt;는 원래 다른 서비스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;검색에서 업종별 특화 화면을 보여주면 어떨까?&quot;&lt;/b&gt;라는 생각에서 플레이스 서비스 시작
&lt;ul&gt;
&lt;li&gt;식당, 숙박, 미용실, 관광 명소 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.2 플레이스/지도 통합의 배경&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;플레이스 서비스의 성장 업종별로 특화 및 세분화
&lt;ul&gt;
&lt;li&gt;해수욕장, 은행, 병/의원, 주유소, 약국(공적마스크), 미용실(스타일 검색)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;문제점: NAVER 검색 결과와 지도 검색 결과가 다르게 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.3 어떻게 통합할까?&lt;/h3&gt;
&lt;p&gt;가장 빠르고 효율적인 방법 찾기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;중복 개발 피하기 -&amp;gt; 플레이스 서비스를 지도 앱과 PC 지도에 넣자&lt;/li&gt;
&lt;li&gt;코드 재사용할 수 있도록 기존 화면을 그.대.로. 집어 넣으면 어떨까?&lt;/li&gt;
&lt;li&gt;지도앱에 Webview 형태로 플레이스 서비스를 넣으면 되지 않을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 지도앱(iOS/Android) 장소결과를 Native에서 Webview로 변경하기&lt;/h2&gt;
&lt;h3&gt;2.1 어느 영역을 Webview로 전환하지?&lt;/h3&gt;
&lt;p&gt;설계 조건&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용자가 어색함을 느끼지 않아야 한다.&lt;/li&gt;
&lt;li&gt;플레이스는 가급적 독립적인 영역으로 고립시켜야 한다.&lt;/li&gt;
&lt;li&gt;Native와 Webview의 장점을 잘 살릴 수 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-filename=&quot;2020-12-01-21-08-52.png&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;288&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh4QhK/btqO6gsIwAs/IvKOepbGvslHpJWsguzJy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh4QhK/btqO6gsIwAs/IvKOepbGvslHpJWsguzJy1/img.png&quot; data-alt=&quot;Native인 지도 판과 장소 검색 결과를 담당하는 플레이스를 Webview로 보여주자!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh4QhK/btqO6gsIwAs/IvKOepbGvslHpJWsguzJy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh4QhK%2FbtqO6gsIwAs%2FIvKOepbGvslHpJWsguzJy1%2Fimg.png&quot; data-filename=&quot;2020-12-01-21-08-52.png&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;288&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Native인 지도 판과 장소 검색 결과를 담당하는 플레이스를 Webview로 보여주자!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;2.2 Webview 설계&lt;/h3&gt;
&lt;p&gt;장소 검색 결과는 플레이스 Webview가 담당하고 그 외는 지도앱에서 담당한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;플레이스 목록 페이지 &amp;amp; 업체 페이지에서 가져오자!&lt;/li&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;1112&quot; data-origin-height=&quot;596&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5y73O/btqO3yUISmS/58QV3OuNsnU58vfCH6GkB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5y73O/btqO3yUISmS/58QV3OuNsnU58vfCH6GkB0/img.png&quot; data-alt=&quot;앱으로 들어가면서 바뀐 점들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5y73O/btqO3yUISmS/58QV3OuNsnU58vfCH6GkB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5y73O%2FbtqO3yUISmS%2F58QV3OuNsnU58vfCH6GkB0%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;596&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;앱으로 들어가면서 바뀐 점들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;2.3 지도앱 + 플레이스 검색 Flow&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1033&quot; data-origin-height=&quot;292&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KLwG5/btqOUlbwGf0/zKuRxzBGWd8X6ojikY6RPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KLwG5/btqOUlbwGf0/zKuRxzBGWd8X6ojikY6RPk/img.png&quot; data-alt=&quot;지도앱 + 플레이스 검색 Flow&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KLwG5/btqOUlbwGf0/zKuRxzBGWd8X6ojikY6RPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKLwG5%2FbtqOUlbwGf0%2FzKuRxzBGWd8X6ojikY6RPk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1033&quot; data-origin-height=&quot;292&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;지도앱 + 플레이스 검색 Flow&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;2.4 Webview -&amp;gt; Native 통신&lt;/h3&gt;
&lt;p&gt;목록 결과를 지도에 &lt;b&gt;마커&lt;/b&gt;로 그려줘야 함&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Webview &amp;lt;-&amp;gt; Native 어떻게 통신할 것인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;(aOS) App scheme 사용 (inapp://)&lt;/li&gt;
&lt;li&gt;(iOS) webkit.messageHandler 사용&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Native에서 Webview 호출 방식&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;기본: url 호출
&lt;ul&gt;
&lt;li&gt;문제: '현 지도에서 검색'터치 시 깜빡임&lt;/li&gt;
&lt;li&gt;원인: Native -&amp;gt; Webview - url 변경 호출
&lt;ul&gt;
&lt;li&gt;서버로 요청이 넘어가서 SSR된 결과를 그린다.&lt;/li&gt;
&lt;li&gt;SPA인데?&lt;/li&gt;
&lt;li&gt;보는 화면에서 목록만 업데이트 시켜주기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;해결: Webview에서 Global 함수 제공
&lt;ul&gt;
&lt;li&gt;SSR이 아닌 CSR로 업데이트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Webview 밑에 깔려 있는 지도판을 터치 할 수 있어야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Webview 위에 touch 이벤트를 받을 수 있는 &lt;b&gt;가상의 layer&lt;/b&gt;를 하나 더 두자&lt;/li&gt;
&lt;li&gt;Layer 사이즈는 Webview에서 전달하는 이벤트 기반으로 동작&lt;/li&gt;
&lt;li&gt;Webview가 스크롤 될 때마다 높이값을 Native에 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Q. 왜 Webview 영역 전체를 스크롤하지 않았나?&lt;br /&gt;A. Webview 영역을 위로 올려 최상단이 되었을 때 내부 system scroll로 전환되는 사용성이 좋지 않음&lt;/p&gt;
&lt;p&gt;Native 카드와 Webview를 묶어서 자연스럽게 보이도록 하자!&lt;/p&gt;
&lt;h2&gt;3. PC지도 서비스(Angular)에 플레이스 서비스(React) iframe 으로 집어넣기&lt;/h2&gt;
&lt;h3&gt;3.1 어떻게 통합할 것인가?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;585&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SYUi3/btqO3ytEFCX/l1p6NBiTkfBPBCMLemKxx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SYUi3/btqO3ytEFCX/l1p6NBiTkfBPBCMLemKxx1/img.png&quot; data-alt=&quot;PC지도 서비스에 플레이스 서비스를 넣는 초기 기획안&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SYUi3/btqO3ytEFCX/l1p6NBiTkfBPBCMLemKxx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSYUi3%2FbtqO3ytEFCX%2Fl1p6NBiTkfBPBCMLemKxx1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;585&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PC지도 서비스에 플레이스 서비스를 넣는 초기 기획안&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1안) 플레이스는 API만 제공 &amp;amp; 화면은 지도웹에서&lt;/li&gt;
&lt;li&gt;2안) 플레이스 화면을 재사용 &amp;amp; 지도웹은 마커
&lt;ul&gt;
&lt;li&gt;2-1안) Angular에 React Component 포함&lt;/li&gt;
&lt;li&gt;2-2안) HTML import(link rel) -&amp;gt; deprecated&lt;/li&gt;
&lt;li&gt;2-3안) iframe 으로 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.2 iframe 괜찮을까?!&lt;/h3&gt;
&lt;p&gt;단점)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;url 문제&lt;/li&gt;
&lt;li&gt;debugging 어려움&lt;/li&gt;
&lt;li&gt;보안 조심&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;장점)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;두 영역을 분리하여 고립시키기에는 오히려 좋다.&lt;/li&gt;
&lt;li&gt;cross domain components 조합&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.3 Angular + React&lt;/h3&gt;
&lt;p&gt;지도 서비스(Angular)와 플레이스 서비스(React)의 공존&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iframe 사용으로 서비스 간 고립&lt;/li&gt;
&lt;li&gt;library/framework가 달라서 발생하는 문제는 없음&lt;/li&gt;
&lt;li&gt;서비스 간에 통신을 어떻게 할 지가 더 중요&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.4 PC지도 + 플레이스 검색 flow&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;268&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/epdMxi/btqO3zeZLuc/jTYoR2TASMRcm3V2svHWmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/epdMxi/btqO3zeZLuc/jTYoR2TASMRcm3V2svHWmK/img.png&quot; data-alt=&quot;PC지도 + 플레이스 검색 flow&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/epdMxi/btqO3zeZLuc/jTYoR2TASMRcm3V2svHWmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FepdMxi%2FbtqO3zeZLuc%2FjTYoR2TASMRcm3V2svHWmK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;268&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PC지도 + 플레이스 검색 flow&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;3.5 서비스간의 통신&lt;/h3&gt;
&lt;p&gt;단순한 통신 흐름 지향&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;지도 -&amp;gt; 플레이스
&lt;ul&gt;
&lt;li&gt;iframe URL 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;플레이스 -&amp;gt; 지도
&lt;ul&gt;
&lt;li&gt;postMessage() 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.6 PC서비스에서 모바일과 달라지는 점&lt;/h3&gt;
&lt;p&gt;1) 가로 스크롤&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;가로 system scroll&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PC에서 가로 스크롤은 익숙하지 않음&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IE11 스크롤 커스텀 노출이 어려움&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;@egjs/flicking + 좌우 버튼 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;2) 무한 스크롤 관련 처리&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;모바일: 무한 스크롤&lt;/li&gt;
&lt;li&gt;PC: 페이징&lt;/li&gt;
&lt;li&gt;wrapper component로 대응&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;3) PC만 지원하는 이벤트처리&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mouseenter 이벤트&lt;/li&gt;
&lt;li&gt;mouseleave 이벤트&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;4) 사용성 때문에 변경되는 부분&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;동작 환경과 사용성에 맞춰 변경
&lt;ul&gt;
&lt;li&gt;지도 탭 삭제, 뒤로가기 버튼 삭제, MY 탭 삭제 등&lt;/li&gt;
&lt;/ul&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;ul&gt;
&lt;li&gt;iframe 내가 아닌 새 창에서 외부 페이지가 열림&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;로그인이 필요한 경우
&lt;ul&gt;
&lt;li&gt;iframe 본 창에 로그인 처리 요청&lt;/li&gt;
&lt;li&gt;본 창 &amp;amp; iframe 새로고침(로그인 쿠키 반영)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.7 Internet Explorer 11 대응&lt;/h3&gt;
&lt;p&gt;Build target 변경&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;모바일&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 한 벌의 코드로 여러 서비스 제공은 어떻게 할까?&lt;/h2&gt;
&lt;h3&gt;4.1 플레이스 서비스 개발 스택&lt;/h3&gt;
&lt;p&gt;JavaScript Web Fullstack&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Node.js with Koa&lt;/li&gt;
&lt;li&gt;React with hooks&lt;/li&gt;
&lt;li&gt;GraphQL with Apollo&lt;/li&gt;
&lt;li&gt;kubernetes with NCC&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.2 어떻게 분기처리 할 것인가?&lt;/h3&gt;
&lt;p&gt;지속 가능한 코드 = 유지보수하기 쉬운 코드&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서비스 환경을 체크하는 if/else 분기는 되도록 적게&lt;/li&gt;
&lt;li&gt;component 단위를 작은 기능에 집중해서 작게 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;서비스 운영이 쉬운 환경 구성&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;코드는 재사용&lt;/li&gt;
&lt;li&gt;domain &amp;amp; server &amp;amp; build는 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.3 배포 환경 분리&lt;/h3&gt;
&lt;p&gt;독립된 별도 서버군으로 별도 도메인으로 대응&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;코드는 재사용하지만 서비스간의 독립성은 보장&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;webpack build를 별도로 한다.&lt;/li&gt;
&lt;li&gt;환경별로 entry 포인트를 따로 가져간다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;API 레벨의 분기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GraphQL의 장점을 살리자
&lt;ul&gt;
&lt;li&gt;GraphQL은 versionless 추구
&lt;ul&gt;
&lt;li&gt;하위 호환성을 지키기 위해 field type 변경 X&lt;/li&gt;
&lt;li&gt;변경이 필요한 field는 신규로 추가하여 대응&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;화면별로 필요한 field를 정의하고 호출하는 것으로 정함&lt;/li&gt;
&lt;li&gt;GraphQL resolver는 client가 어떤 환경인지 몰라도 되도록 관리&lt;/li&gt;
&lt;li&gt;분기의 권한은 client에게 있다.&lt;/li&gt;
&lt;li&gt;GraphQL query 레벨에서 device 별 분기 처리 진행
&lt;ul&gt;
&lt;li&gt;@skip, @include 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 성능 개선&lt;/h2&gt;
&lt;h3&gt;5.1 이미지 사이즈를 줄여야 한다.&lt;/h3&gt;
&lt;p&gt;사진이 많은 건 좋은데 많이 느려졌다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;빠르고 가벼운 화면 VS. 사용자에게 좋은 정보&lt;/li&gt;
&lt;li&gt;이미지 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.2. 첫 로딩 사이즈를 줄여야 한다.&lt;/h3&gt;
&lt;p&gt;이미지 lazy loading &amp;amp; Component lazy loading&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;화면에 보이는 부분만 loading&lt;/li&gt;
&lt;li&gt;DOM 최적화 + 이미지 load 최적화&lt;/li&gt;
&lt;li&gt;react-lazyload 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dynamic Loading&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;code splitting - asset size 최적화&lt;/li&gt;
&lt;li&gt;@loadable/component&lt;/li&gt;
&lt;li&gt;react-router와 조합 -&amp;gt; path 별 resource 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SSR인데 왜 화면이 늦게 보일까?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;플레이스 페이지 노출여부 제어는 지도에서 한다.&lt;/li&gt;
&lt;li&gt;플레이스 페이지 로드 완료는 message로 플레이스에서 지도로 전달&lt;/li&gt;
&lt;li&gt;원인: message 전달 타이밍 이슈
&lt;ul&gt;
&lt;li&gt;CSR 시점에 message 전달 -&amp;gt; SSR의 이점 못살림&lt;/li&gt;
&lt;li&gt;SSR load 후 바로 로드 완료 message 전달로 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점
&lt;ul&gt;
&lt;li&gt;화면은 미리 보이지만 interaction은 CSR 후 가능&lt;/li&gt;
&lt;li&gt;&quot;SSR과 CSR 사이의 시간 &amp;lt; 사용자가 화면을 인식하는 시간&quot;이기 때문에 괜찮다고 판단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.3 Rendering 최적화&lt;/h3&gt;
&lt;p&gt;스크롤 이벤트로 인한 React Component re-render 최적화&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;상위 Component에 걸려있는 scroll 이벤트로 인한 불필요한 re-render&lt;/li&gt;
&lt;li&gt;component 별로 스크롤 이벤트가 필요한 부분만 처리하는 hook 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.4 스크롤 최적화&lt;/h3&gt;
&lt;p&gt;IntersectionObserver를 이용한 무한 스크롤 최적화&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;무한 스크롤 시 언제 추가 contents 로딩 할건지 확인&lt;/li&gt;
&lt;li&gt;기존: scroll event + getBoundingClientRect() 사용&lt;/li&gt;
&lt;li&gt;개선: IntersectionObserver() 사용 - async non-blocking API&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;장점&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;frame 안정적, 저사양 폰에서 효과가 좋음&lt;/li&gt;
&lt;li&gt;코드 개선 효과&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. 앞으로 해야할 일&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;개발 후 확인해야 할 환경이 많다 -&amp;gt; 테스트 자동화&lt;/li&gt;
&lt;li&gt;polyfill 관리 불편 -&amp;gt; 환경별 자동화&lt;/li&gt;
&lt;li&gt;업종 추가/개선&lt;/li&gt;
&lt;li&gt;끊임 없는 Refactoring/성능 개선/사용성 개선&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Devlog</category>
      <category>DEVIEW</category>
      <category>DEVIEW 2020</category>
      <category>네이버</category>
      <category>네이버 지도</category>
      <category>플레이스</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/228</guid>
      <comments>https://geonlee.tistory.com/228#entry228comment</comments>
      <pubDate>Thu, 3 Dec 2020 14:58:27 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트로 HTML5 캔버스에 회전하는 도형 만들기</title>
      <link>https://geonlee.tistory.com/227</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;javascript-frameworks.jpg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;589&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byokrb/btqOCcRv93n/LUCJP6o5TPkp4IUyhI1S20/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byokrb/btqOCcRv93n/LUCJP6o5TPkp4IUyhI1S20/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byokrb/btqOCcRv93n/LUCJP6o5TPkp4IUyhI1S20/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyokrb%2FbtqOCcRv93n%2FLUCJP6o5TPkp4IUyhI1S20%2Fimg.jpg&quot; data-filename=&quot;javascript-frameworks.jpg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;589&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;index.html&lt;/h2&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;test&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;script type=&quot;module&quot; src=&quot;app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;app.js&lt;/h2&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;import { Polygon } from &quot;./polygon.js&quot;

class App {
    constructor() {
        this.canvas = document.createElement('canvas');
        document.body.appendChild(this.canvas);
        this.ctx = this.canvas.getContext('2d');

        this.pixelRatio = window.devicePixelRatio &amp;gt; 1 ? 2 : 1;

        window.addEventListener('resize', this.resize.bind(this), false);
        this.resize()

        this.isDown = false;
        this.moveX = 0;
        this.offsetX = 0;

        document.addEventListener('pointerdown', this.onDown.bind(this), false);
        document.addEventListener('pointermove', this.onMove.bind(this), false);
        document.addEventListener('pointerup', this.onUp.bind(this), false);

        window.requestAnimationFrame(this.animate.bind(this));
    }

    resize() {
        this.stageWidth = document.body.clientWidth;
        this.stageHeight = document.body.clientHeight;

        this.canvas.width = this.stageWidth * this.pixelRatio;
        this.canvas.height = this.stageHeight * this.pixelRatio;

        this.ctx.scale(this.pixelRatio, this.pixelRatio)

        this.polygon = new Polygon(
            this.stageWidth / 2,
            this.stageHeight + (this.stageHeight/2),
            this.stageHeight / 1.5,
            15
        )
    }
    animate(ctx, moveX) {
        window.requestAnimationFrame(this.animate.bind(this));

        this.ctx.clearRect(0,0, this.stageWidth, this.stageHeight);

        this.moveX *= 0.9;

        this.polygon.animate(this.ctx, this.moveX)
    }

    onDown(e) {
        this.isDown = true
        this.moveX = 0;
        this.offsetX = e.clientX;
    }
    onMove(e) {
        if(this.isDown){
            this.moveX = e.clientX - this.offsetX;
            this.offsetX = e.clientX
        }
    }
    onUp(e) {
        this.isDown = false
    }

}

window.onload = () =&amp;gt; {
    new App();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;polygon.js&lt;/h2&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;const PI2 = Math.PI * 2;

const COLORS = [
    '#ca00d5',
    '#ef3e2f',
    '#b05af2',
    '#e8c476',
    '#e8bcf4',
    '#615118',
    '#fca6dd',
    '#ddccbd',
    '#e6cbeb',
    '#81156a',
    '#eba118',
    '#7956ed',
]

export class Polygon {
    constructor(x, y, radius, sides, rotate){
        /** 도형 중앙 x좌표 */
        this.x = x;
        /** 도형 중앙 y좌표 */
        this.y = y;
        /** 반지름 */
        this.radius = radius;
        /** 각의 수 */
        this.sides = sides;
        /** 회전한 각도 */
        this.rotate = 0
    }

    animate(ctx, moveX) {
        ctx.save();

        /** 360도를 각의 수만큼 나눈 각도 */
        const angle = PI2 / this.sides;
        const angle2 = PI2 / 4;

        /** (x, y)만큼 기준점 이동 */
        ctx.translate(this.x, this.y);

        this.rotate -= moveX * 0.008;
        ctx.rotate(this.rotate);

        for(let i = 0 ; i &amp;lt; this.sides ; i++){
            /** 각도별 도형의 중앙 x좌표 */
            const x = this.radius * Math.cos(angle * i);
            /** 각도별 도형의 중앙 y좌표 */
            const y = this.radius * Math.sin(angle * i);

            ctx.save();

            ctx.fillStyle = COLORS[i]
            /** 전체 polygon의 중심에서 (x,y)만큼 기준점 이동  */
            ctx.translate(x,y)
            /** 각도에 따라 자식 도형의 회전각도도 달라져야한다. */
            ctx.rotate(((360/this.sides) * i+45) * Math.PI / 180);

            ctx.beginPath();
            for(let j = 0;j&amp;lt;4;j++){
                const x2 = 160 * Math.cos(angle2 * j);
                const y2 = 160 * Math.sin(angle2 * j);
                /** 자식 정사각형 그리기 */
                (j == 0) ? ctx.moveTo(x2, y2) : ctx.lineTo(x2,y2);
            }
            ctx.fill();
            ctx.closePath();
            ctx.restore();
        }

        ctx.restore();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;결과물&lt;/h2&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/agitated-bouman-1qyee?autoresize=1&amp;amp;fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;</description>
      <category>Web/JavaScript</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/227</guid>
      <comments>https://geonlee.tistory.com/227#entry227comment</comments>
      <pubDate>Sat, 28 Nov 2020 22:49:44 +0900</pubDate>
    </item>
    <item>
      <title>DEVIEW 2020 WELCOME 패키지</title>
      <link>https://geonlee.tistory.com/226</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;처음으로 온라인으로 진행되는 DEVIEW 2020, WELCOME 패키지를 신청할 수 있다길래 신청했더니 최근에 택배가 와서 간단히 소개해보려한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsrhbT/btqN01i4lKI/iUKqd0T9isVzPs596LlBqk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsrhbT/btqN01i4lKI/iUKqd0T9isVzPs596LlBqk/img.jpg&quot; data-alt=&quot;뭔가 많이 들어있는 것 같은 박스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsrhbT/btqN01i4lKI/iUKqd0T9isVzPs596LlBqk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsrhbT%2FbtqN01i4lKI%2FiUKqd0T9isVzPs596LlBqk%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;뭔가 많이 들어있는 것 같은 박스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nytDF/btqNVSH8Thh/14CtJsaxA9Un1kPerDiMmk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nytDF/btqNVSH8Thh/14CtJsaxA9Un1kPerDiMmk/img.jpg&quot; data-alt=&quot;뭔가 많이 들어있기는하다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nytDF/btqNVSH8Thh/14CtJsaxA9Un1kPerDiMmk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnytDF%2FbtqNVSH8Thh%2F14CtJsaxA9Un1kPerDiMmk%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;뭔가 많이 들어있기는하다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;구성품은 다음과 같다.&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;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxtgz3/btqNYxphFUu/GZmbRhuv4bOz2rSAx7iB5K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxtgz3/btqNYxphFUu/GZmbRhuv4bOz2rSAx7iB5K/img.jpg&quot; data-alt=&quot;스티커 구성 힙한거 하나 넣어줬으면...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxtgz3/btqNYxphFUu/GZmbRhuv4bOz2rSAx7iB5K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxtgz3%2FbtqNYxphFUu%2FGZmbRhuv4bOz2rSAx7iB5K%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스티커 구성 힙한거 하나 넣어줬으면...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Devlog</category>
      <category>DEVIEW</category>
      <category>네이버</category>
      <category>스티커</category>
      <category>웰컴패키지</category>
      <category>텀블러</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/226</guid>
      <comments>https://geonlee.tistory.com/226#entry226comment</comments>
      <pubDate>Sun, 22 Nov 2020 17:05:46 +0900</pubDate>
    </item>
    <item>
      <title>Creative Coding 개발환경 설정 및 기본 프로젝트</title>
      <link>https://geonlee.tistory.com/225</link>
      <description>&lt;h1&gt;개발 환경 설정&lt;/h1&gt;
&lt;h2&gt;코드 에디터&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Visual Studio Code 확장 프로그램&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint&quot;&gt;ESlint&lt;/a&gt;, &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=dbaeumer.jshint&quot;&gt;jslint&lt;/a&gt; - JavaScript용 코드 lint 확장 프로그램&lt;/li&gt;
&lt;li&gt;Beautify - 코드 포메터 확장 프로그램&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&quot;&gt;Live Server&lt;/a&gt; - HTML 파일을 서빙하는 가상의 서버를 띄우는 확장 프로그램&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;추가 팁&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;파일이 크지 않기 때문에 로컬영역이 아닌 클라우드에 저장해서 사용하는 것도 좋다.
&lt;ul&gt;
&lt;li&gt;구글 드라이브 + &lt;a href=&quot;https://support.google.com/a/answer/7491144?hl=en&quot;&gt;드라이브 파일 스트림&lt;/a&gt;으로 로컬 드라이브처럼 사용 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;기본 프로젝트&lt;/h1&gt;
&lt;h2&gt;index.html&lt;/h2&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;test&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;script type=&quot;module&quot; src=&quot;app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;style.css&lt;/h2&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;* {
    outline: 0;
    margin: 0;
    padding: 0;
}

html {
    width: 100%;
    height: 100%;
}

body {
    width: 100%;
    height: 100%;
    background-color: #b34ec7;
}

canvas {
    width: 100%;
    height: 100%;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;app.js&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Window/requestAnimationFrame&quot;&gt;requestAnimationFrame&lt;/a&gt; - 브라우저에게 수행하기를 원하는 애니메이션을 알리고 다음 리페인트가 진행되기 전에 해당 애니메이션을 업데이트하는 함수를 호출하게 하는 함수, 재생 시간대마다 달라지는 음악의 파형을 표현하기 위해 사용하기도 한다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Window/devicePixelRatio&quot;&gt;devicePixelRatio&lt;/a&gt; - 현재 표시 장치의 물리적 픽셀과 CSS 픽셀의 비율&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/HTML/Canvas/Tutorial/Drawing_shapes&quot;&gt;캔버스를 이용한 도형 그리기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;class App {
    constructor() {
        this.canvas = document.createElement('canvas');
        document.body.appendChild(this.canvas);
        this.ctx = this.canvas.getContext('2d'); // 2D 렌더링 컨텍스트 생성

        this.pixelRatio = window.devicePixelRatio &amp;gt; 1 ? 2 : 1;

        window.addEventListener('resize', this.resize.bind(this), false);
        this.resize()

        window.requestAnimationFrame(this.animate.bind(this));
    }

    resize() {
        this.stageWidth = document.body.clientWidth;
        this.stageHeight = document.body.clientHeight;

        this.canvas.width = this.stageWidth * this.pixelRatio;
        this.canvas.height = this.stageHeight * this.pixelRatio;

        this.ctx.scale(this.pixelRatio, this.pixelRatio)
    }
    animate() {
        window.requestAnimationFrame(this.animate.bind(this));
        this.ctx.clearRect(0,0, this.stageWidth, this.stageHeight); // canvas의 사각형 영역 지우기

        this.ctx.fillStyle = '#f0d785';
        this.ctx.beginPath(); // 도형 그리기 시작
        this.ctx.arc(
            this.stageWidth /2, // 중심축의 x좌표
            this.stageHeight / 2, // 중심축의 y좌표
            300, // 반지름
            0, // 시작 각도
            2* Math.PI, // 종료 각도
            true // true일 시 반시계방향, false일 시 시계방향, 값을 안 넣었을 때에는 시계방향이 기본값
        )
        this.ctx.fill();
    }

}

window.onload = () =&amp;gt; {
    new App(); // 새로운 앱 인스턴스 생성
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;결과물&lt;/h1&gt;
&lt;p&gt;원 하나를 캔버스에 그린다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;기존 코드에서 색상값 정도만 바꾸었다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4M9GX/btqN02hXRBV/F238DhiawNfME89MKpChBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4M9GX/btqN02hXRBV/F238DhiawNfME89MKpChBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4M9GX/btqN02hXRBV/F238DhiawNfME89MKpChBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4M9GX%2FbtqN02hXRBV%2FF238DhiawNfME89MKpChBK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Web/JavaScript</category>
      <category>canvas</category>
      <category>Createive Codeing</category>
      <category>javascript</category>
      <category>애니메이션</category>
      <category>웹</category>
      <category>코딩</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/225</guid>
      <comments>https://geonlee.tistory.com/225#entry225comment</comments>
      <pubDate>Sun, 22 Nov 2020 16:50:55 +0900</pubDate>
    </item>
    <item>
      <title>Next.js의 기본 구조 정리</title>
      <link>https://geonlee.tistory.com/224</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/0mD6s/btqNZL8BdWx/JJyjijtWkWAeinnEeDFNH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0mD6s/btqNZL8BdWx/JJyjijtWkWAeinnEeDFNH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0mD6s/btqNZL8BdWx/JJyjijtWkWAeinnEeDFNH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0mD6s%2FbtqNZL8BdWx%2FJJyjijtWkWAeinnEeDFNH1%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;h1&gt;Next.js 구조&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Next.js의 기본 구조는 다음처럼 구성됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;|-- pages
|   |-- _document // HTML Document, Application Container, 각종 페이지 등을 작성한다.
|   |-- _app      // Application Container. 공통의 레이아웃을 작성한다.
|   |-- _error    // Error Page.
|   |-- hello     // Hello Page /hello로 시작되는 경로의 페이지 컴포넌트&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;_app&lt;/h2&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;// import App from 'next/app'

function MyApp({ Component, pageProps }) {
  return &amp;lt;Component {...pageProps} /&amp;gt;
}

// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// MyApp.getInitialProps = async (appContext) =&amp;gt; {
//   // calls page's `getInitialProps` and fills `appProps.pageProps`
//   const appProps = await App.getInitialProps(appContext);
//
//   return { ...appProps }
// }

export default MyApp&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;\_app.js&lt;/code&gt;는 client에서 띄우길 바라는 전체 컴포넌트의 레이아웃으로 이해하면 쉽습니다.&lt;/li&gt;
&lt;li&gt;공통 레이아웃 이므로 최초에 실행되어 내부에 들어갈 컴포넌트들을 실행합니다.
&lt;ul&gt;
&lt;li&gt;지속적으로 띄울 레이아웃&lt;/li&gt;
&lt;li&gt;페이지를 탐색 할 때 상태 유지&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentDidCatch&lt;/code&gt;를 사용하여 사용자 정의 오류 처리&lt;/li&gt;
&lt;li&gt;추가 데이터를 페이지에 주입&lt;/li&gt;
&lt;li&gt;글로벌 &lt;code&gt;CSS&lt;/code&gt; 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://nextjs.org/docs/advanced-features/custom-app&quot;&gt;https://nextjs.org/docs/advanced-features/custom-app&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;_document&lt;/h2&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      &amp;lt;Html&amp;gt;
        &amp;lt;Head /&amp;gt;
        &amp;lt;body&amp;gt;
          &amp;lt;Main /&amp;gt;
          &amp;lt;NextScript /&amp;gt;
        &amp;lt;/body&amp;gt;
      &amp;lt;/Html&amp;gt;
    )
  }
}

export default MyDocument&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;\_document.js&lt;/code&gt;는 SPA에서 시작점이 되는 &lt;code&gt;index.html&lt;/code&gt;이라고 생각하면 됩니다.&lt;/li&gt;
&lt;li&gt;서버에서만 렌더링되며 &lt;code&gt;onClick&lt;/code&gt;과 같은 이벤트 핸들러가 동작하지 않습니다.&lt;br /&gt;&lt;img src=&quot;https://salgum1114.github.io/static/images/nextjs/document-app-differences.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://nextjs.org/docs/advanced-features/custom-document&quot;&gt;https://nextjs.org/docs/advanced-features/custom-document&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;_error&lt;/h2&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;function Error({ statusCode }) {
  return &amp;lt;p&amp;gt;{statusCode ? `An error ${statusCode} occurred on server` : 'An error occurred on client'}&amp;lt;/p&amp;gt;
}

Error.getInitialProps = ({ res, err }) =&amp;gt; {
  const statusCode = res ? res.statusCode : err ? err.statusCode : 404
  return { statusCode }
}

export default Error&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;전역에서 Error 처리를 공통으로 하고자 할 때, 공통적으로 사용할 수 있는 Error Page를 작성할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://nextjs.org/docs/advanced-features/custom-error-page&quot;&gt;https://nextjs.org/docs/advanced-features/custom-error-page&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>Web/Next.js</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/224</guid>
      <comments>https://geonlee.tistory.com/224#entry224comment</comments>
      <pubDate>Sun, 22 Nov 2020 15:57:50 +0900</pubDate>
    </item>
    <item>
      <title>Creative Coding을 들어가며</title>
      <link>https://geonlee.tistory.com/223</link>
      <description>&lt;h1&gt;Creative Coding&lt;/h1&gt;
&lt;p&gt;&amp;nbsp;몇 년전에 김종민님이라는 개발자분의 &lt;a href=&quot;https://blog.cmiscm.com/?page_id=3023&quot;&gt;포트폴리오&lt;/a&gt;를 보고 엄청 놀랐던 기억이 난다. 웹에서 예술을 구현했다고 해야할까 웹이라는 접근 편의성과 인터페이스를 이용한 인터렉티브를 정말 제대로 이용하였고, 몇몇 작업물만 보아도 내공이 느껴지는 작품들이였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2019-08-29-10-53-05.2019-08-29-10_53_52.gif&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;900&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bohjmI/btqNthGyp4Y/Zqh7gRTjNK4ncqhv3dUCS1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bohjmI/btqNthGyp4Y/Zqh7gRTjNK4ncqhv3dUCS1/img.gif&quot; data-alt=&quot;가장 최근에 작업하신 LEON SANS 프로젝트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bohjmI/btqNthGyp4Y/Zqh7gRTjNK4ncqhv3dUCS1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bohjmI/btqNthGyp4Y/Zqh7gRTjNK4ncqhv3dUCS1/img.gif&quot; data-filename=&quot;2019-08-29-10-53-05.2019-08-29-10_53_52.gif&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;900&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;가장 최근에 작업하신 LEON SANS 프로젝트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;그 당시, FE 개발자를 희망하고 디자인에 관심이 있다보니 저런 작업물을 만들어보고 싶었지만, 현재까지 생업을 핑계삼아(라고 쓰고 귀찮아서라고 읽음) 이런 생각들을 구석에처박아 놨었다. 그러다가 해당 개발자분이 운영하는 채널의 영상이 드문드문 유튜브에서 추천되어서 봤더니 올 해부터 유튜브 영상으로 작품에 대한 스토리 혹은 튜토리얼 등을 올리시는 걸 알게되었다. 이렇게 본격적으로 알려주는데 구석에 쳐박아 놓았던 나의 생각을 이제 꺼내볼 때가 된 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/cpEeqACsF_Q&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;</description>
      <category>Web/JavaScript</category>
      <category>Creative Coding</category>
      <category>design</category>
      <category>javascript</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/223</guid>
      <comments>https://geonlee.tistory.com/223#entry223comment</comments>
      <pubDate>Sat, 14 Nov 2020 17:55:00 +0900</pubDate>
    </item>
    <item>
      <title>Next.js에서 Apollo Client 사용하기</title>
      <link>https://geonlee.tistory.com/222</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/ctXzHz/btqNouOBCKB/GJVMxTp3I535ralmkSnkz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctXzHz/btqNouOBCKB/GJVMxTp3I535ralmkSnkz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctXzHz/btqNouOBCKB/GJVMxTp3I535ralmkSnkz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctXzHz%2FbtqNouOBCKB%2FGJVMxTp3I535ralmkSnkz1%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;Apollo Client&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/apollographql/apollo-client&quot;&gt;@apollo/client&lt;/a&gt; 라이브러리를 이용합니다. Apollo는 GraphQL 서버에서 필요한 정확한 데이터를 쉽게 query할 수 있는 GraplQL 클라이언트입니다. 데이터를 가져오고 변조하는 것 이외에도, Apollo는 query와 그 결과를 분석하여 클라이언트 사이드 캐시를 구성하며 이를 통해 추가 query와 mutation이 실행될 때 최신 상태로 유지되게 합니다.&lt;/p&gt;
&lt;h2&gt;Next.js에서 Apollo Client 사용하기&lt;/h2&gt;
&lt;p&gt;SSR을 할 수 있는 Next.js 프레임워크에서 Apollo Client를 사용하는 방법이 여러가지 있다. &lt;a href=&quot;https://github.com/lfades/next-with-apollo&quot;&gt;next-with-apollo&lt;/a&gt;라는 라이브러리가 유명하지만 아쉽게도 최신 스펙인 &lt;code&gt;getServerSideProps&lt;/code&gt;나 &lt;code&gt;getStaticProps&lt;/code&gt;를 정식으로 지원하고 있지 않다. 따라서 이번 글에서는 라이브러리 없이 구현한 예제들을 소개하고자한다.&lt;/p&gt;
&lt;h3&gt;/lib/apolloClient.ts&lt;/h3&gt;
&lt;p&gt;우리가 사용할 Apollo Client 객체를 생성하는 파일이다. Next.js에서는 SSR 과정과 CSR 과정이 나누어져있기 때문에 이 부분까지 고려해서 짜야한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { useMemo } from &amp;#39;react&amp;#39;
import { ApolloClient, HttpLink, InMemoryCache } from &amp;#39;@apollo/client&amp;#39;
import merge from &amp;#39;deepmerge&amp;#39;

let apolloClient: ApolloClient&amp;lt;NormalizedCacheObject&amp;gt; = null

function createApolloClient() {
  return new ApolloClient({
    ssrMode: typeof window === &amp;#39;undefined&amp;#39;,
    link: new HttpLink({
      uri: &amp;#39;https://nextjs-graphql-with-prisma-simple.vercel.app/api&amp;#39;, // 서버 URL (상대 주소가 아닌 절대 주소를 써야한다.)
      credentials: &amp;#39;same-origin&amp;#39;, // `credentials`나 `headers`같은 추가적 fetch() 옵션
    }),
    cache: new InMemoryCache(),
  })
}

export function initializeApollo(initialState = null) {
  const _apolloClient = apolloClient ?? createApolloClient()

  // Next.js에서 Apollo Client를 이용해 데이터를 가져오는 함수가 있다면, 초기 상태값이 여기에서 합쳐진다.
  if (initialState) {
    // 클라이언트에서의 받은 데이터인 현재 캐시 데이터를 가져온다.
    const existingCache = _apolloClient.extract()

    // 현재 캐시와 SSR 메소드인 getStaticProps/getServerSideProps로 부터 받은 데이터를 합친다.
    const data = merge(initialState, existingCache)

    // 합쳐진 데이터를 저장한다.
    _apolloClient.cache.restore(data)
  }
  // SSG(Server Side Generation)와 SSR(Server Side Rendering)은 항상 새로운 Apollo Client를 생성한다.
  if (typeof window === &amp;#39;undefined&amp;#39;) return _apolloClient
  // 클라이언트의 Apollo Client는 한 번만 생성한다.
  if (!apolloClient) apolloClient = _apolloClient

  return _apolloClient
}

export function useApollo(initialState) {
  const store = useMemo(() =&amp;gt; initializeApollo(initialState), [initialState])
  return store
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;/pages/_app.js&lt;/h3&gt;
&lt;p&gt;우리가 Apollo Client를 사용하고자 하는 최상위 페이지(/page/~)에 있는 SSR 메소드(&lt;code&gt;getStaticProps/getServerSideProps&lt;/code&gt;) 안의 내용을 넣어줍니다. 해당 Page 컴포넌트가 로드될 때, 서버의 SSR 메소드에서 본인 혹은 자식 컴포넌트들에서 필요한 &lt;code&gt;qurey&lt;/code&gt;를 가져옵니다. &lt;code&gt;qurey&lt;/code&gt;의 값을 받았을 때, Apollo Client의 store는 완전히 초기화됩니다. 결과적으로 &lt;strong&gt;브라우져에서 가져온 데이터&lt;/strong&gt;와 &lt;strong&gt;hydrate Apollo&lt;/strong&gt;를 가진 초기 HTML를 제공할 수 있게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { ApolloProvider } from &amp;#39;@apollo/client&amp;#39;
import { useApollo } from &amp;#39;../lib/apolloClient&amp;#39;

export default function App({ Component, pageProps }) {
  const apolloClient = useApollo(pageProps.initialApolloState)

  return (
    &amp;lt;ApolloProvider client={apolloClient}&amp;gt;
      &amp;lt;Component {...pageProps} /&amp;gt;
    &amp;lt;/ApolloProvider&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;/pages/index.tsx&lt;/h3&gt;
&lt;p&gt;페이지 컴포넌트의 예시로 컴포넌트 내의 Query 메소드와 SSR 메소드를 주의깊게 확인해야합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import {
  ALL_POSTS_QUERY,
} from &amp;#39;@/~&amp;#39;
import {
  allPostsQueryVars,
} from &amp;#39;@/~&amp;#39;
import { initializeApollo } from &amp;#39;../lib/apolloClient&amp;#39;

const Home = () =&amp;gt; (
   ...
  // SSR에서 사용한 query와 같은 query(ALL_POSTS_QUERY)를 사용
  const { loading, error, data } = useQuery(ALL_POSTS_QUERY, {
    variables: allPostsQueryVars,
  })

)

export const getServerSideProps: GetServerSideProps&amp;lt;{}, {}&amp;gt; = async (ctx) =&amp;gt; {
  /*
    1. 투표 등 실시간성 데이터가 계속 업데이트 되어야하는 경우 getStaticProps와 revalidate를 이용
    2. 일반적인 경우 getServerSideProps를 이용
  */

  // browser단의 context(headers)를 SSR에 넘기는 과정
  const apolloClient = initializeApollo(null, ctx)

  await apolloClient.query({
    query: ALL_POSTS_QUERY,
    variables: allPostsQueryVars,
  })

  return {
    props: {
      initialApolloState: apolloClient.cache.extract(),
    },
  }
}

export default Home&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Web/Next.js</category>
      <category>Apollo Client</category>
      <category>graphQL</category>
      <category>javascript</category>
      <category>Next.js</category>
      <category>react</category>
      <category>typescript</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/222</guid>
      <comments>https://geonlee.tistory.com/222#entry222comment</comments>
      <pubDate>Sat, 14 Nov 2020 17:16:31 +0900</pubDate>
    </item>
    <item>
      <title>기획자</title>
      <link>https://geonlee.tistory.com/221</link>
      <description>&lt;h1&gt;기획자의 종류&lt;/h1&gt;
&lt;h2&gt;서비스 기획자&lt;/h2&gt;
&lt;p&gt;서비스 기획자는 고객이 느끼는 여러 불편과 필요를 찾아내어 기획자가 의도한 방향대로 고객들이 행동하도록하는 서비스를 기획하는 역할을 합니다. 고객 경험(UX) 개선을 위한 신규 서비스 구축부터 완성 이후에는 서비스의 데이터를 기반으로 지표를 분석하여 구체적인 해결방안을 제시하고, 제안된 해결 방안을 최종적으로 실행하는 등 서비스의 처음부터 끝까지 참여하고 총괄하는 역할을 합니다. 세부적인 분류로는 앱 기획자, 웹 기획자, 모바일 기획자 등이 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;'배달의 민족'이라는 서비스가 없던 시절은 전화로 주문했지만, '배달의 민족' 서비스가 출시된 이후에는 앱으로 주문하는 등 기존에 사용자들이 경험한 '전화 주문의 번거로움'이라는 기존 사용자 경험에서의 불편을 개선시켜주는 아디이어와 비지니스 모델을 실제로 구현한 사례입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;앱(App) 기획자&lt;/h3&gt;
&lt;p&gt;앱 기획자는 실제로 사용자가 '앱'을 설치해서 실행했을 때 처음에 어떤 화면이 나오고, 어떤 메뉴를 통해 무슨 기능을 수행할 것인지 고객의 이동 경로를 보기 좋고 사용하기 편하게 최적화된 배치를 제공할 수 있도록 어플리케이션 내의 모든 고객 경험(UX)를 총괄하는 역할을 합니다.&lt;/p&gt;
&lt;h3&gt;웹(Web) 기획자&lt;/h3&gt;
&lt;p&gt;웹 기획자는 모바일, 데스크탑 브라우저로 웹을 이용할 때 들어가는 웹사이트(홈페이지)를 기획 후 디자인, 개발을 거쳐서 완성되기까지 전체 과정을 참여하고 총괄하는 역할로 PC로도 접속 가능한 웹에 좀 더 특화된 기획자 입니다.&lt;/p&gt;
&lt;h2&gt;게임 기획자&lt;/h2&gt;
&lt;p&gt;게임 기획자는 게임의 규칙, 세계관, 스토리, 캐릭터등을 구상하여 최종적으로 사용자들에게 어떨게 게임이 보여질 지에 대한 전반적인 틀을 구성하는 역할을 합니다.&lt;/p&gt;
&lt;h2&gt;광고 기획자&lt;/h2&gt;
&lt;p&gt;광고 기획자는 광고 영상이나 글, 이미지를 이용한 컨텐츠형 관고를 기획하는 역할을 합니다. 요즘에는 인터넷 배너 광고나 SNS 채널의 게시물등 다양한 채널과 포맷의 광고를 기획합니다. 아이디어 구상를 구상하고 메시지를 도출해서 결과물을 제작하는 일련의 과정을 총괄합니다.&lt;/p&gt;
&lt;h2&gt;프로모션 기획자 (온/오프라인)&lt;/h2&gt;
&lt;p&gt;프로모션 기획자는 온라인과 오프라인으로 나눌 수 있습니다. 온라인 프로모션은 새로운 게임의 출시, 블랙 프라이데이세일같은 브랜딩 프로모션에서 특정한 브랜드의 '이슈'를 어떻게 하면 디지털 채널을 이용하여 효율적으로 참여를 유도할 수 있을 것인가?를 고민하는 작업입니다. 오프라인 프로모션은 흔히 말하는 '행사'로 청계청 마라톤, 페스티벌 등 다양한 오프라인 행사의 기획과 준비를 하는 작업입니다.&lt;/p&gt;
&lt;h2&gt;전략 기획자&lt;/h2&gt;
&lt;p&gt;전략 기획자는 특정 브랜드나 서비스를 고객한테 효율적으로 연결할 방법을 기획합니다. 예를 들어 SK의 'FLO'가 음악 스트리밍 서비스로서 '멜론'이나 '애플 뮤직'같은 여러 경쟁 서비스와 대비한 차별화 전략이 무엇이 있을 지를 고민합니다. 시장에 대한 거시적인 흐름 파악과 전략 수립을 하며, 위의 뚜력한 역할이 정해진 기획자들과 달리 시장 환경의 이해, 타겟 분석 등 마케터의 역할도 하면서 다양한 채널 별 성장 방안과 마케터와의 긴밀한 커뮤니케이션을 하는 총괄적 의미의 기획자입니다.&lt;/p&gt;
&lt;h1&gt;기획자에게 필요한 자질&lt;/h1&gt;
&lt;h2&gt;관찰력&lt;/h2&gt;
&lt;p&gt;세상이 돌아가는 흐름 속에서 '불편한 문제'를 파악하고, 문제뿐 아니라 잠재된 기회 요소 발굴해야합니다. 따라서, 편협한 시야가 아닌 포괄적 시야로 세상을 바라보는 관찰력과 호기심이 필요합니다. 평소에도 내가 서비스를 사용하면서 겪은 고객 경험에서의 편안함이 어떤 방식을 통해 이루어질 수 있는 지 고객의 관점에서 분석해보는 것도 좋습니다. 또한 현재 유행하는 것이 무엇인지 아는 트랜드 트래킹 능력도 관찰력을 키우는 데에 도움을 줄 수 있습니다.&lt;/p&gt;
&lt;h2&gt;문제해결적 사고&lt;/h2&gt;
&lt;p&gt;결국에는 문제의 '해결'이 핵심과제입니다. 일반인은 '생각'에 그치지만 기획자는 '실체'로 구축 해야합니다. 따라서 그 과정에서 '문제와 불편함을 어떻게 해소할 수 있을 까?'라는 구체적인 방안을 창의적으로 구현하는 것이 필요합니다.&lt;/p&gt;
&lt;h2&gt;창의력&lt;/h2&gt;
&lt;p&gt;문제를 발견하고 문제에 대한 해결방한까지 잘 모색했더라도, 결국 고객이 사용하는 것은 '서비스'입니다. 앞서 발굴한 문제와 기획자의 아이디어가 '어떤 모습'과 '어떤 경험'을 제공할 것인가에 대한 고객의 전체 여정을 고려한 창의적 사고가 필요합니다.&lt;/p&gt;
&lt;h2&gt;커뮤니케이션&lt;/h2&gt;
&lt;p&gt;디자인, 개발쪽 모두와 협업을 하기 때문에 커뮤니케이션 능력이 필요합니다.&lt;/p&gt;
&lt;h1&gt;기획자가 되려면...&lt;/h1&gt;
&lt;p&gt;우선, 같은 기획자이지만 주력 채널과 영역이 구분되기 때문에 '어떤' 기획자가 될 것인가에 대한 고민을 해야합니다. 위에서 설명한 각각의 기획자들이 어떤 일을 하는 지, 어떤 보람이 있는 지 힘듦이 있는 지 살펴볼 필요가 있습니다. 책을 좋아해서 좋은 책을 만들고 싶다면 출판사에서 출판 기획 업무를 할 수도 있는 것입니다. 나의 일적인 성형과 스타일을 비교하여 잘 선택해야합니다. 결국 기획자란 세상에 없는 일을 하는 사람입니다. 따라서 새롭고 창의적인 일을 원하는 사람이 어울리는 직업입니다.&lt;/p&gt;</description>
      <category>Life/Bla Bla</category>
      <category>기획</category>
      <category>기획자</category>
      <category>서비스기획자</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/221</guid>
      <comments>https://geonlee.tistory.com/221#entry221comment</comments>
      <pubDate>Sat, 17 Oct 2020 17:59:00 +0900</pubDate>
    </item>
    <item>
      <title>Express와 Koa를 비교해보자</title>
      <link>https://geonlee.tistory.com/217</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/b2LBzv/btqI4TqaMO3/vtlXc2ZRSp8Wkz7Qi7MiiK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2LBzv/btqI4TqaMO3/vtlXc2ZRSp8Wkz7Qi7MiiK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2LBzv/btqI4TqaMO3/vtlXc2ZRSp8Wkz7Qi7MiiK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2LBzv%2FbtqI4TqaMO3%2FvtlXc2ZRSp8Wkz7Qi7MiiK%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;ul&gt;
&lt;li&gt;비교 기준
&lt;ul&gt;
&lt;li&gt;인기(GitHub 스타 수, npm 다운로드 수)&lt;/li&gt;
&lt;li&gt;설치&lt;/li&gt;
&lt;li&gt;기초 Hello World 앱&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;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Express&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/expressjs/express&quot;&gt;GitHub 스타 수: +50,100&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/express&quot;&gt;npm 주간 다운로드 수: +12MM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://expressjs.com/ko/&quot;&gt;Express&lt;/a&gt;는 최소한의 유연한 웹과 모바일 애플리케이션을 위한 탄탄한 특징들을 제공하는 Node.js 웹 애플리케이션 프레임워크로 서버와 라우트를 다루는 걸 도와주는 미들웨어처럼 작동합니다.&lt;/p&gt;
&lt;h2&gt;설치&lt;/h2&gt;
&lt;p&gt;express를 설치하기 위해서 Node.js가 설치되어있어야 합니다. 만약 express를 특정 위치에 설치하고자한다면&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;$ npm install express --save&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;잠깐 설치하고 dependencies 목록에 추가하기를 원하지 않는다면&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;$ npm install express --no-save&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Hello World&lt;/h2&gt;
&lt;p&gt;아래는 3000번 포트를 사용하여 &quot;Hello World&quot;로 응답하는 express 앱을 만드는 가장 간단한 예시입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) =&amp;gt; res.send('Hello World!'))

app.listen(port, () =&amp;gt; console.log(`Example app listening on port ${port}!`))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다른 url로 간다면 서버는 &lt;code&gt;404 Not Found&lt;/code&gt;로 응답할 것입니다.&lt;/p&gt;
&lt;h2&gt;장점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Node.js 웹 미들웨어의 표준입니다.&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;h2&gt;단점&lt;/h2&gt;
&lt;p&gt;Express.js가 매우 편리하고 사용하기 쉬운 프레임워크이지만, 개발 과정중 적잖은 단점이 영향을 줄 것입니다.&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;/ul&gt;
&lt;h2&gt;성능&lt;/h2&gt;
&lt;p&gt;Express는 익숙한 Node.js의 특징을 숨기지 않고 기초적인 웹 애플리케이션 기능의 얆은 레이어를 제공합니다.&lt;/p&gt;
&lt;h3&gt;보안&lt;/h3&gt;
&lt;p&gt;Node.js의 취약성이 곧 Express의 취약점이 됨, 따라서 Node.js의 취약성을 예의주시하고 있어야하고, Node.js 최신 안정 버전으로 지속적으로 업데이트 해야 합니다.&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://koajs.com/&quot;&gt;Koa&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Express를 만든 팀이 2016년도에 만듦&lt;/li&gt;
&lt;li&gt;내장 미들웨어가 번들되어있지 않는 등 기본으로 설계된 뼈대가 적지만 탄탄함(경량화)&lt;/li&gt;
&lt;li&gt;미들웨어를 붙이는 등 커스터마이징이 자유로움&lt;/li&gt;
&lt;li&gt;태생이 ES6 기반이기 때문에 async/await을 지원해서 try-catch 에러 처리가 필요없음&lt;/li&gt;
&lt;li&gt;콜백 지옥 탈출 가능 (개발자 경험)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;보안&lt;/h3&gt;
&lt;p&gt;보안을 위한 다양한 미들웨어나 헤더가 준비되어있음&lt;/p&gt;
&lt;h2&gt;성능&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Express(38510 req/sec) &amp;lt; Koa(50933 req/sec)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;코드 비교&lt;/h2&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Express&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const express = require('express'); 
const app = express(); 
const PORT = process.env.PORT || 3000; 
const server = app.listen(PORT, () =&amp;gt; { 
  console.log(`Express is listening to &amp;lt;http://localhost&amp;gt;:${PORT}`); 
});&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Koa&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const koa = require('koa'); 
const app = koa(); 
const PORT = process.env.PORT || 3000; 
const server = app.listen(PORT, () =&amp;gt; { 
  console.log(`Koa is listening to &amp;lt;http://localhost&amp;gt;:${PORT}`); 
});&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Middleware&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Express&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;app.use((req, res, next) =&amp;gt; { 
  console.log(`Time : ${Date.now()}`); 
  next(); 
});&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Koa&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;app.use(async (ctx, next) =&amp;gt; { 
  console.log(`Time: ${Date.now()}`); 
  await next(); 
});&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;참고자료&lt;/h1&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@giljae/back-end-for-front-end-pattern-bff-4b73f29858d6&quot;&gt;Back-end for Front-end Pattern (BFF)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://devlog-h.tistory.com/13&quot;&gt;Koa vs Express 비교&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://nextjs.org/docs/advanced-features/custom-server&quot;&gt;next custom server documentation&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/examples/custom-server/server.js&quot;&gt;next-custom-server&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/examples/custom-server-express/server.js&quot;&gt;next-custom-server-express&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/examples/custom-server-koa/server.js&quot;&gt;next-custom-server-koa&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://backend-intro.vlpt.us/1/04.html&quot;&gt;Koa를 사용하여 웹서버 만들기&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://nodesource.com/blog/Express-Koa-Hapi&quot;&gt;Choosing the right Node.js Framework: Express, Koa, or Hapi?&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Web</category>
      <category>BFF</category>
      <category>express</category>
      <category>KoA</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/217</guid>
      <comments>https://geonlee.tistory.com/217#entry217comment</comments>
      <pubDate>Tue, 15 Sep 2020 01:08:39 +0900</pubDate>
    </item>
    <item>
      <title>create-react-app 와 next.js 어떤 걸 써야 할까?</title>
      <link>https://geonlee.tistory.com/216</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;600&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3knqf/btqIPSdmtc7/QAHX0TeA8kOUKDAKY3lprk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3knqf/btqIPSdmtc7/QAHX0TeA8kOUKDAKY3lprk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3knqf/btqIPSdmtc7/QAHX0TeA8kOUKDAKY3lprk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3knqf%2FbtqIPSdmtc7%2FQAHX0TeA8kOUKDAKY3lprk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;600&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;h1&gt;CRA(eject X) vs CRA(eject O) vs Next.js&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CRA without eject&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CRA에서 eject를 하지 않고 사용하는 방법&lt;/li&gt;
&lt;li&gt;React를 이용하여 프로젝트를 만들 때 사용하는 가장 기본적인 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신경써야 할 코드량이 적음&lt;/li&gt;
&lt;li&gt;webpack 설정을 신경쓰지 않을 수 있음&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;react-scripts&lt;/code&gt;로 인한 추상화로 설정 flow 이해가 힘듬&lt;/li&gt;
&lt;li&gt;추상화 정도가 높아서 라이브러리 적용시 webpack등 코어한 부분을 건드려야할 경우, 결국 eject를 해야 함&lt;/li&gt;
&lt;li&gt;(CRA 공통) webpack-dev-server를 이용한 개발 모드를 위한 서버만 제공, build된 파일로 프로덕션 서버(BFF) 실행을 위해서는 CRA와 동떨어진 새로운 서버를 만들어야함&lt;/li&gt;
&lt;li&gt;(CRA 공통) SSR을 자체적으로 지원하지 않기 때문에 추가 작업 필요&lt;/li&gt;
&lt;li&gt;(CRA 공통) code-splitting을 위해서는 특별히 코드에 명시해야 함&lt;/li&gt;
&lt;li&gt;(CRA 공통) client-side routing을 위해서 &lt;code&gt;react-router&lt;/code&gt;같은 라이브러리를 설치해야 함&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CRA with eject&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CRA에서 eject를 해서 사용하는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;webpack을 커스터마이징하여 설정할 수 있음&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;건드릴 필요 없는 불필요한 코드 증가로 인한 복잡성 증가&lt;/li&gt;
&lt;li&gt;JavaScript로 짜여진 CRA의 모든 config 파일들(&lt;code&gt;Webpack&lt;/code&gt;, &lt;code&gt;Babel&lt;/code&gt; ...)을 직접 유지보수해야 함&lt;/li&gt;
&lt;li&gt;One Build Dependency의 장점을 잃게 됨, 작업 도중 하나의 패키지가 필요해서 설치한다거나, 삭제할 때, 항상 다른 패키지들과의 의존성을 신경써야 함&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Zero configuration 컨셉을 지닌 ZEIT사의 &lt;a href=&quot;https://nextjs.org/&quot;&gt;next.js&lt;/a&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;a href=&quot;https://medium.com/@ljs0705/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%9B%B9-react-%ED%8F%AC%ED%8C%85-%ED%9B%84%EA%B8%B0-76402cc5e031&quot;&gt;카카오 페이지&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/zeit/next.js/discussions/10640#discussioncomment-595&quot;&gt;트리플&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hyperconnect.github.io/2019/10/25/webview-history.html?fbclid=IwAR0wBKMWPrNAAZOfohTteaUFH1FwJCMuQx7u6Mz5Iti9ZpvWNf8q4LqDTO0&quot;&gt;하이퍼커넥트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://woowabros.github.io/experience/2020/04/14/stop-making-todo-list.html&quot;&gt;우아한 형제들 웹툰&lt;/a&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;Netflix, Tencent News, Twitch, Hulu, Nike, Uber, AT&amp;amp;T, Auth0 등 &lt;a href=&quot;https://nextjs.org/showcase&quot;&gt;다양한 곳&lt;/a&gt;에서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수십 가지 다양한 라이브러리 &lt;a href=&quot;https://github.com/zeit/next.js/tree/canary/examples&quot;&gt;연동 예제&lt;/a&gt; (redux, apollo, &amp;hellip;) 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Zero Configuration&lt;/b&gt;이라는 특징에 맞게 플랫폼 관리 시간이 적게 들고 컴포넌트 개발을 통한 서비스 개발에 집중할 수 있게 함&lt;/li&gt;
&lt;li&gt;데이터 프리패칭 지원 - SSG(Static Site Generation), SSR(ServerSide Rendering) 사용가능 (CSR도 사용가능)&lt;/li&gt;
&lt;li&gt;개발 모드를 위한 서버 뿐만 아니라, 프로덕션 서버(BFF)도 제공 및 다양한 서버 라이브러리(&lt;code&gt;Express&lt;/code&gt;, &lt;code&gt;Koa&lt;/code&gt;)로 수정 가능&lt;/li&gt;
&lt;li&gt;SSR 지원 및 Google Chrome 팀과 협업으로 SEO 및 로드 성능이 높음&lt;/li&gt;
&lt;li&gt;코드베이스가 TypeScript이고, 자체적으로 TypeScript 지원&lt;/li&gt;
&lt;li&gt;특별한 설정을 하지 않아도 지원해주는 개발 편의기능 (페이지에 사용되는 코드만 로드하는 &lt;code&gt;code-splitting&lt;/code&gt;를 통한 번들 최적화, 파일 기반 라우팅, Webpack, Babel을 이용한 자동 컴필레이션 및 번들링, &lt;code&gt;React Fast Refresh&lt;/code&gt;, &lt;code&gt;SSG&lt;/code&gt;, &lt;code&gt;SSR&lt;/code&gt;, static 파일 서빙시 &lt;code&gt;./public&lt;/code&gt;를 &lt;code&gt;/&lt;/code&gt;에 매핑해줌)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커스텀해야 할 부분이 생길 경우가 있음 (그러나 CRA eject에 비하면 편리)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;next.config.js&lt;/code&gt; - next-transpile-modules, spritePlugin, optimizedImages&lt;/li&gt;
&lt;li&gt;&lt;code&gt;custom server&lt;/code&gt; - 지원하지 않는 브라우저 처리 혹은 미들웨어 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리액트 프레임워크이기 때문에 프레임워크에 대한 지식 필요(&lt;code&gt;_app.js&lt;/code&gt;, &lt;code&gt;_document.js&lt;/code&gt; 등)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;추상화 정도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CRA(eject X) &amp;gt; next.js &amp;gt; CRA(eject O)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발자 경험&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;next.js &amp;gt; CRA(eject X) &amp;gt; CRA(eject O)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@jsh901220/create-react-app%EC%97%90%EC%84%9C-eject%EC%82%AC%EC%9A%A9%EC%95%88%ED%95%98%EA%B8%B0-customize-cra-react-app-rewired-10a83522ace0&quot;&gt;Create-React-App에서 Eject사용안하기(customize-cra, react-app-rewired)&lt;/a&gt; - CRA eject 비교&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://coffeencoding.com/cra-vs-next-js-vs-gatsby/&quot;&gt;CRA vs Next.js vs Gatsby &amp;ndash; Comparison and How to Choose One&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://leerob.io/blog/nextjs-gatsby-create-react-app&quot;&gt;Next.js vs. Gatsby vs. Create React App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=jg2ha2RIWN0&amp;amp;feature=youtu.be&amp;amp;ab_channel=44BITS&quot;&gt;[4분코딩] 리액트(React)와 찰떡 궁합 Next.js&lt;/a&gt; - 간단한 Next.js 설명&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Web/Next.js</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/216</guid>
      <comments>https://geonlee.tistory.com/216#entry216comment</comments>
      <pubDate>Tue, 15 Sep 2020 00:16:19 +0900</pubDate>
    </item>
    <item>
      <title>Monorepo와 lerna</title>
      <link>https://geonlee.tistory.com/215</link>
      <description>&lt;h1&gt;Monorepo vs Polyrepo&lt;/h1&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/x0YoC/btqEFEqiebd/ywDrsooIX56dKCC4Ue7Y50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x0YoC/btqEFEqiebd/ywDrsooIX56dKCC4Ue7Y50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x0YoC/btqEFEqiebd/ywDrsooIX56dKCC4Ue7Y50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx0YoC%2FbtqEFEqiebd%2FywDrsooIX56dKCC4Ue7Y50%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;☝️ Monorepo&lt;/h2&gt;
&lt;p&gt;Monorepo는 여러 프로젝트의 소스 코드를 관리하는 데에 하나의 저장소(repository)만 사용하는 것을 의미합니다. 예를 들어 Monorepo를 이용하면 웹 앱 프로젝트, 모바일 앱 프로젝트, 서버 프로젝트가 한 저장소에 있게 됩니다.&lt;/p&gt;
&lt;p&gt;Monorepo와 대비되는 개념으로는 Polyrepo(혹은 Multirepo)가 있습니다.&lt;/p&gt;
&lt;h2&gt;  Polyrepo&lt;/h2&gt;
&lt;p&gt;Polyrepo는 여러 프로젝트의 소스 코드를 관리하는 데에 여러 개의 저장소를 사용하는 것을 의미합니다. Monorepo와 달리 Polyrepo에서는 웹 앱 프로젝트, 모바일 앱 프로젝트, 서버 프로젝트가 각각의 저장소를 갖게 됩니다.&lt;/p&gt;
&lt;h2&gt;  Monorepo의 장점&lt;/h2&gt;
&lt;h3&gt;통합적인 test, build, release 작업&lt;/h3&gt;
&lt;p&gt;한 저장소에서 관리되기 때문에 한 번의 커맨드로 여러 패키지의 스크립트(&lt;code&gt;test&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;release&lt;/code&gt;)를 실행할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;코드의 통일성 증가&lt;/h3&gt;
&lt;p&gt;여러 개의 저장소로 관리될 경우 다른 저장소를 보게 될 기회가 많이 없기 때문에 전체적인 &lt;b&gt;코드 스타일&lt;/b&gt;, &lt;b&gt;아키텍쳐 패턴&lt;/b&gt;, &lt;b&gt;테스트 방법&lt;/b&gt; 등에 통일성이 없이 코드가 중구난방이 됩니다. 혹여 통일성을 맞추려해도 통일성에 대한 문서화 작업과 복잡한 적용 과정을 거치게 되면서 많은 시간을 허비하게 됩니다. 또한 같은 기능을 가진 함수를 각자의 방식으로 10개의 저장소에서 사용되었다고하면 불필요한 공간을 차지한다고도 볼 수 있습니다. 그러나, Monorepo로 관리될 경우 이러한 통일성의 문제를 해결할 수 있습니다. 전체 프로젝트에 적용되는 &lt;code&gt;eslint&lt;/code&gt;로 코드 스타일을 맞추고 다른 프로젝트 코드를 보면서 아키텍쳐 패턴을 확인할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;분산된 이슈의 통합&lt;/h3&gt;
&lt;p&gt;대주제에 해당하는 하나의 저장소에서 &lt;b&gt;이슈&lt;/b&gt;를 처리하기 때문에 여러 저장소에서 이슈를 올리면서 논의가 분산되는 것을 막을 수 있습니다.&lt;/p&gt;
&lt;h3&gt;개별적 모듈 버전 관리&lt;/h3&gt;
&lt;p&gt;Monorepo로 관리되어도 내부의 프로젝트들은 각각 개별적인 모듈이기 때문에 각각 버전 관리를 할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;쉬운 코드 공유&lt;/h3&gt;
&lt;p&gt;기존의 방식대로라면 &lt;code&gt;npm&lt;/code&gt;등의 패키지 매니저에 등록하고 해당 패키지를 설치하여 사용하는 방식이지만, Monorepo로 관리된다면 다음과 같이 패키지 매니저 등록 없이 코드를 공유할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;// ./package.json
{
  &quot;name&quot;: &quot;@foo/server&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;license&quot;: &quot;MIT&quot;,
  &quot;dependencies&quot;: {
    &quot;@foo/common&quot;: &quot;^1.0.1&quot;,
    ...
  },
  &quot;scripts&quot;: {
      &quot;test&quot;: &quot;echo testing server with version: $npm_package_version&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;공유한 패키지 사용 방법은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;// ./packages/server/*
const commonFunction = require(&quot;@foo/common&quot;);

commonFunction();&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;Monorepo&lt;/th&gt;
&lt;th&gt;Polyrepo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;저장소 갯수&lt;/td&gt;
&lt;td&gt;1개&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;td&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이슈 관리&lt;/td&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;td&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;  lerna&lt;/h1&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/bbKnxK/btqEC7UYHuU/QqLwmBeZMSpclBk2yY6Pkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbKnxK/btqEC7UYHuU/QqLwmBeZMSpclBk2yY6Pkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbKnxK/btqEC7UYHuU/QqLwmBeZMSpclBk2yY6Pkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbKnxK%2FbtqEC7UYHuU%2FQqLwmBeZMSpclBk2yY6Pkk%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;lerna&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/lerna/lerna&quot;&gt;lerna&lt;/a&gt;는 다중의 패키지가 있는 Monorepo 구조로 된 자바스크립트 프로젝트의 &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;release&lt;/code&gt; 같은 작업을 최적화 시켜주는 툴입니다. lerna는 위에 언급했던 것처럼 여러 저장소의 의존성을 묶어주는 역할과 각 패키지별 버전 업데이트를 도와줍니다.&lt;/p&gt;
&lt;h2&gt;lerna 프로젝트 구성&lt;/h2&gt;
&lt;p&gt;lerna를 이용한 프로젝트는 다음과 같은 구조를 가집니다.&lt;/p&gt;
&lt;pre class=&quot;ceylon&quot;&gt;&lt;code&gt;lerna-repo/
  packages/
    package_1/package.json
    package_2/package.json
  package.json
  lerna.json&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;packages&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;packages&lt;/code&gt;는 필요한 node 패키지들을 관리하는 폴더입니다. &lt;code&gt;Polyrepo&lt;/code&gt;에서 &lt;code&gt;Monorepo&lt;/code&gt;로 전환하는 경우 사용하던 node 패키지를 그대로 넣어서 사용 가능합니다.&lt;/p&gt;
&lt;h3&gt;package.json&lt;/h3&gt;
&lt;p&gt;최상위에 있는 &lt;code&gt;package.json&lt;/code&gt;는 기존의 &lt;code&gt;package.json&lt;/code&gt;처럼 &lt;code&gt;script&lt;/code&gt;를 추가하여 개별적으로 혹은 동시에 패키지의 스크립트를 실행할 수 있게 합니다.&lt;/p&gt;
&lt;h3&gt;lerna.json&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;lerna.json&lt;/code&gt;에서는 &lt;code&gt;lerna&lt;/code&gt;관련 커맨드를 사용하는 옵션을 결정합니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
 &quot;packages&quot;: [ &quot;packages/*&quot; ], // node 패키지가 들어갈 폴더
 &quot;npmClient&quot;: &quot;npm&quot;, // npm 혹은 yarn 중 어떤 것을 사용할 지
 &quot;bootstrap&quot;: {
  &quot;ignore&quot;: // lerna bootstrap에서 제외되는 부분
  &quot;npmClientArgs&quot;: // npm install 명령 인자
 },
 &quot;version&quot;: &quot;0.0.0&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;lerna 시작하기&lt;/h2&gt;
&lt;p&gt;lerna를 이용한 기본적인 프로젝트를 만들어봅시다.&lt;br /&gt;우선, lerna관련 커맨드를 사용해야하기 때문에 lerna를 전역으로 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;$ npm install --global lerna&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;새로운 git 저장소를 생성하고 해당 폴더로 들어갑니다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ git init lerna-repo &amp;amp;&amp;amp; cd lerna-repo&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;해당 프로젝트를 lerna 기본 구조로 바꿉니다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ npx lerna init&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 작업까지 끝나면 최종적으로 다음과 같은 구조를 가진 프로젝트가 생성됩니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;lerna-repo/
  packages/
  package.json
  lerna.json&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;lerna에서 사용할 수 있는 커맨드&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;lerna init&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;새로운 lerna 저장소를 생성하거나 현재 저장소를 새로운 lerna 버전으로 업데이트합니다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;lerna bootstrap&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;각 패키지별로 겹치는 의존성(dependency)은 연결하면서 현재 lerna 저장소의 패키지들의 모든 의존성을 설치합니다. 예를 들어 &lt;code&gt;@foo/server&lt;/code&gt;와 &lt;code&gt;@foo/client&lt;/code&gt;에서 같은 &lt;code&gt;lodash&lt;/code&gt; 라이브러리를 사용한다면, 겹치는 의존성은 연결되어서 루트 디렉토리의 &lt;code&gt;node_modules&lt;/code&gt;에 설치되고, 겹치지 않는 의존성은 각각의 패키지 프로젝트의 &lt;code&gt;node_modules&lt;/code&gt;에 설치됩니다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;lerna import &amp;lt;pathToRepo&amp;gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;로컬에 위치하는 &lt;code&gt;&amp;lt;pathToRepo&amp;gt;&lt;/code&gt;에 해당하는 패키지를 커밋 히스토리와 함께 &lt;code&gt;packages/&amp;lt;directory-name&amp;gt;&lt;/code&gt;으로 import합니다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;lerna publish&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;업데이트된 패키지의 새로운 배포를 생성합니다. 새 버전으로 올리고 git과 npm에서 모든 패키지를 업데이트합니다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;lerna changed&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;지난 배포 이후 어떤 패키지에 변화가 있었는 지 확인합니다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;lerna diff [package?]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;지난 배포 이후 개별 혹은 모든 패키지의 &lt;code&gt;diff&lt;/code&gt;를 보여줍니다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;lerna run [script]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;해당 &lt;code&gt;script&lt;/code&gt;를 포함하는 각 패키지에 해당 &lt;a href=&quot;https://docs.npmjs.com/misc/scripts&quot;&gt;npm script&lt;/a&gt;를 실행합니다. 각 프로젝트에 &lt;code&gt;test&lt;/code&gt;관련 script가 존재한다면, 루트 디렉토리에서 &lt;code&gt;lerna run test&lt;/code&gt; 커맨드로 모든 프로젝트의 테스트 코드를 실행할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;lerna ls&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;현재 lerna 저장소에 있는 모든 public 패키지를 나열합니다.&lt;/p&gt;
&lt;h1&gt;참고자료&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=p6qoJ4apCjA&quot;&gt;How to Use Lerna&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=0_qhdOeMuhk&quot;&gt;Better code sharing through monorepos?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=rdeBtjBNcDI&quot;&gt;Marcel Cutts - MonoRepos for the Masses | ReactNext 2018&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=lV8-1S28ycM&quot;&gt;Uber Technology Day: Monorepo to Multirepo and Back Again&lt;/a&gt; - 우버의 안드로이드 패키지가 Monorepo에서 시작해서 Multirepo로 바꿨다가 다시 Monorepo로 돌아오게 된 내용의 영상&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=7Lr8xYPKG5w&quot;&gt;Jacob Bass: Modularity and Monorepositories&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;https://youtu.be/G8KXFWftCg0&quot;&gt;Yarn Workspaces Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Design Pattern</category>
      <category>javascript</category>
      <category>lerna</category>
      <category>monorepo</category>
      <category>multirepo</category>
      <category>polyrepo</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/215</guid>
      <comments>https://geonlee.tistory.com/215#entry215comment</comments>
      <pubDate>Fri, 5 Jun 2020 14:44:39 +0900</pubDate>
    </item>
    <item>
      <title>tsconfig.json 컴파일 옵션 정리</title>
      <link>https://geonlee.tistory.com/214</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;922&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl6MRI/btqEsEyPlol/LxfgqwoIou3K8JpSvLcRLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl6MRI/btqEsEyPlol/LxfgqwoIou3K8JpSvLcRLK/img.png&quot; data-alt=&quot;TypeScript&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl6MRI/btqEsEyPlol/LxfgqwoIou3K8JpSvLcRLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl6MRI%2FbtqEsEyPlol%2FLxfgqwoIou3K8JpSvLcRLK%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;922&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TypeScript&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;tsconfig.json&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Typescript&lt;/b&gt;는 동적 타입언어인 &lt;b&gt;JavaScript&lt;/b&gt;를 타입이 있는 형태로 코딩할 수 있게 해주는 언어입니다. 따라서 TypeScript로 짜여진 코드를 JavaScript로 변환해 줄 트랜스파일러(컴파일러)가 필요합니다. 컴파일은 다음과 같은 커맨드로 사용 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;tsc foo.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 컴파일 과정에 여러 옵션을 정할 수 있는데 이 옵션을 작성하는 파일이 &lt;code&gt;tsconfig.json&lt;/code&gt;입니다. &lt;code&gt;tsconfig.json&lt;/code&gt;의 기본 형태는 다음 커맨드를 통해 생성할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;npx tsc --init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 커맨드로 생성되는 기본 형태의 &lt;code&gt;tsconfig.json&lt;/code&gt; 및 옵션들은 다음과 같습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;tsconfig.json의 컴파일 옵션 정리&lt;/h2&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    /* https://aka.ms/tsconfig.json 를 방문하면 해당 파일에 대한 더 많은 정보를 얻을 수 있습니다. */

    // 옵션은 아래와 같은 형식으로 구성되어 있습니다.
    // &quot;모듈 키&quot;: 모듈 값                        /* 설명: 사용가능 옵션 (설명이 &quot;~ 여부&quot;인 경우 'true', 'false') */

    /* 기본 옵션 */
    // &quot;incremental&quot;: true,                   /* 증분 컴파일 설정 여부 */
    &quot;target&quot;: &quot;es5&quot;,                          /* 사용할 특정 ECMAScript 버전 설정: 'ES3' (기본), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 혹은 'ESNEXT'. */
    &quot;module&quot;: &quot;commonjs&quot;,                     /* 모듈을 위한 코드 생성 설정: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    // &quot;lib&quot;: [],                             /* 컴파일에 포함될 라이브러리 파일 목록 */
    // &quot;allowJs&quot;: true,                       /* 자바스크립트 파일 컴파일 허용 여부 */
    // &quot;checkJs&quot;: true,                       /* .js 파일의 오류 검사 여부 */
    // &quot;jsx&quot;: &quot;preserve&quot;,                     /* JSX 코드 생성 설정: 'preserve', 'react-native', 혹은 'react'. */
    // &quot;declaration&quot;: true,                   /* '.d.ts' 파일 생성 여부. */
    // &quot;declarationMap&quot;: true,                /* 각 '.d.ts' 파일의 소스맵 생성 여부. */
    // &quot;sourceMap&quot;: true,                     /* '.map' 파일 생성 여부. */
    // &quot;outFile&quot;: &quot;./&quot;,                       /* 단일 파일로 합쳐서 출력합니다. */
    // &quot;outDir&quot;: &quot;./&quot;,                        /* 해당 디렉토리로 결과 구조를 보냅니다. */
    // &quot;rootDir&quot;: &quot;./&quot;,                       /* 입력 파일의 루트 디렉토리(rootDir) 설정으로 --outDir로 결과 디렉토리 구조를 조작할 때 사용됩니다. */
    // &quot;composite&quot;: true,                     /* 프로젝트 컴파일 여부 */
    // &quot;tsBuildInfoFile&quot;: &quot;./&quot;,               /* 증분 컴파일 정보를 저장할 파일 */
    // &quot;removeComments&quot;: true,                /* 주석 삭제 여부 */
    // &quot;noEmit&quot;: true,                        /* 결과 파일 내보낼지 여부 */
    // &quot;importHelpers&quot;: true,                 /* 'tslib'에서 헬퍼를 가져올 지 여부 */
    // &quot;downlevelIteration&quot;: true,            /* 타겟이 'ES5', 'ES3'일 때에도 'for-of', spread 그리고 destructuring 문법 모두 지원 */
    // &quot;isolatedModules&quot;: true,               /* 각 파일을 분리된 모듈로 트랜스파일 ('ts.transpileModule'과 비슷합니다). */

    /* 엄격한 타입-확인 옵션 */
    &quot;strict&quot;: true,                           /* 모든 엄격한 타입-체킹 옵션 활성화 여부 */
    // &quot;noImplicitAny&quot;: true,                 /* 'any' 타입으로 구현된 표현식 혹은 정의 에러처리 여부 */
    // &quot;strictNullChecks&quot;: true,              /* 엄격한 null 확인 여부 */
    // &quot;strictFunctionTypes&quot;: true,           /* 함수 타입에 대한 엄격한 확인 여부 */
    // &quot;strictBindCallApply&quot;: true,           /* 함수에 엄격한 'bind', 'call' 그리고 'apply' 메소드 사용 여부 */
    // &quot;strictPropertyInitialization&quot;: true,  /* 클래스의 값 초기화에 엄격한 확인 여부 */
    // &quot;noImplicitThis&quot;: true,                /* 'any' 타입으로 구현된 'this' 표현식 에러처리 여부 */
    // &quot;alwaysStrict&quot;: true,                  /* strict mode로 분석하고 모든 소스 파일에 &quot;use strict&quot;를 추가할 지 여부 */

    /* 추가적인 확인 */
    // &quot;noUnusedLocals&quot;: true,                /* 사용되지 않은 지역 변수에 대한 에러보고 여부 */
    // &quot;noUnusedParameters&quot;: true,            /* 사용되지 않은 파라미터에 대한 에러보고 여부 */
    // &quot;noImplicitReturns&quot;: true,             /* 함수에서 코드의 모든 경로가 값을 반환하지 않을 시 에러보고 여부 */
    // &quot;noFallthroughCasesInSwitch&quot;: true,    /* switch문에서 fallthrough 케이스에 대한 에러보고 여부 */

    /* 모듈 해석 옵션 */
    // &quot;moduleResolution&quot;: &quot;node&quot;,            /* 모듈 해석 방법 설정: 'node' (Node.js) 혹은 'classic' (TypeScript pre-1.6). */
    // &quot;baseUrl&quot;: &quot;./&quot;,                       /* non-absolute한 모듈 이름을 처리할 기준 디렉토리 */
    // &quot;paths&quot;: {},                           /* 'baseUrl'를 기준으로 불러올 모듈의 위치를 재지정하는 엔트리 시리즈 */
    // &quot;rootDirs&quot;: [],                        /* 결합된 컨텐츠가 런타임에서의 프로젝트 구조를 나타내는 루트 폴더들의 목록 */
    // &quot;typeRoots&quot;: [],                       /* 타입 정의를 포함할 폴더 목록, 설정 안 할 시 기본적으로 ./node_modules/@types로 설정 */
    // &quot;types&quot;: [],                           /* 컴파일중 포함될 타입 정의 파일 목록 */
    // &quot;allowSyntheticDefaultImports&quot;: true,  /* default export이 아닌 모듈에서도 default import가 가능하게 할 지 여부, 해당 설정은 코드 추출에 영향은 주지 않고, 타입확인에만 영향을 줍니다. */
    &quot;esModuleInterop&quot;: true,                  /* 모든 imports에 대한 namespace 생성을 통해 CommonJS와 ES Modules 간의 상호 운용성이 생기게할 지 여부,  'allowSyntheticDefaultImports'를 암시적으로 승인합니다. */
    // &quot;preserveSymlinks&quot;: true,              /* symlik의 실제 경로를 처리하지 않을 지 여부 */
    // &quot;allowUmdGlobalAccess&quot;: true,          /* UMD 전역을 모듈에서 접근할 수 있는 지 여부 */

    /* 소스 맵 옵션 */
    // &quot;sourceRoot&quot;: &quot;&quot;,                      /* 소스 위치 대신 디버거가 알아야 할 TypeScript 파일이 위치할 곳 */
    // &quot;mapRoot&quot;: &quot;&quot;,                         /* 생성된 위치 대신 디버거가 알아야 할 맵 파일이 위치할 곳 */
    // &quot;inlineSourceMap&quot;: true,               /* 분리된 파일을 가지고 있는 대신, 단일 파일을 소스 맵과 가지고 있을 지 여부 */
    // &quot;inlineSources&quot;: true,                 /* 소스맵과 나란히 소스를 단일 파일로 내보낼 지 여부, '--inlineSourceMap' 혹은 '--sourceMap'가 설정되어 있어야 한다. */

    /* 실험적 옵션 */
    // &quot;experimentalDecorators&quot;: true,        /* ES7의 decorators에 대한 실험적 지원 여부 */
    // &quot;emitDecoratorMetadata&quot;: true,         /* decorator를 위한 타입 메타데이터를 내보내는 것에 대한 실험적 지원 여부 */

    /* 추가적 옵션 */
    &quot;skipLibCheck&quot;: true,                     /* 정의 파일의 타입 확인을 건너 뛸 지 여부 */
    &quot;forceConsistentCasingInFileNames&quot;: true  /* 같은 파일에 대한 일관되지 않은 참조를 허용하지 않을 지 여부 */
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Web/TypeScript</category>
      <category>javascript</category>
      <category>ts</category>
      <category>tsconfig.json</category>
      <category>typescript</category>
      <category>web</category>
      <category>웹</category>
      <category>컴파일</category>
      <category>타입스크립트</category>
      <category>트랜스파일</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/214</guid>
      <comments>https://geonlee.tistory.com/214#entry214comment</comments>
      <pubDate>Thu, 28 May 2020 15:34:12 +0900</pubDate>
    </item>
    <item>
      <title>20.05.13</title>
      <link>https://geonlee.tistory.com/211</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;코로나 시국이 끝나길 기다리며&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZrse1/btqD8PzDG5Y/qCiKnjMIEb5qArAdKm1nh0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZrse1/btqD8PzDG5Y/qCiKnjMIEb5qArAdKm1nh0/img.jpg&quot; data-alt=&quot;우니파스타~ 우니 미쵸&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZrse1/btqD8PzDG5Y/qCiKnjMIEb5qArAdKm1nh0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZrse1%2FbtqD8PzDG5Y%2FqCiKnjMIEb5qArAdKm1nh0%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;우니파스타~ 우니 미쵸&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kVLj1/btqD8Ogp1u1/ZbN6OWUr7YFflDokKx0eo0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kVLj1/btqD8Ogp1u1/ZbN6OWUr7YFflDokKx0eo0/img.jpg&quot; data-alt=&quot;프리지아&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kVLj1/btqD8Ogp1u1/ZbN6OWUr7YFflDokKx0eo0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkVLj1%2FbtqD8Ogp1u1%2FZbN6OWUr7YFflDokKx0eo0%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프리지아&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcK29z/btqD8g5kDcp/E0ffmhCI8pxKX6Bbjvr0f0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcK29z/btqD8g5kDcp/E0ffmhCI8pxKX6Bbjvr0f0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcK29z/btqD8g5kDcp/E0ffmhCI8pxKX6Bbjvr0f0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcK29z%2FbtqD8g5kDcp%2FE0ffmhCI8pxKX6Bbjvr0f0%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brjpHi/btqD7G4taEr/VOdYdTgH2KJOjykesB4pr1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brjpHi/btqD7G4taEr/VOdYdTgH2KJOjykesB4pr1/img.jpg&quot; data-alt=&quot;살라다가 뺀찌먹은 컵, 귀여운데...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brjpHi/btqD7G4taEr/VOdYdTgH2KJOjykesB4pr1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrjpHi%2FbtqD7G4taEr%2FVOdYdTgH2KJOjykesB4pr1%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;살라다가 뺀찌먹은 컵, 귀여운데...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MHb5N/btqD8OtYNG9/7kSYryJH26B9pXPwemYLjK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MHb5N/btqD8OtYNG9/7kSYryJH26B9pXPwemYLjK/img.jpg&quot; data-alt=&quot;또오잉&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MHb5N/btqD8OtYNG9/7kSYryJH26B9pXPwemYLjK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMHb5N%2FbtqD8OtYNG9%2F7kSYryJH26B9pXPwemYLjK%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;또오잉&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkbfKK/btqD8OHwYoe/cazqI4zcO9p0CiV8FZynUk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkbfKK/btqD8OHwYoe/cazqI4zcO9p0CiV8FZynUk/img.jpg&quot; data-alt=&quot;취미를 위한 지름은 옳다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkbfKK/btqD8OHwYoe/cazqI4zcO9p0CiV8FZynUk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkbfKK%2FbtqD8OHwYoe%2FcazqI4zcO9p0CiV8FZynUk%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;취미를 위한 지름은 옳다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LywS4/btqD6myEFOX/Nbdt1NabSOPuTZpd7Bcj71/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LywS4/btqD6myEFOX/Nbdt1NabSOPuTZpd7Bcj71/img.jpg&quot; data-alt=&quot;인도커리는 언제 먹어도 맛나다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LywS4/btqD6myEFOX/Nbdt1NabSOPuTZpd7Bcj71/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLywS4%2FbtqD6myEFOX%2FNbdt1NabSOPuTZpd7Bcj71%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인도커리는 언제 먹어도 맛나다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deYmDd/btqD9tplJ25/BBNVXNm3aJzqW5df7YPnKK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deYmDd/btqD9tplJ25/BBNVXNm3aJzqW5df7YPnKK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deYmDd/btqD9tplJ25/BBNVXNm3aJzqW5df7YPnKK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeYmDd%2FbtqD9tplJ25%2FBBNVXNm3aJzqW5df7YPnKK%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;1591&quot; data-filename=&quot;blob&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JxlqW/btqD54dVQGV/YdSo005jxq193Pg3UofIm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JxlqW/btqD54dVQGV/YdSo005jxq193Pg3UofIm1/img.png&quot; data-alt=&quot;이제는 달고나 커피로 K-카페까지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JxlqW/btqD54dVQGV/YdSo005jxq193Pg3UofIm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJxlqW%2FbtqD54dVQGV%2FYdSo005jxq193Pg3UofIm1%2Fimg.png&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;1591&quot; data-filename=&quot;blob&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이제는 달고나 커피로 K-카페까지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d8rkKS/btqD6mrPrHC/kjqxxkumxAZjkKxBkG1590/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d8rkKS/btqD6mrPrHC/kjqxxkumxAZjkKxBkG1590/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d8rkKS/btqD6mrPrHC/kjqxxkumxAZjkKxBkG1590/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd8rkKS%2FbtqD6mrPrHC%2FkjqxxkumxAZjkKxBkG1590%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NJ4ab/btqD9ksGPfB/faIKLt0MyG8IRQDdYyCEl1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NJ4ab/btqD9ksGPfB/faIKLt0MyG8IRQDdYyCEl1/img.jpg&quot; data-alt=&quot;수제 난(이라고 쓰고 타버린 밀가루 덩어리라 읽는다)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NJ4ab/btqD9ksGPfB/faIKLt0MyG8IRQDdYyCEl1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNJ4ab%2FbtqD9ksGPfB%2FfaIKLt0MyG8IRQDdYyCEl1%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수제 난(이라고 쓰고 타버린 밀가루 덩어리라 읽는다)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qkWVh/btqD6Q7n260/fzWU7KK14qzk6nVhdiOL6k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qkWVh/btqD6Q7n260/fzWU7KK14qzk6nVhdiOL6k/img.jpg&quot; data-alt=&quot;청포도 샐러드인데 뭔가 더럽다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qkWVh/btqD6Q7n260/fzWU7KK14qzk6nVhdiOL6k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqkWVh%2FbtqD6Q7n260%2FfzWU7KK14qzk6nVhdiOL6k%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;청포도 샐러드인데 뭔가 더럽다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mawwJ/btqD9tCRJ0z/uTxfQTviLvxQFFlcRKo250/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mawwJ/btqD9tCRJ0z/uTxfQTviLvxQFFlcRKo250/img.jpg&quot; data-alt=&quot;또오오잉&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mawwJ/btqD9tCRJ0z/uTxfQTviLvxQFFlcRKo250/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmawwJ%2FbtqD9tCRJ0z%2FuTxfQTviLvxQFFlcRKo250%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;또오오잉&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cabCoe/btqD9N2bAFA/7RSBsTKnp4aHvSKZQdAgDk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cabCoe/btqD9N2bAFA/7RSBsTKnp4aHvSKZQdAgDk/img.jpg&quot; data-alt=&quot;또오오오잉&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cabCoe/btqD9N2bAFA/7RSBsTKnp4aHvSKZQdAgDk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcabCoe%2FbtqD9N2bAFA%2F7RSBsTKnp4aHvSKZQdAgDk%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;또오오오잉&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE4JUQ/btqD8PfkFJ2/iKxvg0kMADkhugro5lSZX1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE4JUQ/btqD8PfkFJ2/iKxvg0kMADkhugro5lSZX1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE4JUQ/btqD8PfkFJ2/iKxvg0kMADkhugro5lSZX1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE4JUQ%2FbtqD8PfkFJ2%2FiKxvg0kMADkhugro5lSZX1%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGbUph/btqD9uoeQb3/4zw8hp7k6EnDEQcoOR1XDk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGbUph/btqD9uoeQb3/4zw8hp7k6EnDEQcoOR1XDk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGbUph/btqD9uoeQb3/4zw8hp7k6EnDEQcoOR1XDk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGbUph%2FbtqD9uoeQb3%2F4zw8hp7k6EnDEQcoOR1XDk%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzuvcG/btqD7GKfFQ4/g0yE3dx2BGwW5Wu7yZPfD0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzuvcG/btqD7GKfFQ4/g0yE3dx2BGwW5Wu7yZPfD0/img.jpg&quot; data-alt=&quot;경성컨셉의 이자카야&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzuvcG/btqD7GKfFQ4/g0yE3dx2BGwW5Wu7yZPfD0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzuvcG%2FbtqD7GKfFQ4%2Fg0yE3dx2BGwW5Wu7yZPfD0%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;경성컨셉의 이자카야&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AwWk0/btqD8N2SUUg/q2975BRMyYA99iSVH9Zhd1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AwWk0/btqD8N2SUUg/q2975BRMyYA99iSVH9Zhd1/img.jpg&quot; data-alt=&quot;가지구이 존맛!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AwWk0/btqD8N2SUUg/q2975BRMyYA99iSVH9Zhd1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAwWk0%2FbtqD8N2SUUg%2Fq2975BRMyYA99iSVH9Zhd1%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;가지구이 존맛!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbLqYX/btqD8PTYe6Z/I9kDiFKQW5b0BMmbZKyklK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbLqYX/btqD8PTYe6Z/I9kDiFKQW5b0BMmbZKyklK/img.jpg&quot; data-alt=&quot;진짜 치사뽕!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbLqYX/btqD8PTYe6Z/I9kDiFKQW5b0BMmbZKyklK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbLqYX%2FbtqD8PTYe6Z%2FI9kDiFKQW5b0BMmbZKyklK%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;진짜 치사뽕!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3xv1c/btqD9tXbKVU/qcBK0gJYcVHeKcV2NpvNi1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3xv1c/btqD9tXbKVU/qcBK0gJYcVHeKcV2NpvNi1/img.jpg&quot; data-alt=&quot;이런 건 왜 묻는거에요?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3xv1c/btqD9tXbKVU/qcBK0gJYcVHeKcV2NpvNi1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3xv1c%2FbtqD9tXbKVU%2FqcBK0gJYcVHeKcV2NpvNi1%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이런 건 왜 묻는거에요?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MiY1f/btqD9jmXRKl/VVhyNZoAKL4YnKskam4E80/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MiY1f/btqD9jmXRKl/VVhyNZoAKL4YnKskam4E80/img.jpg&quot; data-alt=&quot;본 투 비 프론트 엔드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MiY1f/btqD9jmXRKl/VVhyNZoAKL4YnKskam4E80/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMiY1f%2FbtqD9jmXRKl%2FVVhyNZoAKL4YnKskam4E80%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;본 투 비 프론트 엔드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZWVTq/btqD9klRojG/X4ZtFZk8zBFaZeLBuFuhh0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZWVTq/btqD9klRojG/X4ZtFZk8zBFaZeLBuFuhh0/img.jpg&quot; data-alt=&quot;ㄴㅇㄱ 상상도 못한 정체&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZWVTq/btqD9klRojG/X4ZtFZk8zBFaZeLBuFuhh0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZWVTq%2FbtqD9klRojG%2FX4ZtFZk8zBFaZeLBuFuhh0%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ㄴㅇㄱ 상상도 못한 정체&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIaZDp/btqD9N2bAPZ/uZYhUXkLgtS42sEZziGP9K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIaZDp/btqD9N2bAPZ/uZYhUXkLgtS42sEZziGP9K/img.jpg&quot; data-alt=&quot;관짝중년단&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIaZDp/btqD9N2bAPZ/uZYhUXkLgtS42sEZziGP9K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIaZDp%2FbtqD9N2bAPZ%2FuZYhUXkLgtS42sEZziGP9K%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;관짝중년단&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;342&quot; data-filename=&quot;blob&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFK16a/btqD9M3gN0q/WurmfKVWKpDNpz1rNFH3k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFK16a/btqD9M3gN0q/WurmfKVWKpDNpz1rNFH3k1/img.png&quot; data-alt=&quot;은행이 나에게 불만있나&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFK16a/btqD9M3gN0q/WurmfKVWKpDNpz1rNFH3k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFK16a%2FbtqD9M3gN0q%2FWurmfKVWKpDNpz1rNFH3k1%2Fimg.png&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;342&quot; data-filename=&quot;blob&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;은행이 나에게 불만있나&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmNZvd/btqD6mFsjmd/KZ3JTKH2mdnsjGYGxxCTp0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmNZvd/btqD6mFsjmd/KZ3JTKH2mdnsjGYGxxCTp0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmNZvd/btqD6mFsjmd/KZ3JTKH2mdnsjGYGxxCTp0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmNZvd%2FbtqD6mFsjmd%2FKZ3JTKH2mdnsjGYGxxCTp0%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAPSxm/btqD7YDQq23/pRZkfQRw0DwaMv5HcZSNg1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAPSxm/btqD7YDQq23/pRZkfQRw0DwaMv5HcZSNg1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAPSxm/btqD7YDQq23/pRZkfQRw0DwaMv5HcZSNg1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAPSxm%2FbtqD7YDQq23%2FpRZkfQRw0DwaMv5HcZSNg1%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGecHr/btqD6RkNDbv/T6BGjRYBXq2eJY8G9KRx4K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGecHr/btqD6RkNDbv/T6BGjRYBXq2eJY8G9KRx4K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGecHr/btqD6RkNDbv/T6BGjRYBXq2eJY8G9KRx4K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGecHr%2FbtqD6RkNDbv%2FT6BGjRYBXq2eJY8G9KRx4K%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G5KVi/btqD6PHjIWA/mk65UzTKQuNOyxY7EsBYpK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G5KVi/btqD6PHjIWA/mk65UzTKQuNOyxY7EsBYpK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G5KVi/btqD6PHjIWA/mk65UzTKQuNOyxY7EsBYpK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG5KVi%2FbtqD6PHjIWA%2Fmk65UzTKQuNOyxY7EsBYpK%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bchK6X/btqD9u2Q6i3/F7i5xgFgEqRxk6V0hZ6tn0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bchK6X/btqD9u2Q6i3/F7i5xgFgEqRxk6V0hZ6tn0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bchK6X/btqD9u2Q6i3/F7i5xgFgEqRxk6V0hZ6tn0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbchK6X%2FbtqD9u2Q6i3%2FF7i5xgFgEqRxk6V0hZ6tn0%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zu1ED/btqD9ONzcpv/BuJyObZLKKKWgh1kb58ZkK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zu1ED/btqD9ONzcpv/BuJyObZLKKKWgh1kb58ZkK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zu1ED/btqD9ONzcpv/BuJyObZLKKKWgh1kb58ZkK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZu1ED%2FbtqD9ONzcpv%2FBuJyObZLKKKWgh1kb58ZkK%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/79rSn/btqD8OncK9v/bLBylfkuxIRYeCahPkwdmk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/79rSn/btqD8OncK9v/bLBylfkuxIRYeCahPkwdmk/img.jpg&quot; data-alt=&quot;올 더 싱굴레뒤~&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/79rSn/btqD8OncK9v/bLBylfkuxIRYeCahPkwdmk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F79rSn%2FbtqD8OncK9v%2FbLBylfkuxIRYeCahPkwdmk%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;올 더 싱굴레뒤~&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCeXLY/btqD9jmXRWE/yaTYEvJszUsF6SBKmotma0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCeXLY/btqD9jmXRWE/yaTYEvJszUsF6SBKmotma0/img.jpg&quot; data-alt=&quot;Z플립 뽐뿌온다..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCeXLY/btqD9jmXRWE/yaTYEvJszUsF6SBKmotma0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCeXLY%2FbtqD9jmXRWE%2FyaTYEvJszUsF6SBKmotma0%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Z플립 뽐뿌온다..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsFXZS/btqD6QGkJqF/sOWPVINRcTncQFjqukusq1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsFXZS/btqD6QGkJqF/sOWPVINRcTncQFjqukusq1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsFXZS/btqD6QGkJqF/sOWPVINRcTncQFjqukusq1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsFXZS%2FbtqD6QGkJqF%2FsOWPVINRcTncQFjqukusq1%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bicdlX/btqD9tv6uAT/ZzKRtxk6ucdez3XAv8yHvK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bicdlX/btqD9tv6uAT/ZzKRtxk6ucdez3XAv8yHvK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bicdlX/btqD9tv6uAT/ZzKRtxk6ucdez3XAv8yHvK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbicdlX%2FbtqD9tv6uAT%2FZzKRtxk6ucdez3XAv8yHvK%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock undefined&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oqoUr/btqD6RrDgCm/juunzlDG0UTZyeViz30wZk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oqoUr/btqD6RrDgCm/juunzlDG0UTZyeViz30wZk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oqoUr/btqD6RrDgCm/juunzlDG0UTZyeViz30wZk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoqoUr%2FbtqD6RrDgCm%2FjuunzlDG0UTZyeViz30wZk%2Fimg.jpg&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Life/年 月 日</category>
      <author>택시 운전사</author>
      <guid isPermaLink="true">https://geonlee.tistory.com/211</guid>
      <comments>https://geonlee.tistory.com/211#entry211comment</comments>
      <pubDate>Wed, 13 May 2020 21:13:23 +0900</pubDate>
    </item>
  </channel>
</rss>