<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>크롱의 Dev Log</title>
    <link>https://crong-dev.tistory.com/</link>
    <description>이것저것 해보고 싶은 크롱의 개발 블로그</description>
    <language>ko</language>
    <pubDate>Fri, 26 Jun 2026 22:27:57 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>개발하는 크롱</managingEditor>
    <image>
      <title>크롱의 Dev Log</title>
      <url>https://tistory1.daumcdn.net/tistory/4341443/attach/781b89c99f704f38adeeb5b5adfd2d16</url>
      <link>https://crong-dev.tistory.com</link>
    </image>
    <item>
      <title>그로스 해킹 (양승화) 리뷰</title>
      <link>https://crong-dev.tistory.com/74</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vGFZK/btrN5FOi9om/MAEQFbGWZiMtU4DAz5NAz1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vGFZK/btrN5FOi9om/MAEQFbGWZiMtU4DAz5NAz1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vGFZK/btrN5FOi9om/MAEQFbGWZiMtU4DAz5NAz1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvGFZK%2FbtrN5FOi9om%2FMAEQFbGWZiMtU4DAz5NAz1%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;800&quot; height=&quot;1080&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세 줄 리뷰&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그로스 해킹에 관심이 있고, 데이터를 활용해 서비스를 개선시키고자 하는 사람이라면 한 번쯤 읽어볼만한 책.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 그로스 해킹을 처음 도입하려는 사람한테 추천. 그로스해킹 전반적인 개념과, 활용 방법, 하기 좋은 실수가 모두 담겨 있는 책.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그로스 해킹 개념을 어느 정도 아는 상태에서 읽었는데 반은 아는 내용이긴 했지만, 그래도 배우고 얻을 게 있는 책.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책에서도 이야기하고 제품 만들면서도 생각하지만 &quot;무엇이 문제인지 아는 것&quot;, 지금 단계에서 어떤 문제가 가장 중요한지 아는 것이 제일 중요하다. 아니면 삽질 제대로 하고 스타트업은 망하기 좋다!&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;3.3.4) 코호트별 전환율 쪼개보기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 어떤 지표를 볼 때 코호트 분석이 굉장히 유의미한 경우가 많다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.4) 리텐션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리텐션,,,,,, 일반적으로 제품 처음 만들 때(초기 단계) 가장 신경써서 봐야 하는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.6) 추천&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이럴 계수라든지, 장치라든지 설명이 꽤 도움되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책 좋은 책이고 많은 도움 되었지만, 사실 이거 읽고 실제로 그로스 해킹을 제대로 이해가 가능하거나 바로 실전에 적용하기엔 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제에 적용하려면 더 많은 노력과 이해가 필요하다. (하지만 이 책을 시작으로 도전해보고 하면서 배우면 된다고 생각합니다.)&lt;/p&gt;</description>
      <category>책을 읽읍시다</category>
      <author>개발하는 크롱</author>
      <guid isPermaLink="true">https://crong-dev.tistory.com/74</guid>
      <comments>https://crong-dev.tistory.com/74#entry74comment</comments>
      <pubDate>Sun, 9 Oct 2022 17:55:56 +0900</pubDate>
    </item>
    <item>
      <title>프로덕트 오너 (김성한 저) 리뷰</title>
      <link>https://crong-dev.tistory.com/73</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2036&quot; data-origin-height=&quot;1278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caR2ci/btrMUTggzap/l48gdWfD0YAykDxncVKdK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caR2ci/btrMUTggzap/l48gdWfD0YAykDxncVKdK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caR2ci/btrMUTggzap/l48gdWfD0YAykDxncVKdK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaR2ci%2FbtrMUTggzap%2Fl48gdWfD0YAykDxncVKdK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2036&quot; height=&quot;1278&quot; data-origin-width=&quot;2036&quot; data-origin-height=&quot;1278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;랜선 사수로부터 일하는 방식 배우기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현업에 있는 업계 선배한테 업무 조언을 받은 기분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 일하는지, 어떻게 하면 좋은지 본인이 경험하고 생각한 걸 공유해주는 선배를 만난 기분.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마치 선배가 일하는 거 지켜보면서 나도 어떻게 일하면 좋을지 생각해 볼 수 있는 기회였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 쿠팡과 내 스타트업은 규모나 일하는 방식이 다를 수 밖에 없다. 그냥 큰 곳은 이렇게 일하는 구나 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기억하고 싶은 부분&lt;/h3&gt;
&lt;h1&gt;2. 고객의 목소리를 어디까지 반영할 것인가&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;(1) 고객은 제품을 사지 않는다, 고용한다&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  고객은 해결해야 할 일이 생길 때, 그것에 도움을 주는 제품을 &amp;lsquo;고용(구매)&amp;rsquo;한다.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 제품과 서비스를 고용의 대상으로 바라보면, 각각 어떤 일을 고객을 위해 해줘야 하는지 알 수 있다 &amp;rarr; 그것이 고객에게 전달되는 &lt;b&gt;실제 가치&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;식스 페이저 기법 활용&lt;/li&gt;
&lt;li&gt;&amp;ldquo;우리는 고객을 위해 어떤 일을 하는가?&amp;rdquo;가 중요&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;[적용]&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;고객의 해결해야 할 일은?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(따로 노션에 정리함)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;ldquo;내 서비스&amp;rdquo;가 고용되는 이유는? (무엇을 위해? / 내 서비스가 고객을 위해 어떤 일을 하는가? / 3~5가지)&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;h2 data-ke-size=&quot;size26&quot;&gt;(2) 서비스는 하나라도 사용자 유형은 다양하다&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수많은 고객의 통일된 의견을 파악하려면, 먼저 고객의 관점에서 바라봐야 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;아마존 사례 참고하기! &amp;rarr; 고객이 가진 본질적 의도를 분석하고 유형별로 서비스에서 제공하는 것 달라짐&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;(3) 모든 사람을 만족시킬 수는 없다&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CS 들어왔을 때, 몇 명의 고객이 불편을 토로했는지, 불편을 토로한 고객이 어떤 고객인지 먼저 확인&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;설문조사나 들어온 피드백, CS 빈도 파악하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(따로 노션에 정리함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;(4) 식스 페이저로 모두의 동의를 얻어 기록하라&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;식스 페이저 문서 작성시, 고객 분류와 더불어 공을 제일 많이 들이는 부분 &amp;rarr; 가이드 원칙
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;4~6개의 원칙 목록화&lt;/li&gt;
&lt;li&gt;해당 프로덕트 개발 및 운영 시 꼭 지켜야 하는 법&lt;/li&gt;
&lt;li&gt;1번이 가장 중요한 원칙, 중요한 순으로 작성&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;(5) 고객의 요청과 회사가 정한 목표가 충돌한다면&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회사 목표가 무엇인지 질문 받았을 때, 모든 직원과 대표가 같은 답을 해야 함.&lt;/li&gt;
&lt;li&gt;프로덕트가 회사 전체 내에서 어떤 역할을 맡고 있는지가 중요
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회사 전체에 대한 유기적인 고려 없이 독단적으로 고객을 위한다는 취지로 방향성을 잡는 건 옳지 않다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;+) 페르소나와 실제 고객을 혼동하지 마라&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고객이 누구인지 파악할 때는, 데이터나 사용 패턴 등을 감안하여 최대한 포괄적으로 접근해야 함&lt;/p&gt;
&lt;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;&amp;rarr; 프로덕트를 사용해서 어떤 가치를 얻으려고 하는지 이해한다면, 역으로 그 가치를 각각 추구하는 사용자들을 하나씩 그룹화할 수 있다. 더 이상 통합할 수 없는 단계까지 도달하면, 그게 PO가 고려해야 하는 고객의 목록이 된다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 데이터 속에서 진실을 찾는 법&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;ldquo;고객 행동을 트래킹할 수 있도록 로그를 다 심어두죠&amp;rdquo; = PO가 어떤 데이터가 중요한지 모른다는 뜻&lt;/li&gt;
&lt;li&gt;주별로 WBR 공유하자! &amp;rarr; 어디에 집중해야 하는지 파악 가능 / not only 현재 상태 파악, but also 어디에 집중해야 할지 그리고 해결책 도출 도움
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주요 요점&lt;/li&gt;
&lt;li&gt;프로덕트 목표&lt;/li&gt;
&lt;li&gt;주요 지표&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;(3) 행동을 부르지 않는 데이터는 버린다&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 데이터 분석의 결과는 &amp;ldquo;액셔너블&amp;rdquo; 해야 함&lt;/li&gt;
&lt;li&gt;도움이 되는 데이터 분석은, 데이터를 통해 얻은 인사이트를 토대로 그다음 어떤 행동으로 옮겨야 하는지 &lt;b&gt;명확하게 제시해야 함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;액셔너블하지 않은 데이터 &amp;rarr; 어떤 노력으로도 효과적으로 고칠 수 없는 이슈. 섣불리 액션 취하면 안됨. 과감히 무시!
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예) 시즌이나 외부 이벤트 등에 의해 일시적으로 영향받은 데이터&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;액셔너블한 데이터&lt;/li&gt;
&lt;li&gt;이미 액션을 취해 대응하고 있는 데이터 &amp;rarr; 참조만.&lt;/li&gt;
&lt;li&gt;데이터 볼 때 &lt;b&gt;&amp;ldquo;그래서 뭐?&amp;rdquo; &amp;rarr; 당장 액션을 취할 수 없는 문제라면 데이터를 무시하는 결단.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 디자이너를 최고의 파트너로 삼는 법&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UI/UX 전문가는 디자이너. PO는 디자인 관련 의견내지 말 것.&lt;/li&gt;
&lt;li&gt;객관적인 요구사항만 전달하기(의견 X) &amp;rarr; PO가 의견 강조하면 고객을 위한 디자인이 아니라 PO를 위한 디자인 함&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>책을 읽읍시다</category>
      <author>개발하는 크롱</author>
      <guid isPermaLink="true">https://crong-dev.tistory.com/73</guid>
      <comments>https://crong-dev.tistory.com/73#entry73comment</comments>
      <pubDate>Sun, 25 Sep 2022 17:52:41 +0900</pubDate>
    </item>
    <item>
      <title>인스파이어드 리뷰</title>
      <link>https://crong-dev.tistory.com/72</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;마티 케이건의 인스파이어드 리뷰&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ak74M/btrLLzRgozc/JDsNO7KVwZwKr0US9tnQ90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ak74M/btrLLzRgozc/JDsNO7KVwZwKr0US9tnQ90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ak74M/btrLLzRgozc/JDsNO7KVwZwKr0US9tnQ90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAk74M%2FbtrLLzRgozc%2FJDsNO7KVwZwKr0US9tnQ90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;1156&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;533&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;h2 data-ke-size=&quot;size26&quot;&gt;IT 제품을 만드는 모든 사람에게 추천할 만한 책&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;인스파이어드 책 자체를 사실 반년 전부터 읽기 시작했는데 이제서야 다 읽었다. (역시 강제성이 없으면 안 읽게 돼)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;읽는데 오래 걸렸던 이유는 책이 두껍다거나 어려워서는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;읽으면서 계속 멈추고 내 상황에 대입해서 판단, 분석, 고민을 하다보니 오래 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다 너무 어려운 고민들이라 쉽게 끝나지 않았고, 계속 읽다가 멈추고 고민하다보니 시간이 더 오래 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;엄청 공감하면서 읽었던 부분도 있고, 이 책을 통해 새롭게 알게 된 지식들도 꽤 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;놀라웠던건 내가 갖고 있는 고민에 대한 언급과 해결법이 읽다가 나오는 경험을 종종 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;스타트업을 경영하면서 놓치고 있던 부분들에 유의미한 질문을 많이 던져주는 책이라 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그리고 실용서 같은 책으로 그냥 비전이나 방향만 던져주는게 아니라 실천 가능한 방법과 사례까지 제시해줘 여러모로 도움이 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그러나 책의 절반 이상은 대충 넘기면서 읽었다. (내 상황에 맞지 않는 더 큰 기업에 적용되는 이야기거나 현실성 떨어지는 이상적인 내용은 그냥 그렇구나.....하면서 넘김)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아무튼 IT 제품을 만들고 기업을 운영하는 역할에 있는 사람이라면 한번쯤 읽어볼만한 책인듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;읽으면서 다시 보고 싶은 부분들에 표시를 하면서 읽는 편인데, 꽤 많이 표시한 책이라 사실 한번이 아니고 여러번 읽어도 좋은 책이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;좋았던 / 혹은 나중에도 보고 싶은 부분 중 일부&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 제품이 성공하면 팀의 모든 사람이 제 역할을 잘 해냈기 때문이다. 하지만 제품이 실패하면 그것은 제품 관리자의 잘못이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 성공적인 제품 관리자는 최고로 똑똑하고 창의적이고 집요해야 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[고객이 상호작용하는 여정을 찾는데 도움이 되는 질문들]&lt;br /&gt;&lt;br /&gt;- 고객이 그 제품을 어떻게 처음 알게 되는가?&lt;br /&gt;- 처음 방문한 사용자를 어떻게 안착시키고, 새로운 기능을 소개할 것인가?&lt;br /&gt;- 고객의 하루 일과에 따라 제품과 어떻게 상호작용이 발생하는가?&lt;br /&gt;- 사용자의 관심을 두고 어떤 것들과 경쟁을 하고 있는가?&lt;br /&gt;- 한 달이 된 고객과 1면이 된 고객은 어떤 차이점이 있을까?&lt;br /&gt;- 제품에 대해 더 높은 애착을 가지게 하기 위해서 어떻게 사용자에게 동기부여할 수 있을까?&lt;br /&gt;-&amp;nbsp; 어떻게 희열을 느끼는 순간을 만들 수 있을까?&lt;br /&gt;- 사용자가 다른 사람에게 어떻게 경험을 공유할 수 있을까?&lt;br /&gt;- 제품의 반응성을 어떻게 느끼고 있는가?&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[제품 비전의 원칙]&lt;br /&gt;- 왜에서 시작하라.&lt;br /&gt;- 솔루션이 아니라 문제와 사랑에 빠져라.&lt;br /&gt;- 비전을 크게 생각하는 것에 두려워하지 마라.&lt;br /&gt;- 현재의 자신을 파괴하는 데 두려워하지 마라.&lt;br /&gt;- 제품 비전은 영감을 불어넣는다.&lt;br /&gt;- 적절하고 유의미한 트렌드를 선택하고 포함하라.&lt;br /&gt;- 공이 있던 곳이 아니라 공이 향하는 곳으로 움직여라.&lt;br /&gt;- 비전은 완고하게 하되 세세한 부분은 유연하게 하라.&lt;br /&gt;- 모든 제품 비전은 믿음이라는 것을 깨달아라.&lt;br /&gt;- 계속, 집요하게 비전을 전파하라.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 한 번에 한 가지 시장 혹은 고객에 집중하라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마지막으로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 더 똑똑하고, 더 능력있고, 더 열정적인 사람이 되어야만 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 최근 제품 비전에 대한 고민을 많이 하는데 이 책이 꽤 도움이 되었다. (그러나 여전히 고민 중)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 꿈을 아주 잘 팔아야 하는데 쉽지 않은 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 팀이 일하는 방식에 대해 이런저런 방법론을 많이 접했으나, 그래서 우리 같은 팀은 어떻게 하는 것이 최선일지 고민이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 목표 일정과, 결과물이 아닌 성과를 제대로 이루면서 팀원의 만족도까지 잡을 수 있는 프로세스를 잡는게 가능할지는 모르겠으나 일단 조금씩 새로운 방법 적용하면서 제일 잘 맞는 방법을 찾아봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 목표만 알려주고 방법은 자율적으로 할 수 있도록 하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 우선순위 변경되는 일 최소화하고, 개인적인 우선순위와 조직 전체의 우선순위가 충돌이 없도록 처리하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>책을 읽읍시다</category>
      <author>개발하는 크롱</author>
      <guid isPermaLink="true">https://crong-dev.tistory.com/72</guid>
      <comments>https://crong-dev.tistory.com/72#entry72comment</comments>
      <pubDate>Sun, 11 Sep 2022 17:55:38 +0900</pubDate>
    </item>
    <item>
      <title>비대면 체육수업 지원 서비스 개발 회고 || tfjs posenet + react 사용법</title>
      <link>https://crong-dev.tistory.com/71</link>
      <description>&lt;h3 id=&quot;01&quot; data-ke-size=&quot;size23&quot;&gt;프로젝트 진행 동기&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&quot;중고등학생 동생이 비대면 수업을 받는 것을 보며 자연스럽게 문제의 심각성을 깨닫고, 해결해야겠다는 결심이 프로젝트 진행으로 이어졌습니다.&quot; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코로나19 바이러스로 인해 학생들이 학교에 가지 못하고 집에서 비대면 수업을 받게 되었습니다. 중고등학생 동생이 집에서 수업을 받는 모습을 자연스럽게 지켜보게 되었습니다. 분명히 수업을 하고 있을 시간인데 동생이 침대에 누워서 자는 것을 보고 의아하게 생각했습니다. 저는 자고 있는 동생을 깨워서 수업시간에 왜 잠을 자느냐고 혼을 냈습니다. 그러자 동생은 체육 시간이라며 아무 것도 하지 않고 영상만 틀어준다며 자도 된다고 했습니다. 그때 저는 굉장한 충격을 받았습니다. 비대면 체육 수업이 제대로 이루어지고 있지 않는 문제의 심각성을 인식했고, 선생님 입장에서도 비대면 으로 체육 수업을 진행하기가 어렵다는 사실을 깨달았습니다. 코로나로 인해 비대면 원격 수업이 증가하면서 체육 수업은 신체활동보다는 이론 영상들로 대체되었으며, 실제 대면 수업을 진행하더라도 호흡기계 감염병인 코로나19의 확산 방지를 위해 숨이 차며 비말이 많이 발생되는 활동 수업들은 진행에 어려움을 겪고 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&quot;코로나19로 인해 가속화된 온라인/비대면 수업, 그리고 비대면 수업에 준비되지 않은 체육 교과&quot; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코로나19로 인해 우리의 생활 모습이 크게 변화했습니다. &lt;/span&gt;신종 코로나바이러스 감염증(코로나19) 여파로 시장이 온라인 중심으로 급속하게 개편되고, 기술이 비약적으로 발전하면서 앞으로 재택근무 와 온라인 수업이 일상화되는 시대가 도래할 것이라고 전문가들이 예상하고 있습니다. 다른 교과의 경우, 온라인 수업에 대한 대비가 비교적 잘 이루어져 있습니다. 국어, 수학, 영어 같은 교과는 인터넷 강의를 통한 학습이 자연스럽습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 온라인 교육 시장이 크게 성장했습니다. 하지만 체육 교과의 경우, 성장하는 온라인 시장 에 발맞춰 제대로 준비하고 있지 못합니다. &lt;span&gt;실제로 설문조사 결과들을 보면, 예체능 과목들은 코로나 이후, 만족도가 가장 낮은 과목이자 보충수업이 가장 필요한 과목입니다. &lt;/span&gt;체육 교과는 초중등 교육 과정에서 체력 증진, 인성 함양 등 다양한 방면에서 중요한 역할을 수행합니다. 온라인 교육 시 장의 성장세에 발맞춰 체육 교과 역시 온라인 중심 교육이 원활히 가능하도록 준비된 상태가 되어 야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 초중등 교육과정에서 체육 수업이 원활히 이루어지지 못하는 문제의 심각성에 공감하고, 앞서나가 체육 수업의 온라인화를 지원할 수 있는 서비스의 필요성에 공감하는 팀원들과 이 문제를 해결하기 위해 본 서비스 기획 및 개발을 시작하게 되었습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;02&quot; data-ke-size=&quot;size23&quot;&gt;프로젝트 요약&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AI 비대면 체육 수업 지원 서비스&lt;/span&gt;&lt;span style=&quot;color: #272938; font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvKEiZ/btrkWOem03X/oUzk6vMgc0yfnNTiQIxYt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvKEiZ/btrkWOem03X/oUzk6vMgc0yfnNTiQIxYt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvKEiZ/btrkWOem03X/oUzk6vMgc0yfnNTiQIxYt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvKEiZ%2FbtrkWOem03X%2FoUzk6vMgc0yfnNTiQIxYt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1424&quot; height=&quot;760&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;760&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 선생님은 학생이 수행할 운동을 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 학생은 운동 목록에서 선생님이 지시한 운동을 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 운동을 하고 인공지능을 통해 자세 피드백을 받습니다. 시스템이 학생의 운동 횟수와 정확도 등을 측정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 학생의 운동 수행 결과가 서버로 전송되어 선생님의 학생 관리 화면에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;03&quot; 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;TensorflowJs의 PoseNet 라이브러리를 사용해서 자세의 키포인트 추출을 하고 동작을 검증할 수 있도록 연결하는 역할을 수행했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;05&quot; data-ke-size=&quot;size23&quot;&gt;tfjs posenet 동작 원리&lt;/h3&gt;
&lt;div style=&quot;height: auto !important;&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;자바스크립트로 ML 모델을 개발하고 브라우저 혹은 Node.js에서 실행할 수 있는 TensorFlow.js을 활용하여 실시간으로 사람의 자세를 추정하는 human pose estimation을 구현할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;PoseNet&lt;br /&gt;&amp;nbsp; - PoseNet은 MobileNet 혹은 ResNet 기반의 human pose estimation 네트워크이다.&lt;br /&gt;&amp;nbsp; - TensorFlow.js에서 실행할 수 있기 때문에&amp;nbsp; &amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;a) 웹캠 혹은 핸드폰 카메라 기반으로 어플리케이션 실행이 가능하다&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;b) 자바 스크립트로 코딩할 수 있다&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;c) 브라우저 상에서 실행되고, 데이터가 남지 않기 때문에 개인정보 이슈에서 비교적 자유롭다.&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;저희 서비스에서는 텐서플로우js의 포즈넷을 활용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 여러 포즈 측정 라이브러리 중 텐서플로우의 포즈넷을 활용한 이유는 별도 설정없이 일반적인 기기의 웹 브라우저에선 누구나 접근 가능하다는 사실 때문이었습니다.&amp;nbsp; 많은 포즈 측정&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;시스템이 오픈 소스로 제공 되었지만 모두 특수한 하드웨어 또는 카메라와 상당한 시스템 설정이 필요합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;TensorFlow.js에서&lt;/b&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;실행되는 PoseNet을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;사용하면 괜찮은 웹캠이 장착된 데스크톱 또는 전화를 가진 사람이라면 누구나 웹 브라우저 내에서&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용 가능합니다&lt;/span&gt;&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Pose Estimation(포즈 측정, 동작 감지)은 &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&amp;nbsp;이미지 혹은 영상으로부터 사람 형상을 찾아내고, 주요 관절의 위치 등을 찾아내는 컴퓨터 비전 기술을 의미합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1388&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dP3ZLY/btrkSNmyyW4/bkEEkLQPdgL3kS66gYvNc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dP3ZLY/btrkSNmyyW4/bkEEkLQPdgL3kS66gYvNc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dP3ZLY/btrkSNmyyW4/bkEEkLQPdgL3kS66gYvNc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdP3ZLY%2FbtrkSNmyyW4%2FbkEEkLQPdgL3kS66gYvNc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1388&quot; height=&quot;650&quot; data-origin-width=&quot;1388&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;PoseNet에서는 RGB 이미지를 인풋으로 받아 CNN 아키텍처를 거쳐 다음의 아웃풋을 반환합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&amp;nbsp; ●&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;pose&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(pose object) : keypoint의 리스트 &amp;amp; 인스턴스 레벨의 스코어&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&amp;nbsp; ●&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;pose confidence score&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: pose에 대한 전반적인 점수 (0-1 사이의 값)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;rarr; 불확실하게 추정한 포즈는 사용하지 않도록 처리 가능&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&amp;nbsp; ●&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;keypoint&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: pose를 구성하는 17개의 핵심 관절들. position과 confidence score을 포함&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; ●&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;keypoint confidence score&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 추정한 keypoint의 위치가 정확한지에 대한 점수. 신뢰도 (0-1 사이의 값)&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; ●&amp;nbsp;&lt;b&gt;keypoint position&lt;/b&gt;&amp;nbsp;: 인풋 이미지 스케일에서 keypoint에 대한 (x,y) 좌표&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwhys8/btrkIQFcNVT/clGRS8jWb5p4YuFcyJuu11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwhys8/btrkIQFcNVT/clGRS8jWb5p4YuFcyJuu11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwhys8/btrkIQFcNVT/clGRS8jWb5p4YuFcyJuu11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwhys8%2FbtrkIQFcNVT%2FclGRS8jWb5p4YuFcyJuu11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;767&quot; height=&quot;510&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포즈넷 모델에 이미지를 입력으로 넣어 출력되는 결과 예시입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력값으로는 관절 키포인트 좌표와 각 키포인트의 &lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;pose confidence score를 출력합니다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;아래는 출력값의 예시입니다. 저희 서비스에서는 포즈넷 출력값인 각 키포인트들의 좌표를 활용해 운동 동작을 제대로 하는지 판단하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1636910975183&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;score&quot;: 0.32371445304906,
  &quot;keypoints&quot;: [
    { // nose
      &quot;position&quot;: {
        &quot;x&quot;: 301.42237830162,
        &quot;y&quot;: 177.69162777066
      },
      &quot;score&quot;: 0.99799561500549
    },
    { // left eye
      &quot;position&quot;: {
        &quot;x&quot;: 326.05302262306,
        &quot;y&quot;: 122.9596464932
      },
      &quot;score&quot;: 0.99766051769257
    },
    { // right eye
      &quot;position&quot;: {
        &quot;x&quot;: 258.72196650505,
        &quot;y&quot;: 127.51624706388
      },
      &quot;score&quot;: 0.99926537275314
    },
    ...
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;04&quot; data-ke-size=&quot;size23&quot;&gt;React.js / Next.js 에서 tfjs posenet 활용법 및 웹캠 연결법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 라이브러리를 설치합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1636910541070&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install @tensorflow-models/posenet&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 모듈을 불러옵니다.&lt;/p&gt;
&lt;pre id=&quot;code_1636910559639&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import * as posenet from '@tensorflow-models/posenet';
const net = await posenet.load();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 리액트에서 활용하는 방법을 코드와 함께 설명드리겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1636908427660&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useRef, useCallback } from &quot;react&quot;;
import styled from &quot;styled-components&quot;;
import * as posenet from &quot;@tensorflow-models/posenet&quot;;
import Webcam from &quot;react-webcam&quot;;

import { drawKeypoints, drawSkeleton } from &quot;src/utils/draw&quot;;
import estimatePose from &quot;src/utils/estimate-pose&quot;;

export default function ExerciseScreen() {
  // action: 동작명. 아래 estimatePose 인자를 동작명으로 변경해서 테스트
  const [count, step, checkPoses] = estimatePose(&quot;hajung&quot;);
  const checkPose = useCallback((pose) =&amp;gt; checkPoses(pose), [checkPoses]);

  const webcamRef = useRef(null);
  const canvasRef = useRef(null);

  const videoWidth = 640;
  const videoHeight = 480;

  const drawResult = (pose, video, videoWidth, videoHeight, canvas) =&amp;gt; {
    const ctx = canvas.current.getContext(&quot;2d&quot;);
    canvas.current.width = videoWidth;
    canvas.current.height = videoHeight;
    drawKeypoints(pose[&quot;keypoints&quot;], 0.6, ctx);
    drawSkeleton(pose[&quot;keypoints&quot;], 0.7, ctx);
  };

  async function detectWebcamFeed(posenetModel) {
    if (
      typeof webcamRef.current !== &quot;undefined&quot; &amp;amp;&amp;amp;
      webcamRef.current !== null &amp;amp;&amp;amp;
      webcamRef.current.video.readyState === 4
    ) {
      // Get Video Properties
      const video = webcamRef.current.video;
      const videoWidth = webcamRef.current.video.videoWidth;
      const videoHeight = webcamRef.current.video.videoHeight;
      // Set video width
      webcamRef.current.video.width = videoWidth;
      webcamRef.current.video.height = videoHeight;
      // Make Estimation
      const pose = await posenetModel.estimateSinglePose(video);
      // Pose Estimation
      checkPose(pose);
      // Draw Result
      drawResult(pose, video, videoWidth, videoHeight, canvasRef);
    }
  }

  const runPosenet = async () =&amp;gt; {
    const posenetModel = await posenet.load({
      inputResolution: { width: videoWidth, height: videoHeight },
      scale: 0.8,
    });

    setInterval(() =&amp;gt; {
      detectWebcamFeed(posenetModel);
    }, 100);
  };

  runPosenet();

  return (
    &amp;lt;&amp;gt;
      &amp;lt;Wrapper&amp;gt;
        &amp;lt;Webcam
          ref={webcamRef}
          style={{
            width: videoWidth,
            height: videoHeight,
            position: &quot;absolute&quot;,
            top: 0,
            left: 0,
          }}
        /&amp;gt;
        &amp;lt;StyledCanvas
          ref={canvasRef}
          style={{
            width: videoWidth,
            height: videoHeight,
          }}
        /&amp;gt;
      &amp;lt;/Wrapper&amp;gt;
      &amp;lt;p&amp;gt;
        step:{step} / count: {count}
      &amp;lt;/p&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

const Wrapper = styled.div`
  width: 640px;
  height: 480px;
  position: relative;
`;

const StyledCanvas = styled.canvas`
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
`;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 웹캠을 연결하고, 웹캠의 입력을 받아와서 tfjs posenet 모델에 입력으로 넣어 스켈레톤을 추출하고 동작 감지 알고리즘에서 감지하도록 하는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-webcam 라이브러리를 써서 우선 웹캠 컴포넌트를 연결합니다. useRef 훅을 사용해서 위에서 선언한 ref를 연결해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1636908792880&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; &amp;lt;Webcam
          ref={webcamRef}
          style={{
            width: videoWidth,
            height: videoHeight,
            position: &quot;absolute&quot;,
            top: 0,
            left: 0,
          }}
        /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트가 렌더링되면서 runPosenet함수가 실행되게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1636908873033&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const runPosenet = async () =&amp;gt; {
    const posenetModel = await posenet.load({
      inputResolution: { width: videoWidth, height: videoHeight },
      scale: 0.8,
    });

    setInterval(() =&amp;gt; {
      detectWebcamFeed(posenetModel);
    }, 100);
  };

  runPosenet();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const posenetModel은 포즈넷 라이브러리를 불러오고 설정하는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 setInterval을 통해 0.1초 간격으로 호출되는 detectWebcamFeed함수를 알아봅시다.&lt;/p&gt;
&lt;pre id=&quot;code_1636909591164&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  async function detectWebcamFeed(posenetModel) {
    if (
      typeof webcamRef.current !== &quot;undefined&quot; &amp;amp;&amp;amp;
      webcamRef.current !== null &amp;amp;&amp;amp;
      webcamRef.current.video.readyState === 4
    ) {
      // Get Video Properties
      const video = webcamRef.current.video;
      const videoWidth = webcamRef.current.video.videoWidth;
      const videoHeight = webcamRef.current.video.videoHeight;
      // Set video width
      webcamRef.current.video.width = videoWidth;
      webcamRef.current.video.height = videoHeight;
      // Make Estimation
      const pose = await posenetModel.estimateSinglePose(video);
      // Pose Estimation
      checkPose(pose);
      // Draw Result
      drawResult(pose, video, videoWidth, videoHeight, canvasRef);
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹캠의 비디오프레임을 포즈넷 모델의 인자로 넣어 포즈 측정을 한 값을 pose 변수에 받아옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pose를 checkPose의 인자로 넣어 호출합니다. 현재 측정한 포즈들을 계속 입력으로 넣어 정확한 동작으로 운동을 수행하고 있는지 판단하는 checkPose를 호출합니다. checkPose에서 포즈넷에서 감지한 동작들을 가지고 제대로 운동을 수행하는지 판단하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 drawResult함수를 호출해 웹캠 위에 자세 포인트들을 그립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;detectWebcamFeed함수에서 사용하는 drawResult 함수 설명입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추출한 키포인트를 화면에 그리는 함수입니다. 웹캠 화면 위에 점과 점을 연결한 스켈레톤 모양이 뜨게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마치 아래 예시처럼요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;224&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qzIhL/btrk1Qirm7o/IfGC9X3cTUW2rJMsKx2g10/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qzIhL/btrk1Qirm7o/IfGC9X3cTUW2rJMsKx2g10/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qzIhL/btrk1Qirm7o/IfGC9X3cTUW2rJMsKx2g10/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqzIhL%2Fbtrk1Qirm7o%2FIfGC9X3cTUW2rJMsKx2g10%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;224&quot; height=&quot;225&quot; data-origin-width=&quot;224&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1636909186905&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const drawResult = (pose, video, videoWidth, videoHeight, canvas) =&amp;gt; {
    const ctx = canvas.current.getContext(&quot;2d&quot;);
    canvas.current.width = videoWidth;
    canvas.current.height = videoHeight;
    drawKeypoints(pose[&quot;keypoints&quot;], 0.6, ctx);
    drawSkeleton(pose[&quot;keypoints&quot;], 0.7, ctx);
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 drawKeypoints와 drawSkeleton은 따로 utils폴더 아래에 분리해두었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;utils/draw 파일의 코드에 정의되어 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1636909367077&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/utils/draw

/**
 * @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */
import &quot;@tensorflow/tfjs-backend-webgl&quot;;
import * as posenet from &quot;@tensorflow-models/posenet&quot;;
import * as tf from &quot;@tensorflow/tfjs-core&quot;;

const color = &quot;aqua&quot;;
const boundingBoxColor = &quot;red&quot;;
const lineWidth = 2;

export const tryResNetButtonName = &quot;tryResNetButton&quot;;
export const tryResNetButtonText = &quot;[New] Try ResNet50&quot;;
const tryResNetButtonTextCss = &quot;width:100%;text-decoration:underline;&quot;;
const tryResNetButtonBackgroundCss = &quot;background:#e61d5f;&quot;;

function isAndroid() {
  return /Android/i.test(navigator.userAgent);
}

function isiOS() {
  return /iPhone|iPad|iPod/i.test(navigator.userAgent);
}

export function isMobile() {
  return isAndroid() || isiOS();
}

function setDatGuiPropertyCss(propertyText, liCssString, spanCssString = &quot;&quot;) {
  const spans = document.getElementsByClassName(&quot;property-name&quot;);
  for (let i = 0; i &amp;lt; spans.length; i++) {
    const text = spans[i].textContent || spans[i].innerText;
    if (text == propertyText) {
      spans[i].parentNode.parentNode.style = liCssString;
      if (spanCssString !== &quot;&quot;) {
        spans[i].style = spanCssString;
      }
    }
  }
}

export function updateTryResNetButtonDatGuiCss() {
  setDatGuiPropertyCss(
    tryResNetButtonText,
    tryResNetButtonBackgroundCss,
    tryResNetButtonTextCss
  );
}

/**
 * Toggles between the loading UI and the main canvas UI.
 */
export function toggleLoadingUI(
  showLoadingUI,
  loadingDivId = &quot;loading&quot;,
  mainDivId = &quot;main&quot;
) {
  if (showLoadingUI) {
    document.getElementById(loadingDivId).style.display = &quot;block&quot;;
    document.getElementById(mainDivId).style.display = &quot;none&quot;;
  } else {
    document.getElementById(loadingDivId).style.display = &quot;none&quot;;
    document.getElementById(mainDivId).style.display = &quot;block&quot;;
  }
}

function toTuple({ y, x }) {
  return [y, x];
}

export function drawPoint(ctx, y, x, r, color) {
  ctx.beginPath();
  ctx.arc(x, y, r, 0, 2 * Math.PI);
  ctx.fillStyle = color;
  ctx.fill();
}

/**
 * Draws a line on a canvas, i.e. a joint
 */
export function drawSegment([ay, ax], [by, bx], color, scale, ctx) {
  ctx.beginPath();
  ctx.moveTo(ax * scale, ay * scale);
  ctx.lineTo(bx * scale, by * scale);
  ctx.lineWidth = lineWidth;
  ctx.strokeStyle = color;
  ctx.stroke();
}

/**
 * Draws a pose skeleton by looking up all adjacent keypoints/joints
 */
export function drawSkeleton(keypoints, minConfidence, ctx, scale = 1) {
  const adjacentKeyPoints = posenet.getAdjacentKeyPoints(
    keypoints,
    minConfidence
  );

  adjacentKeyPoints.forEach((keypoints) =&amp;gt; {
    drawSegment(
      toTuple(keypoints[0].position),
      toTuple(keypoints[1].position),
      color,
      scale,
      ctx
    );
  });
}

/**
 * Draw pose keypoints onto a canvas
 */
export function drawKeypoints(keypoints, minConfidence, ctx, scale = 1) {
  for (let i = 0; i &amp;lt; keypoints.length; i++) {
    const keypoint = keypoints[i];

    if (keypoint.score &amp;lt; minConfidence) {
      continue;
    }

    const { y, x } = keypoint.position;
    drawPoint(ctx, y * scale, x * scale, 3, color);
  }
}

/**
 * Draw the bounding box of a pose. For example, for a whole person standing
 * in an image, the bounding box will begin at the nose and extend to one of
 * ankles
 */
export function drawBoundingBox(keypoints, ctx) {
  const boundingBox = posenet.getBoundingBox(keypoints);

  ctx.rect(
    boundingBox.minX,
    boundingBox.minY,
    boundingBox.maxX - boundingBox.minX,
    boundingBox.maxY - boundingBox.minY
  );

  ctx.strokeStyle = boundingBoxColor;
  ctx.stroke();
}

/**
 * Converts an arary of pixel data into an ImageData object
 */
export async function renderToCanvas(a, ctx) {
  const [height, width] = a.shape;
  const imageData = new ImageData(width, height);

  const data = await a.data();

  for (let i = 0; i &amp;lt; height * width; ++i) {
    const j = i * 4;
    const k = i * 3;

    imageData.data[j + 0] = data[k + 0];
    imageData.data[j + 1] = data[k + 1];
    imageData.data[j + 2] = data[k + 2];
    imageData.data[j + 3] = 255;
  }

  ctx.putImageData(imageData, 0, 0);
}

/**
 * Draw an image on a canvas
 */
export function renderImageToCanvas(image, size, canvas) {
  canvas.width = size[0];
  canvas.height = size[1];
  const ctx = canvas.getContext(&quot;2d&quot;);

  ctx.drawImage(image, 0, 0);
}

/**
 * Draw heatmap values, one of the model outputs, on to the canvas
 * Read our blog post for a description of PoseNet's heatmap outputs
 * https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5
 */
export function drawHeatMapValues(heatMapValues, outputStride, canvas) {
  const ctx = canvas.getContext(&quot;2d&quot;);
  const radius = 5;
  const scaledValues = heatMapValues.mul(tf.scalar(outputStride, &quot;int32&quot;));

  drawPoints(ctx, scaledValues, radius, color);
}

/**
 * Used by the drawHeatMapValues method to draw heatmap points on to
 * the canvas
 */
function drawPoints(ctx, points, radius, color) {
  const data = points.buffer().values;

  for (let i = 0; i &amp;lt; data.length; i += 2) {
    const pointY = data[i];
    const pointX = data[i + 1];

    if (pointX !== 0 &amp;amp;&amp;amp; pointY !== 0) {
      ctx.beginPath();
      ctx.arc(pointX, pointY, radius, 0, 2 * Math.PI);
      ctx.fillStyle = color;
      ctx.fill();
    }
  }
}

/**
 * Draw offset vector values, one of the model outputs, on to the canvas
 * Read our blog post for a description of PoseNet's offset vector outputs
 * https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5
 */
export function drawOffsetVectors(
  heatMapValues,
  offsets,
  outputStride,
  scale = 1,
  ctx
) {
  const offsetPoints = posenet.singlePose.getOffsetPoints(
    heatMapValues,
    outputStride,
    offsets
  );

  const heatmapData = heatMapValues.buffer().values;
  const offsetPointsData = offsetPoints.buffer().values;

  for (let i = 0; i &amp;lt; heatmapData.length; i += 2) {
    const heatmapY = heatmapData[i] * outputStride;
    const heatmapX = heatmapData[i + 1] * outputStride;
    const offsetPointY = offsetPointsData[i];
    const offsetPointX = offsetPointsData[i + 1];

    drawSegment(
      [heatmapY, heatmapX],
      [offsetPointY, offsetPointX],
      color,
      scale,
      ctx
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;07&quot; data-ke-size=&quot;size23&quot;&gt;프로토타입 개발&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pe-assistant.vercel.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://pe-assistant.vercel.app/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1636905626961&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;체육 수업 도우미와 함께라면 집에서도 체육 수업을 받을 수 있어요 코로나19로 인해 체육 수업을 기존과 같이 제대로 할 수 없습니다. 신체 및 정서 발달 등에 중요한 체육 수업을 코로나19시기&quot; data-og-host=&quot;pe-assistant.vercel.app&quot; data-og-source-url=&quot;https://pe-assistant.vercel.app/&quot; data-og-url=&quot;https://pe-assistant.vercel.app/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pe-assistant.vercel.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pe-assistant.vercel.app/&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;체육 수업 도우미&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;체육 수업 도우미와 함께라면 집에서도 체육 수업을 받을 수 있어요 코로나19로 인해 체육 수업을 기존과 같이 제대로 할 수 없습니다. 신체 및 정서 발달 등에 중요한 체육 수업을 코로나19시기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pe-assistant.vercel.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저희 프로젝트의 프로토타입 url입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1622208137718&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx create-next-app&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;create-next-app으로 nextjs app을 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 타입스크립트, prettier, eslint, babel 설정 등을 해줬고, vercel을 통해 배포했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1636905737276&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 - chorom-ham/pe-assistant&quot; data-og-description=&quot;Contribute to chorom-ham/pe-assistant development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/chorom-ham/pe-assistant&quot; data-og-url=&quot;https://github.com/chorom-ham/pe-assistant&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ck6ybm/hyMljrqAs4/kuKSWQ157OM1vCDwmVjsuK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/chorom-ham/pe-assistant&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/chorom-ham/pe-assistant&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ck6ybm/hyMljrqAs4/kuKSWQ157OM1vCDwmVjsuK/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 - chorom-ham/pe-assistant&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to chorom-ham/pe-assistant 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;&lt;a href=&quot;https://github.com/chorom-ham/pe-assistant&quot;&gt;https://github.com/chorom-ham/pe-assistant&lt;/a&gt;에 가셔서 git clone을 통해 다운 받으신 후&lt;/p&gt;
&lt;pre id=&quot;code_1622208349115&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install
npm run dev&lt;/code&gt;&lt;/pre&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;3450&quot; data-origin-height=&quot;2608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMpbCv/btrkHDFOKAd/nJmlBYrZxKqkwfuEaCkjok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMpbCv/btrkHDFOKAd/nJmlBYrZxKqkwfuEaCkjok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMpbCv/btrkHDFOKAd/nJmlBYrZxKqkwfuEaCkjok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMpbCv%2FbtrkHDFOKAd%2FnJmlBYrZxKqkwfuEaCkjok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3450&quot; height=&quot;2608&quot; data-origin-width=&quot;3450&quot; data-origin-height=&quot;2608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토타입의 현재 UI입니다. 고도화 진행 예정 중에 있습니다.&lt;/p&gt;</description>
      <category>회고/프로젝트</category>
      <author>개발하는 크롱</author>
      <guid isPermaLink="true">https://crong-dev.tistory.com/71</guid>
      <comments>https://crong-dev.tistory.com/71#entry71comment</comments>
      <pubDate>Mon, 15 Nov 2021 00:13:58 +0900</pubDate>
    </item>
    <item>
      <title>[코어 자바스크립트] 02. 실행 컨텍스트</title>
      <link>https://crong-dev.tistory.com/70</link>
      <description>&lt;h1&gt;01) 실행 컨텍스트란?&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;/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;&amp;ldquo;동일한 환경&amp;rdquo;이란? 하나의 실행 컨텍스트를 구성할 수 있는 방법으로 전역공간, eval() 함수, 함수 등이 있음.&lt;/li&gt;
&lt;li&gt;자동으로 생성되는 전역공간과, eval을 제외하면 우리가 흔히 실행 컨텍스트를 구성하는 방법은 함수 실행뿐임.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;처음 자바스크립트 코드를 실행하는 순간 전역 컨텍스트가 콜 스택에 담김. 최상단의 공간은 코드 내부에서 별도 실행 명령이 없어도 브라우저에서 자동 실행하므로 자바스크립트 파일이 열리는 순간 전역 컨텍스트 활성화됨.&lt;/li&gt;
&lt;li&gt;전역 컨텍스트와 관련된 코드를 순차적으로 진행하다가 함수를 호출하면 자바스크립트 엔진은 해당 함수에 대한 환경 정보를 수집해서 해당 함수의 실행 컨텍스트를 생성한 후 콜 스택에 담음. 콜 스택 맨 위에 해당 함수의 실행 컨텍스트가 놓인 상태가 됐으므로 전역 컨텍스트와 관련된 코드의 실행을 일시중단하고 해당 함수의 실행 컨텍스트와 관련된 코드, 즉 해당 함수 내부 코드를 순차로 실행.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스택 구조 상 한 실행 컨텍스트가 콜 스택의 맨 위에 쌓이는 순간이 곧 현재 실행할 코드에 관여하게 되는 시점. (기존 컨텍스트는 더 아래에 위치하기 때문)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 실행 컨텍스트가 활성화될 떄 자바스크립트 엔진은 해당 컨텍스트에 관련된 코드를 실행하는 데 필요한 환경 정보를 수집해서 실행 컨텍스트 객체에 저장.&lt;/li&gt;
&lt;li&gt;실행 컨텍스트 객체는 js엔진이 활용할 목적으로만 생성할 뿐, 개발자는 확인 불가.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 컨텍스트 객체에 담기는 정보:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VariableEnvironment: 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부 환경 정보. &amp;ldquo;선언 시점&amp;rdquo;의 LexicalEnvironment의 스냅샷으로 변경사항은 반영 안됨.&lt;/li&gt;
&lt;li&gt;LexicalEnvironment: 처음엔 VariableEnvironment와 같지만 변경사항이 실시간으로 반영됨&lt;/li&gt;
&lt;li&gt;ThisBinding: this 식별자가 바라봐야 할 대상 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;02) VariableEnvironment&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;ldquo;선언 시점&amp;rdquo;의 LexicalEnvironment의 스냅샷으로 변경사항은 반영 안됨.&lt;/li&gt;
&lt;li&gt;실행 컨텍스트 생성 시 VariableEnvironment에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LexicalEnvironment를 만들고, 이후에는 LexicalEnvironment를 주로 활용.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VariableEnvironment와 LexicalEnvironment의 내부는 environmentRecord와 outerEnvironmentReference로 구성. 초기화 과정 중에는 둘이 완전히 동일하고 이후 코드 진행에 따라 서로 달라짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;03) LexicalEnvironment&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;ldquo;현재 컨텍스트 내부에는 a, b, c와 같은 식별자들이 있고 그 외부 정보는 D를 참조하도록 구성돼 있다.&amp;rdquo;와 같은 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. environmentRecord와 호이스팅&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장됨: 컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 선언한 함수가 있을 경우 그 함수 자체, var로 선언된 변수의 식별자 등.&lt;/li&gt;
&lt;li&gt;컨텍스트 내부 전체를 처음부터 끝까지 훑어나가며 &lt;b&gt;순서대로&lt;/b&gt; 수집함.&lt;/li&gt;
&lt;li&gt;변수 수집 과정을 모두 마쳤더라도 실행 컨텍스트가 관여할 코드들은 아직 실행 전. =&amp;gt; 코드 실행 전임에 불구하고 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명을 모두 알게 있게 됨. =&amp;gt; &lt;b&gt;&lt;i&gt;호이스팅&lt;/i&gt;&lt;/b&gt;. 실제로 식별자들을 최상단에 끌어올리는 것은 아니나 그렇게 이해해도 무방하므로, 변수정보 수집 과정을 더욱 이해하기 쉬운 방법으로 대체한 가상의 개념.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;c.f) 전역 실행 컨텍스트는 변수 객체 생성 대신 자바스크립트 구동환경이 별도로 제공하는 &amp;ldquo;전역 객체&amp;rdquo;(global object)를 활용. 전역 객체에는 브라우저의 window, Node.js의 global 객체 등이 있음. 이들은 자바스크립트 네이티브 객체가 아니라 호스트 객체로 분류됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;정리&amp;gt; 어떤 함수가 호출되면 자바스크립트 엔진이 해당 함수를 실행하는 데 필요한 환경 정보 수집해서 실행 컨텍스트 객체 생성 후 콜 스택에 담음. 이렇게 환경정보를 수집하는 과정에서 컨텍스트 내부 전체의 식별자 정보를 모두 수집하므로 코드 실행 전에 해당 환경에 속한 코드의 모든 변수명을 알게 되는 것. 이것을 마치 &amp;ldquo;&amp;lsquo;변수명&amp;rsquo;을 모두 최상단에 끌어 올린 다음 코드를 실행하는 것&amp;rdquo;(호이스팅)으로 생각하더라도 코드를 해석하는 데 문제없음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;[호이스팅 규칙]&lt;/i&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;environmentRecord는 현재 실행될 컨텍스트의 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있고, 각 식별자에 어떤 값이 할당될 것인지는 관심이 없음. 따라서 호이스팅 시 변수명만 끌어올리고 할당 과정은 원래 자리에 남겨둠.&lt;/li&gt;
&lt;li&gt;변수는 선언부와 할당부를 나누어 선언부만 끌어올리는 반면 함수 선언은 함수 전체를 끌어올림.&lt;/li&gt;
&lt;li&gt;호이스팅이 끝난 상태에서의 함수 선언문은 함수명으로 선언한 변수에 함수를 할당한 것처럼 여길 수 있음.
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;// ex)
function b ( ) { } // var b = function b ( ) { }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;[함수 선언문과 함수 표현식]&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;둘 모두 함수를 새롭게 정의할 때 쓰이는 방식&lt;/li&gt;
&lt;li&gt;함수 선언문: function 정의부만 존재하고 별도의 할당 명령이 없는 것. 반드시 함수명이 정의되어 있어야 함.&lt;/li&gt;
&lt;li&gt;함수 표현식: 정의한 function을 별도의 변수에 할당하는 것을 말함. 함수명 정의 필요없음. 함수명을 정의한 함수 표현식을 &amp;lsquo;기명 함수 표현식&amp;rsquo;, 정의하지 않은 것을 &amp;lsquo;익명 함수 표현식&amp;rsquo;이라고 부르기도 함. 일반적으로 함수 표현식은 익명 함수 표현식을 말함.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function a () {  /*&amp;hellip;.*/  }     // 함수 선언문. 함수명 a가 곧 변수명.
a();     // 실행 OK

var b = function () {  /*&amp;hellip;.*/  }     // (익명) 함수 표현식. 변수명 b가 곧 함수명.
b();     // 실행 OK

var c = function d () {  /*&amp;hellip;.*/  }     // 기명 함수 표현식. 변수명은 c, 함수명은 d.
c();     // 실행 OK
d();     // 에러!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기명 함수 표현식 주의점: 외부에서는 함수명으로 함수 호출 불가. 함수명은 오직 함수 내부에서만 접근 가능. 해당 함수 내에서 재귀 호출용도로 사용 가능. 과거에 디버깅 편해서 사용했는데 이제는 필요 없음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 선언문과 함수 표현식은 호이스팅 시 큰 차이 발생.&lt;/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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 스코프, 스코프 체인, outerEnvironmentReference&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;스코프 체인: &amp;lsquo;식별자에 대한 유효범위&amp;rsquo;를 안에서부터 바깥으로 차례로 검색해 나가는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ES5까지의 자바스크립트: 오직 함수에 의해서만 스코프가 생성됨(전역공간 제외).&lt;/li&gt;
&lt;li&gt;ES6부터는 다른 언어와 비슷하게 블록에 의해서도 스코프 경계가 발생하게 함. var로 선언한 변수에는 작용하지 않고 오직 새로 생긴 let, const, class, strict mode에서의 함수 선언 등에 대해서만 범위로서의 역할 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;[스코프 체인 예시]&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A 함수 내부에 B 함수를 선언하고 다시 B 함수 내부에 C 함수를 선언한 경우, 함수 C의 outerEnvironmentReference는 함수 B의 LexicalEnvironment를 참조함&lt;/li&gt;
&lt;li&gt;함수 B의 LexicalEnvironment에 있는 outerEnvironmentReference는 다시 함수 B가 선언되던 때의 함수 A의 LexicalEnvironment를 참조함&lt;/li&gt;
&lt;li&gt;이처럼 outerEnvironmentReference는 연결리스트 형태를 띔&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lsquo;선언 시점의 LexicalEnvironment&amp;rsquo;를 계속 찾아 올라가면 마지막엔 전역 컨텍스트가 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 outerEnvironmentReference는 오직 자신이 선언된 시점의 LexicalEnvironment만 참조하고 있으므로 가장 가까운 요소부터 차례로 접근할 수 있고 다른 순서로 접근은 불가함.&lt;/li&gt;
&lt;li&gt;따라서 여러 스코프에서 동일한 식별자를 선언한 경우, &lt;b&gt;&lt;i&gt;무조건 스코프 체인 상에서 가장 먼저 발견된 식별자에만 접근 가능&lt;/i&gt;&lt;/b&gt;하게 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 변수에 접근하려고 하면 스코프 체인을 쭉 따라가다가 제일 먼저 발견하는 변수의 값을 반환. 전역 컨텍스트까지 탐색해도 못 찾으면 undefined 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스코프 체인 상에 있는 변수라고 모두 접근 가능한 것은 아님. 동일한 이름의 식별자 여러번 선언한 경우 가장 가까운 식별자에만 접근 가능. 더 상위의 동일한 이름의 변수에는 접근 불가능. =&amp;gt; &lt;b&gt;&lt;i&gt;변수 은닉화&lt;/i&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;04) this&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 컨텍스트의 thisBinding에는 this로 지정된 객체가 저장됨&lt;/li&gt;
&lt;li&gt;실행 컨텍스트 활성화 당시에 this가 지정되지 않은 경우 this에는 전역 객체가 저장됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전역 컨텍스트의 LexicalEnvironment에 담긴 변수를 전역변수라고 하고, 그 외는 다 지역변수&lt;/li&gt;
&lt;li&gt;안전한 코드 구성을 위해 전역변수 사용 최소화할 것.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>JavaScript</category>
      <category>JS</category>
      <category>자바스크립트</category>
      <author>개발하는 크롱</author>
      <guid isPermaLink="true">https://crong-dev.tistory.com/70</guid>
      <comments>https://crong-dev.tistory.com/70#entry70comment</comments>
      <pubDate>Thu, 5 Aug 2021 02:04:46 +0900</pubDate>
    </item>
    <item>
      <title>[코어 자바스크립트] 01. 데이터 타입 정리</title>
      <link>https://crong-dev.tistory.com/69</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;01.&lt;/b&gt; &lt;b&gt;데이터 타입&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;01)&lt;/b&gt; &lt;b&gt;데이터 타입의 종류&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자바스크립트의 데이터 타입은 크게 두 가지: 기본형(원시형, primitive type), 참조형(reference type)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기본형: 숫자, 문자열, 불리언, null, undefined 등 + Symbol(ES6)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 참조형: 객체(배열, 함수, 정규표현식, Map, WeakMap, Set, WeakSet 등이 객체의 하위 분류)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기본형은 불변성을 띔&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;02)&lt;/b&gt; &lt;b&gt;데이터 타입에 관한 배경지식&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.&lt;/b&gt; &lt;b&gt;메모리와 데이터&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- C/C++, Java 등의 정적 타입 언어는 메모리 낭비 최소화를 위해 데이터 타입별로 할당할 메모리 영역을 2바이트, 4바이트 등으로 나누어 정해 놓음. 그 범위를 벗어나는 데이터를 입력하면 오류가 나거나 잘못된 값이 저장됨. 메모리 용량이 매우 부족했던 시절에는 불가피한 선택이었음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자바스크립트는 메모리 용량이 과거보다 월등히 커진 상황에서 등장함. 따라서 메모리 공간을 좀 더 넉넉하게 할당함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자바스크립트에서는 숫자를 정수형인지 부동소수형인지 구분하지 않고 8바이트를 확보함. 씨언어처럼 형변환을 걱정해야하는 상황이 훨씬 덜 발생.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.&lt;/b&gt; &lt;b&gt;식별자와 변수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 변수: 변할 수 있는 수. 수라고 반드시 숫자라는 의미가 아님. 변할 수 있는 무언가(데이터: 숫자, 문자열, 객체 등)로 해석하는 게 적절.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 식별자: 어떤 데이터를 식별하는 데 사용되는 이름. 즉, 변수명.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;03)&lt;/b&gt; &lt;b&gt;변수 선언과 데이터 할당&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.&lt;/b&gt; &lt;b&gt;변수 선언&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;var a;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드로 변수 선언 시 컴퓨터는 메모리에서 비어있는 공간 하나를 확보함. 그 다음 그 공간의 이름(식별자)을 a라고 지정함. 이후 사용자가 a에 접근하고자 하면 컴퓨터는 메모리에서 a라는 이름을 가진 주소를 검색해 해당 공간에 담긴 데이터를 반환할 것임.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 데이터 할당&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 변수 선언과 할당 과정&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리에서 비어있는 공간 확보&lt;/li&gt;
&lt;li&gt;확보한 공간의 식별자 지정&lt;/li&gt;
&lt;li&gt;데이터 저장을 위한 별도의 메모리 공간을 확보해 데이터 저장&lt;/li&gt;
&lt;li&gt;식별자 검색&lt;/li&gt;
&lt;li&gt;데이터를 저장한 주소를 검색해 찾은 공간에 대입&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 처음 메모리에서 확보한 공간에 바로 값을 대입하지 않고 한 단계 더 거치는 이유?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 변환을 자유롭게 할 수 있게 함&lt;/li&gt;
&lt;li&gt;메모리의 효율적 관리&lt;/li&gt;
&lt;li&gt;문자열은 정해진 규격이나 문자수가 없음. 필요한 메모리 용량이 가변적임. 미리 확보한 영역 내에서만 데이터 변환이 가능하다면 처리해야 할 연산이 많아짐(확보된 공간을 변환된 데이터 크기에 맞게 늘리는 작업 필요하기 때문)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+) 문자열 변경 시 문자열을 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;새로&lt;/span&gt; 만들어 별도의 공간에 저장하고 그 주소를 식별자에 연결&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;04) 기본형 데이터와 참조형 데이터&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 불변값&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 불변값과 상수는 다른 개념이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 상수와 변수는 &quot;변수 영역 메모리의 변경 가능성&quot;으로 구분함. 바꿀 수 있으면 변수, 없으면 상수임.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 위에서 &quot;변수 영역 메모리의 변경 가능성&quot;의 의미는 한 번 데이터 할당이 이루어진 변수 공간에 다른 데이터를 재할당할 수 있는지 여부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &quot;불변성 여부&quot;를 구분할 때의 변경 가능성의 대상은 데이터 영역 메모리임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기본형 데이터는 모두 불변값임. 참조형 데이터는 가변값인 경우가 많지만 100%는 아님(변경 불가 설정 혹은 불변값으로 활용하는 방법 있음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 변수에 불변값 할당할 때, 일단 데이터 영역에서 할당하려는 값이 있는지 찾고, 없으면 새로 만듦&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;예) 5를 할당했던 변수 b의 값을 7로 바꾸는 과정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존에 저장된 5 자체를 7로 바꾸는 것이 아님&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존에 저장했던(이미 만들어둔) 7이 있으면 그 주소를 재활용. 없으면 새로 만들어 b에 저장.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 가변값&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 참조형 데이터의 할당: 기본형 데이터와의 차이는 &quot;객체의 변수(프로퍼티) 영역&quot;이 별도로 존재함. 객체의 데이터 영역에는 프로퍼티 주소 목록 저장함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 참조형 데이터의 프로퍼티 재할당 시, 새로운 객체를 생성하는 것이 아니라(데이터 영역에 새 객체 만드는 것이 아님) 기존의 객체 내부 값을 변경하는 것임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 중첩 객체(nested object): 참조형 데이터의 프로퍼티에 다시 참조형 데이터를 할당한 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예)&lt;/p&gt;
&lt;pre id=&quot;code_1626827986107&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var obj = {
    a: 3,
    arr: [1, 2, 3]
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 중첩 객체의 프로퍼티 할당 시 객체 내부의 객체도 참조형 데이터 할당 방식을 따른다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 변수 복사 비교&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 변수 복사 시 복사하려는 변수의 식별자를 검색해 해당 변수의 값을 찾아 대입&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 복사 과정은 기본형/참조형 데이터 모두 같은 주소를 바라보게 된다는 점에서 동일함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 복사 과정은 동일하지만 데이터 할당 과정에서 이미 차이가 있기 때문에 변수 복사 이후의 동작에도 큰 차이 발생함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 변수 복사 이후 값을 변경했을 때 차이점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-&amp;gt; 기본형 데이터를 복사한 변수의 값을 변경하게 되면 해당 변수의 값이 달라짐. 즉 데이터 영역의 다른 주소를 가리키게 됨.&lt;/p&gt;
&lt;pre id=&quot;code_1626828999337&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var a = 10;
var b = a; // 변수 복사

console.log(a); // 10
console.log(b); // 10

b = 20; // 기본형 데이터 복사한 변수의 값 변경

console.log(a); // 10
console.log(b); // 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a랑 b는 이제 다른 값을 바라보게 됨. (a !== b)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 참조형 데이터를 복사한 변수의 &lt;u&gt;프로퍼티 값&lt;/u&gt; 변경 시에는 해당 변수의 값이 그대로임. 여전히 같은 값을 가지고 있음.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1626829233038&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var obj1 = {c: 10, d: &quot;str&quot;};
var obj2 = obj1;

console.log(obj1.c); //10
console.log(obj2.c); //10

obj2.c = 20;

console.log(obj1.c); //20
console.log(obj2.c); //20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 복사한 변수의 프로퍼티 값을 변경했지만 여전히 obj1과 obj2는 같은 값을 바라봄. (obj1 === obj2)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 참조형 데이터를 복사한 변수의 프로퍼티 값이 아니라 객체 자체를 변경했을 때는 메모리의 데이터 영역에 새로운 공간에 새로운 객체가 저장되고 그 주소를 해당 변수의 변수 영역 위치에 저장. 즉 기존의 복사 대상과 값이 달라짐.&lt;/p&gt;
&lt;pre id=&quot;code_1626829647991&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var obj1 = {c: 10, d: &quot;abc&quot;};
var obj2 = obj1;

console.log(obj1 === obj2); // true

obj2.c = 20;
console.log(obj1 === obj2); // true

obj2 = {c: 20, d: &quot;abc&quot;};
console.log(obj1 === obj2); // false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 참조형 데이터가 &quot;가변값&quot;이라고 설명할 때의 &quot;가변&quot;은 참조형 데이터 자체를 변경하는 경우가 아니라 그 내부의 프로퍼티를 변경할 때만 성립. 데이터 자체를 변경하고자 하면(새로운 데이터를 할당하고자 하면) 기본형 데이터와 마찬가지로 기존 데이터 불변.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;05) 불변 객체&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1.&amp;nbsp; 불변 객체를 만드는 간단한 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 프로퍼티가 아니라 데이터 자체를 변경하고자 하면 기본형 데이터와 마찬가지로 기존 데이터가 변하지 않는다. 따라서 내부 프로퍼티를 변경할 때마다 매번 새로운 객체를 만들어 재할당하기로 규칙을 정하거나 새로운 객체를 만드는 도구를 활용한다면 객체도 불변성을 확보할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 불변 객체가 필요한 상황: 값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 상황&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 얕은 복사와 깊은 복사&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 얕은 복사: 바로 아래 단계의 값만 복사하는 방법. 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 &quot;주솟값&quot;만 복사. (원본 사본 모두 동일한 참조형 데이터의 주소를 가리키게 됨. 즉 원본 바뀌면 사본도, 사본 바뀌면 원본도 바뀜)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 깊은 복사: 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법. 내부 프로퍼티가 객체면 재귀적으로 복사 함수를 호출하는 방식으로 하거나, JSON.parse(JSON.stringfy(target)으로 간편하게 가능. JSON 방식은 __proto__, getter/setter 등과 같이 JSON으로 변경할 수 없는 프로퍼티 무시.) 혹은 라이브러리 사용.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;06) undefined와 null&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 &quot;없음&quot;을 나타내는 두 가지 값.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'undefined'는 어떤 변수에 값이 존재하지 않음을 의미하고 'null'은 사용자가 명시적으로 '없음'을 표현하기 위해 대입한 값.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본래의 의미에 따라 사용자가 없음을 표현하기 위해 명시적으로 undefined 대입하는 것은 지양해야함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[undefined]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사용자가 명시적으로 지정할 수도 있지만 값이 존재하지 않을 때 자바스크립트 엔진이 자동으로 부여하는 경우도 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자바스크립트 엔진이 사용자가 응당 어떤 값을 지정할 것이라고 예상되는 상황임에도 실제로는 그렇게 하지 않았을 때 undefined 반환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자바스크립트 엔진이 사용자가 응당 어떤 값을 지정할 것이라고 예상되는 상황&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;1) 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때(변수 선언시 자동으로 undefined 할당하는 것이 아니라 이후 해당 변수에 접근하고자 할 때 비로소 undefined를 반환하는 것)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;2) 객체 내부의 존재하지 않는 프로퍼티에 접근하려고 할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;3) return 문이 없거나 호출되지 않는 함수의 실행 결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- '비어있는 요소'와 'undefined를 할당한 요소'는 출력 결과부터 다름. '비어있는 요소'는 순회와 관련된 많은 배열 메서드들의 순회대상에서 제외됨. undefined는 비록 비어있음을 의미하긴 하지만 하나의 값으로 동작하기 때문에 이때의 프로퍼티나 배열의 요소는 고유의 키값(프로퍼티 이름)이 실존하게 되고, 따라서 순회의 대상이 될 수 있음.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 값으로써 어딘가에 할당된 undefined는 실존하는 데이터인 반면, 자바스크립트 엔진이 반환해주는 undefined는 문자 그대로 값이 없음을 의미.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[null]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'비어있음'을 명시적으로 나타내고 싶을 때는 undefined가 아닌 null을 써라.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 규칙을 따르는 한 undefined가 &quot;값을 대입하지 않은 변수에 접근하고자 할 때 자바스크립트 엔진이 반환해주는 값&quot;으로서만 존재 가능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 주의점: typeof null이 object임. (자바스크립트 자체 버그)&lt;/p&gt;
&lt;pre id=&quot;code_1627575257819&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var n = null;
console.log(typeof n); // object

console.log(n == undefined); // true
console.log(n == null); // true

console.log(n === undefined); // false
console.log(n === null); // true&lt;/code&gt;&lt;/pre&gt;</description>
      <category>JavaScript</category>
      <author>개발하는 크롱</author>
      <guid isPermaLink="true">https://crong-dev.tistory.com/69</guid>
      <comments>https://crong-dev.tistory.com/69#entry69comment</comments>
      <pubDate>Fri, 30 Jul 2021 01:17:03 +0900</pubDate>
    </item>
    <item>
      <title>유튜브 조회수 상승을 위한 제목 및 썸네일 추천 서비스: 유튜브 API 사용법, AWS Rekognition 사용 방법 및 얼굴 인식 데이터로 조회수 예측하기 등</title>
      <link>https://crong-dev.tistory.com/68</link>
      <description>&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;#01&quot;&gt;프로젝트 진행 동기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#02&quot;&gt;프로젝트 요약&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#03&quot;&gt;프로젝트 진행 상황과 본인 역할&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#04&quot;&gt;YouTube API를 활용한 데이터 수집&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#05&quot;&gt;썸네일에서 사람 얼굴을 인식해 feature를 추출&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#06&quot;&gt;얼굴 인식 데이터를 바탕으로 조회수를 예측하는 모델 개발&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#07&quot;&gt;프로토타입 개발&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;01&quot; data-ke-size=&quot;size23&quot;&gt;프로젝트 진행 동기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;773&quot; width=&quot;476&quot; height=&quot;634&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQdt5i/btq52hO7VvK/LND0oXEiit8luSK1ZYFj1k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQdt5i/btq52hO7VvK/LND0oXEiit8luSK1ZYFj1k/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQdt5i/btq52hO7VvK/LND0oXEiit8luSK1ZYFj1k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQdt5i%2Fbtq52hO7VvK%2FLND0oXEiit8luSK1ZYFj1k%2Fimg.webp&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;773&quot; width=&quot;476&quot; height=&quot;634&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국민 83%가 한 달에 평균 30시간을 유튜브 시청에 사용하고 있다는 통계 자료가 있습니다. 유튜브 시장과 크리에이터 수도 꾸준히 증가하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주변에 유튜브 크리에이터로서 영상을 만들고 업로드하는 친구들도 늘어났습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유튜브 채널을 운영하는 사람들은 어떻게 하면 조회수가 잘 나올지에 대해 많은 고민을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 영상 컨텐츠나 퀄리티도 중요하지만 사람들의 이목을 끄는 제목이나 썸네일도 못지 않게 중요하다고 생각했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유튜브 댓글 중 &quot;지나칠 수 없는 썸네일&quot;, &quot;제목 때문에 궁금해서 들어왔다&quot; 같은 댓글을 자주 봤던 기억이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 사람들의 시청을 유도하는 제목과 썸네일의 특징이 있을까 궁금해졌습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;782&quot; data-filename=&quot;스크린샷 2021-05-28 오후 1.42.51.png&quot; width=&quot;691&quot; height=&quot;414&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVEK9C/btq51QqO0zE/TTRr8mjKpQXX9AKdpSZ7w1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVEK9C/btq51QqO0zE/TTRr8mjKpQXX9AKdpSZ7w1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVEK9C/btq51QqO0zE/TTRr8mjKpQXX9AKdpSZ7w1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVEK9C%2Fbtq51QqO0zE%2FTTRr8mjKpQXX9AKdpSZ7w1%2Fimg.png&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;782&quot; data-filename=&quot;스크린샷 2021-05-28 오후 1.42.51.png&quot; width=&quot;691&quot; height=&quot;414&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;구글링을 해보니 위와 같은 컨텐츠들이 나왔고, 무언가 공식이나 features가 있을 것이라는 생각이 좀 더 확실해졌습니다. 더불어 유튜버가 제목과 썸네일을 정하는 데 많은 노력을 쏟고 있다는 것도 확인했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인공지능 모델을 활용해 유튜브 조회수가 높게 나오는 제목 및 썸네일을 찾는 프로젝트를 해보면 재미있겠다(그리고 수익성이 있겠다)라는 생각이 들었습니다. 그렇게 졸업프로젝트로&amp;nbsp;&quot;유튜브 조회수 상승을 위한 제목 및 썸네일 추천 서비스&quot;&amp;nbsp; 개발을 하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;02&quot; data-ke-size=&quot;size23&quot;&gt;프로젝트 요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #272938;&quot;&gt;사용자가 원하는 제목이나 썸네일 후보를 입력하면 예상 조회수가 가장 높은 제목 및 썸네일을 추천하는 서비스입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 유튜브 api를 활용해 필요한 데이터를 수집합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 수집한 데이터를 기반으로 제목 / 썸네일의 텍스트 / 썸네일의 사람 얼굴을 추출하고, 추출한 데이터를 모델에 학습시켜 조회수를 예측하는 모델을 개발합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 조회수 예측 모델을 사용해 사용자가 후보로 넣은 제목과 썸네일의 조회수를 예측합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 사용자가 넣은 후보 중 가장 조회수가 높게 예측된 제목과 서비스를 추천해줍니다. 부가적으로 넣은 후보 간의 수치 데이터를 시각화한 비교 분석 레포트 제공, 조언까지 제공할 계획입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;03&quot; data-ke-size=&quot;size23&quot;&gt;프로젝트 진행 상황과 본인 역할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 프로젝트는 2학기간 진행됩니다.(2021.03~2021.11)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1학기에는 유튜브 제목과 썸네일을 학습시켰을 때 모델이 조회수를 제대로 예측할 수 있는지를 확인하는 것을 목표로 요소 기술 검증을 진행했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제목 / 썸네일의 텍스트 / 썸네일의 사람 얼굴로 나누어서 각각에 대해 조회수를 예측하는 모델을 개발했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전 먼저 유튜브 api를 활용해 필요한 데이터를 수집했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 썸네일의 사람 얼굴을 인식해 feature를 추출하고, 추출한 데이터를 바탕으로 조회수를 예측하는 모델을 개발했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 실제 유저에게 서비스할 웹서비스의 프로토타입을 개발했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제목에 대해서 조회수 예측 모델을 개발한 친구의 블로그 글입니다! 저희 프로젝트에 대해 더 궁금하시다면 방문해주세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tae-hi.tistory.com/2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tae-hi.tistory.com/2&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622210396866&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;문장 벡터 기반 유사도 검색시스템 &amp;amp; LSTM을 활용한 텍스트 분류&quot; data-og-description=&quot;유튜브에서 발표한 자료에 의하면 광고로 인한 순수익이 매년 한국돈 1조원 가량씩 증가하고 있다고 한다. 시장 규모가 큰 만큼 유튜브에 도전해보는 사람들(크리에이터)도 계속 증가하는 추세&quot; data-og-host=&quot;tae-hi.tistory.com&quot; data-og-source-url=&quot;https://tae-hi.tistory.com/2&quot; data-og-url=&quot;https://tae-hi.tistory.com/2&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eaCzJH/hyKnuugDZZ/x8NDKK4lmGQkTOiyjLgyK0/img.png?width=737&amp;amp;height=467&amp;amp;face=0_0_737_467,https://scrap.kakaocdn.net/dn/wUsEh/hyKnjfc8wH/PvHmM4HVat38QMnCFa9su1/img.png?width=737&amp;amp;height=467&amp;amp;face=0_0_737_467,https://scrap.kakaocdn.net/dn/qkGFY/hyKmFYtDx6/gNUUVkzcy4TOukSfguq180/img.png?width=747&amp;amp;height=661&amp;amp;face=0_0_747_661&quot;&gt;&lt;a href=&quot;https://tae-hi.tistory.com/2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tae-hi.tistory.com/2&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eaCzJH/hyKnuugDZZ/x8NDKK4lmGQkTOiyjLgyK0/img.png?width=737&amp;amp;height=467&amp;amp;face=0_0_737_467,https://scrap.kakaocdn.net/dn/wUsEh/hyKnjfc8wH/PvHmM4HVat38QMnCFa9su1/img.png?width=737&amp;amp;height=467&amp;amp;face=0_0_737_467,https://scrap.kakaocdn.net/dn/qkGFY/hyKmFYtDx6/gNUUVkzcy4TOukSfguq180/img.png?width=747&amp;amp;height=661&amp;amp;face=0_0_747_661');&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;amp; LSTM을 활용한 텍스트 분류&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;유튜브에서 발표한 자료에 의하면 광고로 인한 순수익이 매년 한국돈 1조원 가량씩 증가하고 있다고 한다. 시장 규모가 큰 만큼 유튜브에 도전해보는 사람들(크리에이터)도 계속 증가하는 추세&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tae-hi.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;h3 id=&quot;04&quot; data-ke-size=&quot;size23&quot;&gt;YouTube API를 활용한 데이터 수집&lt;/h3&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 data-ke-size=&quot;size16&quot;&gt;그 다음, 구글 클라우드 플랫폼 &amp;gt; API 및 서비스에 들어갑니다. (&lt;a href=&quot;https://console.cloud.google.com/apis&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://console.cloud.google.com/apis&lt;/a&gt;)&lt;/p&gt;
&lt;figure id=&quot;og_1622184336280&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;Google Cloud Platform&quot; data-og-description=&quot;하나의 계정으로 모든 Google 서비스를 Google Cloud Platform을 사용하려면 로그인하세요.&quot; data-og-host=&quot;accounts.google.com&quot; data-og-source-url=&quot;https://console.cloud.google.com/apis&quot; data-og-url=&quot;https://accounts.google.com/ServiceLogin?service=cloudconsole&amp;amp;passive=1209600&amp;amp;osid=1&amp;amp;continue=https://console.cloud.google.com/apis&amp;amp;followup=https://console.cloud.google.com/apis&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://console.cloud.google.com/apis&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://console.cloud.google.com/apis&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;Google Cloud Platform&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;하나의 계정으로 모든 Google 서비스를 Google Cloud Platform을 사용하려면 로그인하세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;accounts.google.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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2420&quot; data-origin-height=&quot;904&quot; data-filename=&quot;스크린샷 2021-05-28 오후 3.54.11.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caEOab/btq52ZubhHn/ixCIdy0G4ad43qVjs6NRQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caEOab/btq52ZubhHn/ixCIdy0G4ad43qVjs6NRQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caEOab/btq52ZubhHn/ixCIdy0G4ad43qVjs6NRQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaEOab%2Fbtq52ZubhHn%2FixCIdy0G4ad43qVjs6NRQ0%2Fimg.png&quot; data-origin-width=&quot;2420&quot; data-origin-height=&quot;904&quot; data-filename=&quot;스크린샷 2021-05-28 오후 3.54.11.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;/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;프로젝트 이름은 아무거나 하셔도 상관 없습니다. 저는 그냥 My Project로 했습니다. 프로젝트를 만들고 나면 아래와 같은 화면이 뜹니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1980&quot; data-filename=&quot;스크린샷 2021-05-28 오후 3.58.33.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEdwjC/btq51QEZKcw/a0QNGTVFuwCydr0e7PaTrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEdwjC/btq51QEZKcw/a0QNGTVFuwCydr0e7PaTrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEdwjC/btq51QEZKcw/a0QNGTVFuwCydr0e7PaTrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEdwjC%2Fbtq51QEZKcw%2Fa0QNGTVFuwCydr0e7PaTrK%2Fimg.png&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1980&quot; data-filename=&quot;스크린샷 2021-05-28 오후 3.58.33.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;/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;여기서 위의 검색창을 활용해 youtube api를 검색합니다. 여기서 YouTube Data API를 선택해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2434&quot; data-origin-height=&quot;1207&quot; data-filename=&quot;스크린샷 2021-05-28 오후 3.59.58(2).png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c14fu5/btq50ZB3xCd/Yfrvvh0ZwKXln7I44dJPSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c14fu5/btq50ZB3xCd/Yfrvvh0ZwKXln7I44dJPSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c14fu5/btq50ZB3xCd/Yfrvvh0ZwKXln7I44dJPSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc14fu5%2Fbtq50ZB3xCd%2FYfrvvh0ZwKXln7I44dJPSk%2Fimg.png&quot; data-origin-width=&quot;2434&quot; data-origin-height=&quot;1207&quot; data-filename=&quot;스크린샷 2021-05-28 오후 3.59.58(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;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 버튼을 클릭해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2438&quot; data-origin-height=&quot;1254&quot; data-filename=&quot;스크린샷 2021-05-28 오후 4.06.57.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qGQiS/btq5XLLteqB/avcKBJyeitaT1GYWmCDCrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qGQiS/btq5XLLteqB/avcKBJyeitaT1GYWmCDCrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qGQiS/btq5XLLteqB/avcKBJyeitaT1GYWmCDCrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqGQiS%2Fbtq5XLLteqB%2FavcKBJyeitaT1GYWmCDCrk%2Fimg.png&quot; data-origin-width=&quot;2438&quot; data-origin-height=&quot;1254&quot; data-filename=&quot;스크린샷 2021-05-28 오후 4.06.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;/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;사용버튼을 클릭하면 아래 화면으로 리다이렉트 됩니다. 여기서 사용자 인증 정보 만들기를 선택해주세요. 혹시 이미 사용자 인증 정보가 있는 경우인데 API key를 새로 발급 받고 싶으면 왼쪽 사이드바의 사용자 인증 정보(Credentials)를 클릭해주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2438&quot; data-origin-height=&quot;1492&quot; data-filename=&quot;스크린샷 2021-05-28 오후 4.08.32.png&quot; width=&quot;883&quot; height=&quot;540&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bueAqW/btq5XGJYZqR/a9sWJEA9iBHz2kMXMkFFz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bueAqW/btq5XGJYZqR/a9sWJEA9iBHz2kMXMkFFz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bueAqW/btq5XGJYZqR/a9sWJEA9iBHz2kMXMkFFz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbueAqW%2Fbtq5XGJYZqR%2Fa9sWJEA9iBHz2kMXMkFFz1%2Fimg.png&quot; data-origin-width=&quot;2438&quot; data-origin-height=&quot;1492&quot; data-filename=&quot;스크린샷 2021-05-28 오후 4.08.32.png&quot; width=&quot;883&quot; height=&quot;540&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;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2435&quot; data-origin-height=&quot;929&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wAU2j/btq5XKTkDSh/J6y8gVKr4oYYs3uAKMJDZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wAU2j/btq5XKTkDSh/J6y8gVKr4oYYs3uAKMJDZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wAU2j/btq5XKTkDSh/J6y8gVKr4oYYs3uAKMJDZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwAU2j%2Fbtq5XKTkDSh%2FJ6y8gVKr4oYYs3uAKMJDZ0%2Fimg.png&quot; data-origin-width=&quot;2435&quot; data-origin-height=&quot;929&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;위와 같이 API key를 발급 받을 수 있습니다. 키를 복사해서 사용하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유튜브 API 사용법은 &lt;a href=&quot;https://developers.google.com/youtube/v3/getting-started?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt;를 확인하시면 됩니다.&lt;/p&gt;
&lt;figure id=&quot;og_1622186292398&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;시작하기 &amp;nbsp;|&amp;nbsp; YouTube Data API &amp;nbsp;|&amp;nbsp; Google Developers&quot; data-og-description=&quot;소개 이 문서는 YouTube와 상호작용할 수 있는 애플리케이션을 개발하려는 개발자를 위해 작성되었습니다. 여기에서는 YouTube 및 API의 기본 개념에 대해 설명합니다. 또한 API가 지원하는 다양한 기&quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/youtube/v3/getting-started?hl=ko&quot; data-og-url=&quot;https://developers.google.com/youtube/v3/getting-started?hl=ko&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developers.google.com/youtube/v3/getting-started?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.google.com/youtube/v3/getting-started?hl=ko&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;nbsp;|&amp;nbsp; YouTube Data API &amp;nbsp;|&amp;nbsp; Google Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;소개 이 문서는 YouTube와 상호작용할 수 있는 애플리케이션을 개발하려는 개발자를 위해 작성되었습니다. 여기에서는 YouTube 및 API의 기본 개념에 대해 설명합니다. 또한 API가 지원하는 다양한 기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developers.google.com/youtube/v3/docs/?apix=true&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;API 공식문서 바로가기&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1622189052155&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;API Reference &amp;nbsp;|&amp;nbsp; YouTube Data API &amp;nbsp;|&amp;nbsp; Google Developers&quot; data-og-description=&quot;YouTube Data API를 사용하면 YouTube 웹사이트에서 일반적으로 실행하는 기능을 사용자의 웹사이트 또는 애플리케이션에 통합할 수 있습니다. 아래 목록에서는 API를 사용하여 검색할 수 있는 다양한 &quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/youtube/v3/docs/?apix=true&quot; data-og-url=&quot;https://developers.google.com/youtube/v3/docs?hl=ko&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developers.google.com/youtube/v3/docs/?apix=true&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.google.com/youtube/v3/docs/?apix=true&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;API Reference &amp;nbsp;|&amp;nbsp; YouTube Data API &amp;nbsp;|&amp;nbsp; Google Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;YouTube Data API를 사용하면 YouTube 웹사이트에서 일반적으로 실행하는 기능을 사용자의 웹사이트 또는 애플리케이션에 통합할 수 있습니다. 아래 목록에서는 API를 사용하여 검색할 수 있는 다양한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.google.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;딥러닝 모델 학습에 사용하기 위해 목표 데이터(영상) 개수를 10만개 이상으로 잡았습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 생각은 search.list API를 활용해 어떤 검색어를 입력했을 때 나오는 결과를 몇십만개를 저장하자는 것이었습니다. 그렇게 하니 최대 몇백개의 데이터 밖에 수집할 수 없었습니다. 그래서 아래와 같이 방식을 바꿨습니다.&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;/ol&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;a href=&quot;https://github.com/hits-hike/youtube-views-prediction-model/blob/main/data/collect_data_using_youtube_api.ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/hits-hike/youtube-views-prediction-model/blob/main/data/collect_data_using_youtube_api.ipynb&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622188950985&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;hits-hike/youtube-views-prediction-model&quot; data-og-description=&quot;Contribute to hits-hike/youtube-views-prediction-model development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/hits-hike/youtube-views-prediction-model/blob/main/data/collect_data_using_youtube_api.ipynb&quot; data-og-url=&quot;https://github.com/hits-hike/youtube-views-prediction-model&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ytnO9/hyKnjlKZJ3/NuAWIwKiJ9L5X7dq2OKzKk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/hits-hike/youtube-views-prediction-model/blob/main/data/collect_data_using_youtube_api.ipynb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/hits-hike/youtube-views-prediction-model/blob/main/data/collect_data_using_youtube_api.ipynb&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ytnO9/hyKnjlKZJ3/NuAWIwKiJ9L5X7dq2OKzKk/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;hits-hike/youtube-views-prediction-model&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to hits-hike/youtube-views-prediction-model development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 유튜브 API를 이용하기 위해 아래와 같이 초기 설정 코드를 입력해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;254&quot; width=&quot;570&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw6Fe7/btq518yZwLG/Vr65Fjb2rLG6qJ40x7LAHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw6Fe7/btq518yZwLG/Vr65Fjb2rLG6qJ40x7LAHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw6Fe7/btq518yZwLG/Vr65Fjb2rLG6qJ40x7LAHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw6Fe7%2Fbtq518yZwLG%2FVr65Fjb2rLG6qJ40x7LAHK%2Fimg.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;254&quot; width=&quot;570&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;깃허브에 코드를 업로드할 때 API key 노출을 주의해야합니다. 저는 같은 폴더에 config 파일을 만들고 해당 파일에 API key를 저장해두었습니다. 이후 .gitignore에 config파일을 넣어주었습니다. 로컬에서만 작업하신다면 developerKey = '위에서 발급 받은 키'로 해두셔도 무방합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저처럼 따로 작성하실 분은 이렇게 해주세요.&lt;/p&gt;
&lt;pre id=&quot;code_1622189906679&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ./config.py
API_KEY = '위에서 발급 받은 키를 여기 붙여넣으시면 됩니다'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;1252&quot; width=&quot;631&quot; height=&quot;608&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ch7gmK/btq520tqSSG/ZFDN50E0GKtHdIZbjNQkwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ch7gmK/btq520tqSSG/ZFDN50E0GKtHdIZbjNQkwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ch7gmK/btq520tqSSG/ZFDN50E0GKtHdIZbjNQkwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fch7gmK%2Fbtq520tqSSG%2FZFDN50E0GKtHdIZbjNQkwK%2Fimg.png&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;1252&quot; width=&quot;631&quot; height=&quot;608&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;search.list API를 사용해서 검색어를 '브이로그'로 설정했을 때 나오는 결과의 채널 아이디를 저장했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: blue;&quot;&gt;get_youtube_channel_search_list&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;keyword&lt;/span&gt;&lt;span&gt;)는 인자로 검색어를 받아 해당 검색어로 검색된 결과의 채널 아이디를 channel_list에 추가하는 함수입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 결과의 유형이 영상일 때는 해당 영상을 올린 채널 아이디를, 채널일 때는 그 채널 아이디를 추가해주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇개의 채널 아이디를 수집했는지 궁금해서 &lt;span style=&quot;color: green;&quot;&gt;print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #ba2121;&quot;&gt;'data num: '&lt;/span&gt; &lt;span style=&quot;color: #666666;&quot;&gt;+&lt;/span&gt; &lt;span style=&quot;color: green;&quot;&gt;str&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: green;&quot;&gt;len&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;channel_list&lt;/span&gt;&lt;span&gt;)))를 통해 확인했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복된 채널을 수집했을 수 있으니 중복된 채널 아이디는 하나만 저장하도록 &lt;span&gt;channel_list&lt;/span&gt; &lt;span style=&quot;color: #666666;&quot;&gt;=&lt;/span&gt; &lt;span&gt;channel_list&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;.&lt;/span&gt;&lt;span&gt;drop_duplicates&lt;/span&gt;&lt;span&gt;() 해주었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;1952&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkwrUP/btq52ig9a4r/FKp4YKXL7DZPZuofFtgWY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkwrUP/btq52ig9a4r/FKp4YKXL7DZPZuofFtgWY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkwrUP/btq52ig9a4r/FKp4YKXL7DZPZuofFtgWY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkwrUP%2Fbtq52ig9a4r%2FFKp4YKXL7DZPZuofFtgWY1%2Fimg.png&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;1952&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;span&gt;위에서 수집한 채널 아이디로 해당 채널에서 업로드한 모든 영상 정보를 가져오고자 했습니다. 위와 같이 두 단계로 나눠 진행했습니다. 두 단계로 나눠 진행한 이유는 search.list api가 잘 동작하지 않았기 때문입니다. 그래서 channels.list api를 활용해 수집된 채널들의 모든 플레이리스트 아이디를 받아왔습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;3번에서 플레이리스트 목록의 모든 영상 정보를 가져왔습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;search.list API 응답 리소스 구조는 아래와 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1622191368903&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;kind&quot;: &quot;youtube#searchResult&quot;,
  &quot;etag&quot;: etag,
  &quot;id&quot;: {
    &quot;kind&quot;: string,
    &quot;videoId&quot;: string,
    &quot;channelId&quot;: string,
    &quot;playlistId&quot;: string
  },
  &quot;snippet&quot;: {
    &quot;publishedAt&quot;: datetime,
    &quot;channelId&quot;: string,
    &quot;title&quot;: string,
    &quot;description&quot;: string,
    &quot;thumbnails&quot;: {
      (key): {
        &quot;url&quot;: string,
        &quot;width&quot;: unsigned integer,
        &quot;height&quot;: unsigned integer
      }
    },
    &quot;channelTitle&quot;: string
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;저희는 모델에 활용할 정보로 제목, 업로드 시각을 활용할 것이라 해당 정보를 저장했습니다. 추후에도 해당 영상을 구분해주기 위해 영상 아이디, 채널명, 채널 아이디를 추가로 저장해주었습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;(위 데이터 수집 코드를 작성할 시기엔 우선적으로 제목에 대한 조회수 예측만 하고자 했기 때문에 썸네일을 따로 저장해주지 않았습니다. 이후에 썸네일만 따로 수집하는 모듈을 만들었습니다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위 search.list API로는 조회수가 같이 응답으로 오지 않기 때문에 영상마다 조회수를 찾아 추가하기 위해 videos.list API를 사용했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;760&quot; width=&quot;710&quot; height=&quot;359&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nIdfE/btq5Y7norkc/KciLHGu1mPpp3ids9CE4Ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nIdfE/btq5Y7norkc/KciLHGu1mPpp3ids9CE4Ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nIdfE/btq5Y7norkc/KciLHGu1mPpp3ids9CE4Ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnIdfE%2Fbtq5Y7norkc%2FKciLHGu1mPpp3ids9CE4Ok%2Fimg.png&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;760&quot; width=&quot;710&quot; height=&quot;359&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;span&gt;조회수(views)를 함께 저장했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;dropna를 통해 결측값을 제거했고, 조회수가 제대로 수집되지 않은 영상들을 제거해주었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이후 to_csv 함수를 통해 수집한 데이터를 csv형태로 저장했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;05&quot; data-ke-size=&quot;size23&quot;&gt;썸네일에서 사람 얼굴을 인식해 feature를 추출&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;썸네일에서 어떤 요소가 사람들의 이목을 끌고 시청을 유도할지 고민했습니다. 고민 결과 썸네일에 사람이 등장한다면 해당 사람의 특징, 유명인 여부, 썸네일에서 사람이 차지하는 비율(크기) 등이 좌우할 것으로 추측했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저희 팀에서 자체적으로 얼굴 인식을 구현할 수도 있었지만 기존 API를 활용하는 것이 정확도나 수집할 수 있는 데이터 측면에서 더 나을 것 같다는 조언을 듣고 아마존의 &lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/what-is.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;rekognition API&lt;/a&gt;를 사용하기로 결정했습니다.&lt;/p&gt;
&lt;figure id=&quot;og_1622193844428&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;What is Amazon Rekognition? - Amazon Rekognition&quot; data-og-description=&quot;What is Amazon Rekognition? Amazon Rekognition makes it easy to add image and video analysis to your applications. You just provide an image or video to the Amazon Rekognition API, and the service can identify objects, people, text, scenes, and activities.&quot; data-og-host=&quot;docs.aws.amazon.com&quot; data-og-source-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/what-is.html&quot; data-og-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/what-is.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/what-is.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/what-is.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;What is Amazon Rekognition? - Amazon Rekognition&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What is Amazon Rekognition? Amazon Rekognition makes it easy to add image and video analysis to your applications. You just provide an image or video to the Amazon Rekognition API, and the service can identify objects, people, text, scenes, and activities.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.aws.amazon.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;썸네일에서 유명인 인식을 하는 기능도 있었으나, 한국 셀럽들을 제대로 인식하지 못하기도 하고 저희 데이터가 십만개가 조금 넘는 수준이기 때문에 현 상황에서 유명인을 하나의 feature로 두는 것은 무리가 있다고 판단해 현재는 유명인 판단을 하고 있지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;썸네일에서 얼굴이 썸네일에서 차지하는 면적, 나오는 사람 수, 얼굴로 파악된 사람의 나이, 성별, 표정 등이 사람들이 썸네일을 보고 시청할지 안할지에 영향을 미친다고 생각해 해당 정보를 Amazon Rekognition &amp;gt; Detect Faces를 통해 수집했습니다.&lt;/p&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를 사용하기 위해서는 일단 계정 가입을 해야합니다. 가입하고 로그인을 합니다. 이후 서비스에서 Machine Learning &amp;gt; Amazon Rekognition을 선택해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ap-northeast-2.console.aws.amazon.com/rekognition/home?region=ap-northeast-2#/&quot;&gt;https://ap-northeast-2.console.aws.amazon.com/rekognition/home?region=ap-northeast-2#/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1622202630015&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;https://ap-northeast-2.console.aws.amazon.com/rekognition/home?region=ap-northeast-2#/&quot; data-og-description=&quot;&quot; data-og-host=&quot;ap-northeast-2.console.aws.amazon.com&quot; data-og-source-url=&quot;https://ap-northeast-2.console.aws.amazon.com/rekognition/home?region=ap-northeast-2#/&quot; data-og-url=&quot;https://ap-northeast-2.console.aws.amazon.com/rekognition/home?region=ap-northeast-2#/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ap-northeast-2.console.aws.amazon.com/rekognition/home?region=ap-northeast-2#/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ap-northeast-2.console.aws.amazon.com/rekognition/home?region=ap-northeast-2#/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;https://ap-northeast-2.console.aws.amazon.com/rekognition/home?region=ap-northeast-2#/&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ap-northeast-2.console.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1734&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZAC5h/btq53kFORC3/yKCAJu3rS3lXCHOuxKCGbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZAC5h/btq53kFORC3/yKCAJu3rS3lXCHOuxKCGbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZAC5h/btq53kFORC3/yKCAJu3rS3lXCHOuxKCGbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZAC5h%2Fbtq53kFORC3%2FyKCAJu3rS3lXCHOuxKCGbk%2Fimg.png&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1734&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 여기서 얼굴 분석(Detecting and Analyzing Faces)를 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/faces.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/rekognition/latest/dg/faces.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622202918511&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;Detecting and analyzing faces - Amazon Rekognition&quot; data-og-description=&quot;Detecting and analyzing faces Amazon Rekognition can detect faces in images and videos. This section covers non-storage operations for analyzing faces. With Amazon Rekognition, you can get information about where faces are detected in an image or video, fa&quot; data-og-host=&quot;docs.aws.amazon.com&quot; data-og-source-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/faces.html&quot; data-og-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/faces.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/faces.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/faces.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;Detecting and analyzing faces - Amazon Rekognition&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Detecting and analyzing faces Amazon Rekognition can detect faces in images and videos. This section covers non-storage operations for analyzing faces. With Amazon Rekognition, you can get information about where faces are detected in an image or video, fa&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/API_DetectFaces.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;API_DetectFaces API 문서 바로가기&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622203229363&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;DetectFaces - Amazon Rekognition&quot; data-og-description=&quot;This is a stateless API operation. That is, the operation does not persist any data.&quot; data-og-host=&quot;docs.aws.amazon.com&quot; data-og-source-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/API_DetectFaces.html&quot; data-og-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/API_DetectFaces.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/API_DetectFaces.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/API_DetectFaces.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;DetectFaces - Amazon Rekognition&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This is a stateless API operation. That is, the operation does not persist any data.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.aws.amazon.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;먼저 아마존 웹 서비스를 사용하기 위해서는 계정을 만들고 IAM 사용자를 생성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 AWS CLI를 다운받습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/setup-awscli-sdk.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/rekognition/latest/dg/setup-awscli-sdk.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622203773481&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;Step 2: Set up the AWS CLI and AWS SDKs - Amazon Rekognition&quot; data-og-description=&quot;If you don't select a region, then us-east-1 will be used by default.&quot; data-og-host=&quot;docs.aws.amazon.com&quot; data-og-source-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/setup-awscli-sdk.html&quot; data-og-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/setup-awscli-sdk.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/setup-awscli-sdk.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.aws.amazon.com/rekognition/latest/dg/setup-awscli-sdk.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;Step 2: Set up the AWS CLI and AWS SDKs - Amazon Rekognition&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;If you don't select a region, then us-east-1 will be used by default.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여기서 시키는 대로 설정해주면 됩니다. config 파일에 aws_access_key_id와&amp;nbsp;aws_secret_access_key를 설정했으면 이젠 aws api를 사용할 준비가 된 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다음은 aws rekognition.detect_faces api 사용 코드입니다. 아마존 api docs에는 S3 bucket에 이미지를 업로드해서 사용하라고 나와있으나 그냥 이미지 url을 아래처럼 인풋으로 넣어줘도 상관 없습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1622204073924&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import boto3
from urllib.request import urlopen
from PIL import Image 
 
client=boto3.client('rekognition')

response = client.detect_faces(
	Image={'Bytes': urlopen(url).read()},
	Attributes=['ALL']
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Response Syntax(응답 구조)입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1622205611855&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
   &quot;FaceDetails&quot;: [ 
      { 
         &quot;AgeRange&quot;: { 
            &quot;High&quot;: number,
            &quot;Low&quot;: number
         },
         &quot;Beard&quot;: { 
            &quot;Confidence&quot;: number,
            &quot;Value&quot;: boolean
         },
         &quot;BoundingBox&quot;: { 
            &quot;Height&quot;: number,
            &quot;Left&quot;: number,
            &quot;Top&quot;: number,
            &quot;Width&quot;: number
         },
         &quot;Confidence&quot;: number,
         &quot;Emotions&quot;: [ 
            { 
               &quot;Confidence&quot;: number,
               &quot;Type&quot;: &quot;string&quot;
            }
         ],
         &quot;Eyeglasses&quot;: { 
            &quot;Confidence&quot;: number,
            &quot;Value&quot;: boolean
         },
         &quot;EyesOpen&quot;: { 
            &quot;Confidence&quot;: number,
            &quot;Value&quot;: boolean
         },
         &quot;Gender&quot;: { 
            &quot;Confidence&quot;: number,
            &quot;Value&quot;: &quot;string&quot;
         },
         &quot;Landmarks&quot;: [ 
            { 
               &quot;Type&quot;: &quot;string&quot;,
               &quot;X&quot;: number,
               &quot;Y&quot;: number
            }
         ],
         &quot;MouthOpen&quot;: { 
            &quot;Confidence&quot;: number,
            &quot;Value&quot;: boolean
         },
         &quot;Mustache&quot;: { 
            &quot;Confidence&quot;: number,
            &quot;Value&quot;: boolean
         },
         &quot;Pose&quot;: { 
            &quot;Pitch&quot;: number,
            &quot;Roll&quot;: number,
            &quot;Yaw&quot;: number
         },
         &quot;Quality&quot;: { 
            &quot;Brightness&quot;: number,
            &quot;Sharpness&quot;: number
         },
         &quot;Smile&quot;: { 
            &quot;Confidence&quot;: number,
            &quot;Value&quot;: boolean
         },
         &quot;Sunglasses&quot;: { 
            &quot;Confidence&quot;: number,
            &quot;Value&quot;: boolean
         }
      }
   ],
   &quot;OrientationCorrection&quot;: &quot;string&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전 여기서 AgeRange, Gender, BoundingBox, Emotions 정보를 활용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;아래는 이미지 url을 인풋으로 받아 aws rekognition.detect_faces api를 사용해 썸네일에서 얼굴이 썸네일에서 차지하는 면적, 나오는 사람 수, 얼굴로 파악된 사람의 나이, 성별, 표정을 반환하는 함수입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1622204331906&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def detect_faces(url):

    client=boto3.client('rekognition')

    try:
        response = client.detect_faces(
            Image={'Bytes': urlopen(url).read()},
            Attributes=['ALL']
        )
    except:
        return None
    
    people_num = len(response['FaceDetails'])
    
    # 감지된 얼굴이 없는 경우
    if people_num == 0:
        return [int(people_num), -1, -1, -1, '', '']
    
    # 가장 큰 얼굴 하나만 데이터로 저장
    max_height = 0
    
    for faceDetail in response['FaceDetails']:
        # size
        height = faceDetail['BoundingBox']['Height']
        
        if height &amp;gt; max_height:
            max_height = height
        else: 
            continue
        
        width = faceDetail['BoundingBox']['Width']
        
        # age는 예상 나이 범위의 (low+high)/2
        age = (faceDetail['AgeRange']['Low'] + faceDetail['AgeRange']['High']) / 2
        
        # size
        width = faceDetail['BoundingBox']['Width']
        height = faceDetail['BoundingBox']['Height']
        
        # emotion
        emotion_confidence = 0
        emotion = ''
        
        for item in faceDetail['Emotions']:
            if item['Confidence'] &amp;gt; emotion_confidence:
                emotion_confidence = item['Confidence']
                emotion = item['Type']
        
        # gender
        gender = faceDetail['Gender']['Value']
    
    
    data = [int(people_num), int(age), width, height, emotion, gender]
    return data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 없는 경우 사람수로 0을, 예상 나이/너비/높이는 -1을, 감정과 성별은 빈 문자열을 반환해 주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 여러명 나오는 경우 여러명의 데이터를 다 저장해 feature로 활용할 수 있었지만 그렇게 되면 feature가 너무 많아질 수 있고 모델 학습에 무리가 있을 것이라 판단했습니다. 따라서 가장 면적을 크게 차지하는(얼굴의 높이가 가장 긴) 사람 한 명의 얼굴에 대해서만 데이터를 분석했습니다. 해당 얼굴의 예상 나이는 예상 나이 범위의 평균을 내서 저장했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 url이 제대로 오픈되지 않는 등의 에러가 나면 api가 에러를 반환합니다. 이 때는 함수에서 None을 반환하도록 했습니다. 이후 None인 값들은 제거하고 모델에 학습시킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마존 서비스는 유료입니다. 참고로, 이 서비스의 프리티어 사용량은 한달에 최대 5000 이미지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 가난한 대학생이기 때문에 과금이 무섭습니다. 왜 학교에서는 후불 서비스에 대해서는 비용 지원을 해주지 않고, 아마존에서는 크레딧을 학교에 팔지 않는 걸까요? 무척 아쉽습니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 그렇기 때문에 함부로 모든 이미지에 대해 api 요청을 보낼 수는 없습니다. 그래서 영상 아이디와 썸네일이 저장된 데이터프레임의 모든 아이템이 아니라 시작 인덱스와 끝 인덱스를 받아 df.loc[start_index] ~ df.loc[end_index - 1]까지의 데이터에 대해서만 얼굴 인식을 할 수 있도록 함수를 만들었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1622204781387&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_data(start_index, end_index):
    if end_index &amp;gt; len(df):
        end_index = len(df)
    for index in range(start_index, end_index):
        url = df['thumbnail'][index]
        data = detect_faces(url)
        df.loc[index, ['people_num', 'age', 'width', 'height', 'emotion', 'gender']] = data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터프레임 df 구조입니다. api를 통해 잘 수집된 것을 확인할 수 있습니다. 상위 10000개의 데이터에 대해서만 수집했습니다. 수집하기 전에 조회수가 1000 미만인 영상들은 목록에서 제거했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1964&quot; data-origin-height=&quot;630&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kIfWf/btq52Ah5UnB/iftZiwTKRI5gJ8Ll4kJJpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kIfWf/btq52Ah5UnB/iftZiwTKRI5gJ8Ll4kJJpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kIfWf/btq52Ah5UnB/iftZiwTKRI5gJ8Ll4kJJpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkIfWf%2Fbtq52Ah5UnB%2FiftZiwTKRI5gJ8Ll4kJJpK%2Fimg.png&quot; data-origin-width=&quot;1964&quot; data-origin-height=&quot;630&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;06&quot; data-ke-size=&quot;size23&quot;&gt;얼굴 인식 데이터를 바탕으로 조회수를 예측하는 모델 개발&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;336&quot; width=&quot;524&quot; height=&quot;145&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ0lkZ/btq54qr2pRZ/Z9sUCi0u9TXJNUGgXqvSPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ0lkZ/btq54qr2pRZ/Z9sUCi0u9TXJNUGgXqvSPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ0lkZ/btq54qr2pRZ/Z9sUCi0u9TXJNUGgXqvSPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ0lkZ%2Fbtq54qr2pRZ%2FZ9sUCi0u9TXJNUGgXqvSPk%2Fimg.png&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;336&quot; width=&quot;524&quot; height=&quot;145&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;해당 모델에 필요한 라이브러리 등을 import합니다.&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;2038&quot; data-origin-height=&quot;1074&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mKomi/btq54oVhLPo/URwlYQA4NkgiHc93odo0CK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mKomi/btq54oVhLPo/URwlYQA4NkgiHc93odo0CK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mKomi/btq54oVhLPo/URwlYQA4NkgiHc93odo0CK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmKomi%2Fbtq54oVhLPo%2FURwlYQA4NkgiHc93odo0CK%2Fimg.png&quot; data-origin-width=&quot;2038&quot; data-origin-height=&quot;1074&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;span&gt;&amp;nbsp;위에서 수집해 csv형태로 저장한 데이터를 모델에 학습시키기 위해 불러왔습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;학습시키기 전에 전처리부터 했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;얼굴 인식을 통해 데이터를 추출한 10000개만 남겨주었습니다. 또한 에러 등으로 인해 생긴 결측값을 제거해주었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;영상 아이디와 썸네일 주소는 학습에 쓰이지 않는 column이기 때문에 제거했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1622206639012&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 데이터 수집한 10000개에 대해서만
df = df.head(10000)

# 결측값 있는 행 제거
df = df.dropna(subset=['people_num'])

# 필요없는 열 제거
df = df.drop(columns = ['video_id', 'thumbnail'])
df&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 전처리코드를 수행한 결과 df입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;770&quot; width=&quot;639&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cb1ZeE/btq50kN3F8Q/H2OZpRGo1tcIBONRlWZQg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cb1ZeE/btq50kN3F8Q/H2OZpRGo1tcIBONRlWZQg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cb1ZeE/btq50kN3F8Q/H2OZpRGo1tcIBONRlWZQg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcb1ZeE%2Fbtq50kN3F8Q%2FH2OZpRGo1tcIBONRlWZQg1%2Fimg.png&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;770&quot; width=&quot;639&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전 RandomForestRegressor을 통해 조회수 예측을 할 것이기 때문에 문자열로 되어 있는 emotion과 gender를 수치 데이터로 바꿔줘야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수치 데이터로 변환할 때 주의할 점이 있습니다. 예를 들어 gender 값을 Male = 1, Female = 2로 변환할 경우 RandomForestRegressor가 연속적인 데이터로 인식하기 때문에 여성이 남성보다 더 높은 값을 갖는다고 생각합니다. 이 경우 결과가 이상하게 나올 수 있기 때문에 원핫인코딩 작업을 해주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;판다스의 get_dummies를 이용하면 쉽게 가능합니다. 아래와 같이 작업해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2226&quot; data-origin-height=&quot;984&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pNgIV/btq52covt0B/yguXBm5fUvJAVPuBbal3P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pNgIV/btq52covt0B/yguXBm5fUvJAVPuBbal3P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pNgIV/btq52covt0B/yguXBm5fUvJAVPuBbal3P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpNgIV%2Fbtq52covt0B%2FyguXBm5fUvJAVPuBbal3P1%2Fimg.png&quot; data-origin-width=&quot;2226&quot; data-origin-height=&quot;984&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;감정과 성별에 None값이 들어가 있는 것도 제거하지 않고 dummy_na=True 옵션을 통해 따로 처리해주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;262&quot; width=&quot;671&quot; height=&quot;117&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dia26B/btq51QeUh5D/GrLrejhMNlYgPZKLE31rmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dia26B/btq51QeUh5D/GrLrejhMNlYgPZKLE31rmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dia26B/btq51QeUh5D/GrLrejhMNlYgPZKLE31rmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdia26B%2Fbtq51QeUh5D%2FGrLrejhMNlYgPZKLE31rmK%2Fimg.png&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;262&quot; width=&quot;671&quot; height=&quot;117&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;학습 데이터 셋과 테스트용 데이터 셋 비율을 8대 2로 나누어 주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자, RandomForestRegressor를 사용해 조회수를 한 번 예측해봅시다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;414&quot; width=&quot;406&quot; height=&quot;242&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pNthf/btq53vHo8d8/8cWZq5XunHrAEjuYEA1fOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pNthf/btq53vHo8d8/8cWZq5XunHrAEjuYEA1fOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pNthf/btq53vHo8d8/8cWZq5XunHrAEjuYEA1fOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpNthf%2Fbtq53vHo8d8%2F8cWZq5XunHrAEjuYEA1fOK%2Fimg.png&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;414&quot; width=&quot;406&quot; height=&quot;242&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 성능을 개선하기 위해서 학습시킬 데이터를 더 수집할 계획입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 사람이 없을 때 나이, 너비, 높이 기본 값을 -1로 준 것이 영향이 있을까 싶어 0으로 바꿔서도 한 번 테스트 해볼 계획입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 조회수 숫자 자체를 예측하는데 조회수 구간을 나누어 분류하는 모델로 변경하면 조회수를 더 잘 예측할 수 있을지 알아보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;07&quot; data-ke-size=&quot;size23&quot;&gt;프로토타입 개발&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hits-hike.vercel.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hits-hike.vercel.app/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1622208034053&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;hits-hike.vercel.app&quot; data-og-source-url=&quot;https://hits-hike.vercel.app/&quot; data-og-url=&quot;https://hits-hike.vercel.app/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://hits-hike.vercel.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hits-hike.vercel.app/&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;히츠하이크&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;hits-hike.vercel.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저희 프로젝트의 프로토타입 url입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1622208137718&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx create-next-app&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;create-next-app으로 nextjs app을 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 타입스크립트, prettier, eslint, babel 설정 등을 해줬고, vercel을 통해 배포했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1622208461140&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;hits-hike/hits-hike-web&quot; data-og-description=&quot;Contribute to hits-hike/hits-hike-web development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/hits-hike/hits-hike-web&quot; data-og-url=&quot;https://github.com/hits-hike/hits-hike-web&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/boS3Gs/hyKnlqwz9A/JuKLBV6Eu7TwmfmxotpHOK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/hits-hike/hits-hike-web&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/hits-hike/hits-hike-web&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/boS3Gs/hyKnlqwz9A/JuKLBV6Eu7TwmfmxotpHOK/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;hits-hike/hits-hike-web&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to hits-hike/hits-hike-web 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;&lt;a href=&quot;https://github.com/hits-hike/hits-hike-web&quot;&gt;https://github.com/hits-hike/hits-hike-web&lt;/a&gt;에 가셔서 git clone을 통해 다운 받으신 후&lt;/p&gt;
&lt;pre id=&quot;code_1622208349115&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install
npm run dev&lt;/code&gt;&lt;/pre&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-origin-width=&quot;2764&quot; data-origin-height=&quot;1900&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pmT8Y/btq52p2cniQ/FXtgA1IzBQToqU9jBqKFn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pmT8Y/btq52p2cniQ/FXtgA1IzBQToqU9jBqKFn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pmT8Y/btq52p2cniQ/FXtgA1IzBQToqU9jBqKFn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpmT8Y%2Fbtq52p2cniQ%2FFXtgA1IzBQToqU9jBqKFn0%2Fimg.png&quot; data-origin-width=&quot;2764&quot; data-origin-height=&quot;1900&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;프로토타입의 현재 UI입니다. 지금은 제목 추천 받기 UI만 구현되어 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;곧 썸네일 추천 받기 UI를 구현하고 전체적인 UI를 개선할 계획입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 좋은 결과있길 기대하면서 열심히 프로젝트를 진행해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;궁금한 것 혹은 조언해주실 부분이 있다면 언제든 댓글 달아주세요! 감사합니다 &lt;/p&gt;</description>
      <category>회고/프로젝트</category>
      <category>AWS</category>
      <category>youtube</category>
      <category>딥러닝</category>
      <category>머신러닝</category>
      <category>얼굴인식</category>
      <category>유튜브</category>
      <category>졸업프로젝트</category>
      <category>졸프</category>
      <author>개발하는 크롱</author>
      <guid isPermaLink="true">https://crong-dev.tistory.com/68</guid>
      <comments>https://crong-dev.tistory.com/68#entry68comment</comments>
      <pubDate>Fri, 28 May 2021 22:41:26 +0900</pubDate>
    </item>
    <item>
      <title>코드 포매터 Prettier 설명 및 설치 방법 (+저장 시 자동 포맷 설정 방법)</title>
      <link>https://crong-dev.tistory.com/67</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 포매터(Code Formatter), Prettier가 무엇인가요?&lt;/h2&gt;
&lt;p&gt;코드 포매터란 정한 코딩 컨벤션(코드 작성 스타일 규칙)에 따라 코드 스타일을 알아서 정리해주는 도구입니다.&lt;/p&gt;
&lt;p&gt;Prettier는 VSCode Extension으로, 코드 포매터입니다.&lt;/p&gt;
&lt;p&gt;코딩 컨벤션을 정하고 코드 포매터를 활용하면 아래 밈처럼 두 개발자 사이의 시빌워가 생기지 않습니다.&lt;/p&gt;
&lt;p&gt;정한 코딩 컨벤션에 맞춰 알아서 포맷팅해주기 때문에 코딩 컨벤션을 지키지 않아 다른 개발자를 화나게 하는 일 혹은 추후에 수정해야하는 노고를 줄여줍니다.&lt;/p&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; width=&quot;360&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pjW6v/btq3n9lT6fZ/uteibDO88CGhynRVM0cCA1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pjW6v/btq3n9lT6fZ/uteibDO88CGhynRVM0cCA1/img.jpg&quot; data-alt=&quot;코딩 컨벤션으로 인한 두 개발자 사이의 시빌워&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pjW6v/btq3n9lT6fZ/uteibDO88CGhynRVM0cCA1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpjW6v%2Fbtq3n9lT6fZ%2FuteibDO88CGhynRVM0cCA1%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;360&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;코딩 컨벤션으로 인한 두 개발자 사이의 시빌워&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Prettier 설치 및 설정 방법 (+저장 시 자동 정렬 설정)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;805&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvFfRq/btq3nIvneu3/5XdonDryIhkhbYBptpkwTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvFfRq/btq3nIvneu3/5XdonDryIhkhbYBptpkwTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvFfRq/btq3nIvneu3/5XdonDryIhkhbYBptpkwTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvFfRq%2Fbtq3nIvneu3%2F5XdonDryIhkhbYBptpkwTK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;805&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;좌측 메뉴바의 Extensions를 클릭합니다. 이후 Prettier를 검색해 설치합니다.&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; width=&quot;818&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUWmTP/btq3nS5G9Kv/boZP6ckateOYJEzbsNVCeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUWmTP/btq3nS5G9Kv/boZP6ckateOYJEzbsNVCeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUWmTP/btq3nS5G9Kv/boZP6ckateOYJEzbsNVCeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUWmTP%2Fbtq3nS5G9Kv%2FboZP6ckateOYJEzbsNVCeK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;818&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;저는 이미 설치되어 있어 &quot;Uninstall&quot;이 뜨지만 처음 설치하신다면 &quot;Install&quot;이라고 뜨실 겁니다. 해당 버튼을 눌러 설치하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;설치했다면 원하는 대로 코딩 스타일을 지정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;첫번째 방법은 Settings에 들어가서 지정하는 방법입니다. 설정에 들어가서 Prettier 검색을 하면 Prettier 관련 설정화면이 뜹니다. 아래 화면에서 원하는 대로 설정할 수 있습니다.&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/LKKOJ/btq3klht4Az/i8lkA7rwysxqc8dUD7oQRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LKKOJ/btq3klht4Az/i8lkA7rwysxqc8dUD7oQRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LKKOJ/btq3klht4Az/i8lkA7rwysxqc8dUD7oQRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLKKOJ%2Fbtq3klht4Az%2Fi8lkA7rwysxqc8dUD7oQRk%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;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;두번째 방법은 Prettier configuration 파일을 통해 관리하는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;.prettierrc.json 혹은 .prettierrc.js 등으로 관리합니다. &lt;span style=&quot;color: #333333;&quot;&gt;파일 예시는 다음과 같습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1619177116445&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// .prettierrc.json
{
  &quot;trailingComma&quot;: &quot;es5&quot;,
  &quot;tabWidth&quot;: 2,
  &quot;semi&quot;: true,
  &quot;singleQuote&quot;: false,
  &quot;bracketSpacing&quot;: true,
  &quot;printWidth&quot;: 80
}

// prettier.config.js or .prettierrc.js
module.exports = {
  &quot;trailingComma&quot;: &quot;es5&quot;,
  &quot;tabWidth&quot;: 2,
  &quot;semi&quot;: true,
  &quot;singleQuote&quot;: false,
  &quot;bracketSpacing&quot;: true,
  &quot;printWidth&quot;: 80
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;설정에서 아래와 같이 경로 설정을 해줍니다.&lt;/p&gt;
&lt;p&gt;.prettierignore을 통해 Prettier의 영향을 받지 않는 파일도 별도로 지정가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;570&quot; height=&quot;NaN&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/bIL8Nw/btq3nbSdDj2/G6gyRMcPnG0y0YhhrizPqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIL8Nw/btq3nbSdDj2/G6gyRMcPnG0y0YhhrizPqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIL8Nw/btq3nbSdDj2/G6gyRMcPnG0y0YhhrizPqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIL8Nw%2Fbtq3nbSdDj2%2FG6gyRMcPnG0y0YhhrizPqK%2Fimg.png&quot; width=&quot;570&quot; height=&quot;NaN&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;마지막으로, 저장 시 자동으로 정렬하도록 설정하는 방법입니다.&lt;/p&gt;
&lt;p&gt;Default Formatter를 Prettier로 설정한 다음 Format on Save 설정을 체크해주세요. 저장 이외에도 타이핑했을 때마다 붙여넣기 할 때마다 자동으로 포맷되도록 설정할 수 있습니다. 저장 시 자동 포맷팅 기능을 활용하면 편하게 개발이 가능합니다. 그럼 모두 즐코딩 :D&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/P3OMD/btq3nh5SjrR/Qj6XEJ7l1cJnY7K0dnJBf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P3OMD/btq3nh5SjrR/Qj6XEJ7l1cJnY7K0dnJBf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P3OMD/btq3nh5SjrR/Qj6XEJ7l1cJnY7K0dnJBf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP3OMD%2Fbtq3nh5SjrR%2FQj6XEJ7l1cJnY7K0dnJBf1%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;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;+) 혹시 설정이 잘 안 먹힌다면 일단 껐다 켜보세요&lt;/p&gt;</description>
      <category>etc.</category>
      <category>extension</category>
      <category>Prettier</category>
      <category>vscode</category>
      <category>코드포매터</category>
      <author>개발하는 크롱</author>
      <guid isPermaLink="true">https://crong-dev.tistory.com/67</guid>
      <comments>https://crong-dev.tistory.com/67#entry67comment</comments>
      <pubDate>Fri, 23 Apr 2021 20:34:50 +0900</pubDate>
    </item>
    <item>
      <title>SW마에스트로 12기 코딩테스트 및 면접 회고</title>
      <link>https://crong-dev.tistory.com/66</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;드디어 소프트웨어 마에스트로 12기 선발의 전 과정이 끝났다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;SW마에스트로(이하 소마) 12기를 뽑는 과정은 아래와 같이 이루어 졌다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;참고로 전형별 결과는 바로 다음 금요일에 알려줬다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthContent&quot; data-origin-width=&quot;8192&quot; data-origin-height=&quot;3447&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wt8Zj/btq1XidJEIJ/h7AcFHckpcJ8IjYyA9zYKK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wt8Zj/btq1XidJEIJ/h7AcFHckpcJ8IjYyA9zYKK/img.jpg&quot; data-alt=&quot;sw마에스트로 12기 모집 일정 안내&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wt8Zj/btq1XidJEIJ/h7AcFHckpcJ8IjYyA9zYKK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWt8Zj%2Fbtq1XidJEIJ%2Fh7AcFHckpcJ8IjYyA9zYKK%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;8192&quot; height=&quot;3447&quot; data-origin-width=&quot;8192&quot; data-origin-height=&quot;3447&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;sw마에스트로 12기 모집 일정 안내&lt;/figcaption&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;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2월 18일까지 서류 접수&lt;/li&gt;
&lt;li&gt;2월 27일 1차 코딩테스트&lt;/li&gt;
&lt;li&gt;3월 13일 2차 코딩테스트&lt;/li&gt;
&lt;li&gt;3월 26일 ~ 28일 심층 면접&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 서류 접수&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;서류 접수 단계에서는 인적사항, PC활용능력, 자기소개서 등을 제출하게 된다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;자기소개서 문항은 바뀌지 않는 것 같으니 소마에 지원할 계획이 있다면 미리 적어두는 것도 좋을 것 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;문항은 다음과 같다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(*필수) [자기소개1] 소프트웨어분야 전문성을 키우기 위해 남들과 달리 특별한 노력을 한 경험을 서술해 주시기 바랍니다. (최소 400자, 최대 3,000자 입력가능)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(*필수) [자기소개2] 귀하의 장래희망을 서술하여 주시기 바랍니다. (최소 400자, 최대 3,000자 입력가능)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(*필수) [연수계획1] 귀하께서는 2021년도 「SW마에스트로」 연수과정에서 동료 연수생들과 협력하여 새로운 프로젝트를 완성하여야 합니다. 어떠한 능력을 갖춘 연수생들과 어떠한 프로젝트를 어떻게 수행할 것인지 귀하의 구체적인 계획을 서술하여 주시기 바랍니다. (최소 400자, 최대 3,000자 입력가능)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(*필수) [연수계획2] 2021년도 「SW마에스트로」 연수 과정에서는 연수생에게 교육지원비, 노트북PC 및 프로젝트 재료비 등을 지원할 예정이며, 본인이 희망하는 온&amp;middot;오프라인 교육과 프로젝트 수행을 도와주는 멘토를 통해 귀하의 SW능력을 향상&amp;middot;발전할 수 있는 기회를 제공하고 있습니다. 앞으로 귀하께서 본 과정을 통해 이루고자 하는 목표가 무엇인지 구체적으로 서술하여 주시기 바랍니다. (최소 400자, 최대 3,000자 입력가능)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;각 문항당 공백 미포함 3000자라는 커다란 압박에 시달렸다. 공백 미포함 만이천자라니. 물론 최소 400자만 적어도 제출은 가능하지만 이런 활동 지원할 때는 최대한 적을 수 있는 만큼 적는 것이 좋다 보니 적는 것이 부담스러웠다. 첫 문항을 굉장히 오래 적었고 마감 날이 다가와서야 나머지 문항을 겨우 적었다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;당연히 각 문항당 3000자씩 꼭 안 채워도 된다. 주변에 합격하신 분들도 보면 짧게 적은 경우(1000자 내외)도 많다. 특히 그냥 양 늘리려고 아무말하는 것은 당연히 추천하지 않는다. 그래도 할 말이 있다면 최대한 풀어내는 것이 좋다. 본인은 문항당 공백 미포함 1500자~2000자 내외로 작성했다. 본인은 서술식으로 적었는데 개조식으로 기술하신 분도 계시더라.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지원서 작성하는 데 꽤 오래 걸리니 미리미리 작성하는 것을 추천드린다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;서류 전형은 제대로 제출만 하면 합격하는 것으로 알고 있다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 1차 코딩테스트&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;1차 코딩테스트는 알고리즘 문제 6개, SQL 1문제, 웹 1문제로 출제되었다. 응시 시간이 총 2시간이라 8문제를 풀기에는 촉박했다. elice 플랫폼을 사용해 진행되었는데 시험 시작할 때 안 들어가지고, 문제 로딩이 안되는 이슈가 있어서 꽤 당황했다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;일단 첫 문제부터 막혀서 당황했다. 또 다른 문제에서도 범위 처리가 제대로 안돼 무한 루프에 빠졌는데 그거 디버깅하느라 시간을 많이 썼다. 전체적으로 크게 어렵다는 느낌은 아니었는데 막상 풀다보니 '어, 이거 이렇게 하는게 아닌데' 하며 다시 풀어야하는게 조금 있었다. 나오는 알고리즘은 중요한 알고리즘(코테에 많이 나오는 알고리즘) 위주로 나왔는데 한 문제에 여러 알고리즘을 적용해야하는 문제가 많았던 것 같다. 난 생각보다 어려웠다. SQL과 Web은 평이한 수준으로 출제됐다. 특히 웹은 css 질문이었는데 평소에 프론트 개발을 해서 그런지 그냥 점수 주는 문제처럼 느껴졌다. 총 8문제 중에 5문제 풀었는데 채점 결과를 제공해 주지 않기 때문에 얼마나 맞았는지는 모른다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthContent&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JXOok/btq1WKVTn4c/e7YEu7mRoq3BtYLlKkfft1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JXOok/btq1WKVTn4c/e7YEu7mRoq3BtYLlKkfft1/img.png&quot; data-alt=&quot;1차 코딩테스트 합격 안내 메일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JXOok/btq1WKVTn4c/e7YEu7mRoq3BtYLlKkfft1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJXOok%2Fbtq1WKVTn4c%2Fe7YEu7mRoq3BtYLlKkfft1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1197&quot; height=&quot;395&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1차 코딩테스트 합격 안내 메일&lt;/figcaption&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;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 2차 코딩테스트&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;2차 코딩테스트도 elice 플랫폼을 사용해 이루어졌다. 1차와는 다르게 화면 녹화가 이루어졌고, 인터넷 검색이 안된다는 주의사항이 추가되었다. 2차 코딩테스트는 알고리즘 3문제, 웹 1문제, SQL 1문제였다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;한 문제는 BFS, DFS 등을 이용한 탐색 문제, 한 문제는 완전탐색 문제, 한 문제는 분할정복 문제였다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;시간 복잡도와, 범위를 생각해야 하는 문제가 있었는데 본인은 시험이 끝나고 범위와 시간복잡도를 고려해야하는 문제라는 것을 깨달았다. 문제의 제한 범위 내의 큰 수를 넣었을 때 본인이 푼 방법으로는 시간이 굉장히 오래걸리기 때문에 아마 그 문제는 부분적으로 맞았을 것 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;1차 코딩테스트 문제를 응용한 문제가 출제되었다. 전체적인 난이도는 백준 실버 정도 생각하면 될 것 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;총 5문제에서 알고리즘 2문제, sql 1문제, 웹 1문제 풀었다. 그런데 알고리즘 한 문제는 상기의 이유로 부분 점수 받았을 것 같고, 웹 한 문제는 다 못 풀었다. 웹 문제에서 요구하는 요구사항 중 하나를 만족시키지 못했다. 프론트엔드 개발한다고 적어두고 틀린 것이 부끄럽고 아무래도 선발에서 큰 감점 요인이 될 것 같아 불안했다. 본인은 개발자 도구를 사용하는 것이 혹시나 부정행위일까 싶어서 사용하지 않았는데 개발자 도구를 사용했다면 금방 풀 수 있었을 것 같다. 다른 분들은 개발자 도구를 사용했다고 들었다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthContent&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qgy04/btq1R8DIg2S/QzHoHSQft0ItvSe3Q5W0k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qgy04/btq1R8DIg2S/QzHoHSQft0ItvSe3Q5W0k0/img.png&quot; data-alt=&quot;2차 코딩테스트 합격 안내 메일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qgy04/btq1R8DIg2S/QzHoHSQft0ItvSe3Q5W0k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQgy04%2Fbtq1R8DIg2S%2FQzHoHSQft0ItvSe3Q5W0k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;589&quot; height=&quot;306&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2차 코딩테스트 합격 안내 메일&lt;/figcaption&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;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 심층 면접&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;면접 대상자라는 메일을 받고 나서부터 엄청 떨렸다. 이게 뭐라고 일주일 내내 긴장하고 스트레스 받았다. 수능 때도 이렇게 안 떨었다. 그만큼 꼭 붙고 싶었다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;면접 준비 정말 열심히 했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지원서 다시 보면서 예상 질문 뽑기&lt;/li&gt;
&lt;li&gt;코딩테스트 풀이 준비&lt;/li&gt;
&lt;li&gt;각종 블로그에서 소마 면접 기출 질문 수집&lt;/li&gt;
&lt;li&gt;면접 예상 질문 리스트 뽑기(cs, 기술, 인성)&lt;/li&gt;
&lt;li&gt;예상 질문에 답변 준비&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;위 프로세스로 면접을 준비했다. 코딩테스트 풀이, 지원서 제외한 기출 질문과 예상 질문 및 답변을 정리한 파일은 40장이 넘었다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이후 지원자들을 위해 받았던 질문들을 생각나는 대로 복기해보겠다. 면접에서 받는 질문은 피면접자의 관심 기술과 지원서 토대로 이루어지니까 감안하고 보시면 좋을 것 같다. 본인은 프론트엔드 개발한다고 했다. 지원서에 UI와 접근성에 관한 것을 많이 언급했다. 특히 기술 질문은 본인의 주사용 기술에 따라 받는 것 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;한 시간 동안 면접이 진행된다. 면접관 5명, 피면접자 5명으로 진행되었다. 면접장이 여러 개 있고 면접관님들이 다 다르기 때문에 분위기도 다르고 받는 질문들도 다를 수 있다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;자기소개 (1분 내외)&lt;/b&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;&quot;@@ 소속 누구입니다&quot;를 말하길 원하는 질문이 아니다. 면접에서는 소속을 밝히지 마라고 하시기도 했다. 소마와 관련된 자기 소개와 간단한 지원 동기를 말하는 것을 추천한다.&lt;/li&gt;
&lt;/ul&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;열심히 준비해갔는데 본인은 질문 안 받았다. 5명 중 한 분에게만 질문하셨다. 꼬리질문 형태로 계속 질문하셨다.&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;li&gt;&lt;b&gt;(본인이 받은) 기술질문&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정렬 알고리즘에는 어떤 것이 있고, 본인은 그 중 무엇을 사용하는가? 왜 그 알고리즘을 사용하는가?&lt;/li&gt;
&lt;li&gt;접근성 관련 정부에서 하는 인증이 있는 것을 아는가? 어떤 것이 있나?&lt;/li&gt;
&lt;li&gt;http 1.0, 2.0, 3.0의 차이는 무엇인가?&lt;/li&gt;
&lt;li&gt;프론트엔드 개발할 때 비동기 방식의 코드는 언제 필요하고, 본인은 개발할 때 어떤 방식을 이용하는가?&lt;/li&gt;
&lt;li&gt;개발 방식(방법론)에는 어떤 것이 있는지? (테스트 주도 개발, 도메인 주도 개발 등이 있다고 답했고 테스트 주도 개발 방식이 무엇인지 물어보셨다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(다른 사람이 받은) 기술질문&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바와 C언어의 차이점&lt;/li&gt;
&lt;li&gt;모던 C++의 특징&lt;/li&gt;
&lt;li&gt;FOUC란?&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;li&gt;&lt;b&gt;지원서 기반 질문&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하고 싶은 프로젝트에서 가장 힘든 지점이 무엇일지?&lt;/li&gt;
&lt;li&gt;협업 과정에서 갈등 대처법 (본인 빼고 다 물어보셨다)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;좋은 UI가 어떻게 세상에 도움이 되는지?&lt;/li&gt;
&lt;li&gt;접근성을 고려한 개발을 한 경험이 있는지?&lt;/li&gt;
&lt;li&gt;하고 싶은 프로젝트가 거의 백엔드 위주인데, 본인은 프론트엔드 개발이 아닌가? 왜 이런 프로젝트를 적었는지?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기타 질문&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각자 상황을 물어보고(학업, 병역 등) 소마 과정에 집중할 수 있는지?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;일단 기억나는 것은 이 정도다. 생각보다 개인에게 주어진 시간이 없다. 준비도 많이 해갔고, 하고 싶은 말이 정말 많았는데 거의 하지 못했다. 같이 들어간 분들이 발언을 많이 하면 본인에게 주어진 시간이 점점 줄어든다. 우리 면접장은 시간 오버가 되어서 마지막에 급하게 끝냈다. 마지막 발언 기회가 있다면 하고 싶은 말도 준비해갔는데 그럴 기회가 없어서 아쉬웠다. 기술 질문 같은 경우 모르는 건 바로 모른다고 답했다. 시간이 그렇게 길지 않다보니 면접관님들도 짧게 본론만 말하는 것을 선호하셨다. 면접장 분위기는 아주 화기애애한 분위기도 아니고, 그렇다고 압박면접 분위기도 아니다. 꼬리 질문을 계속 받을 때는 좀 무섭긴 했다. 적당한 면접 분위기라고 생각하시면 될 것 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;참고로, 면접 가면 교통비 3만원씩 준다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;개인적으로 면접을 못 봤다고 생각하지는 않았지만 그럼에도 아쉬움이 많이 남았다. 특히 코딩 테스트를 못 본 것에 대해(웹 문제) 꼭 언급하고 싶었으나 그럴 기회가 없어서 떨어지더라도 납득해야지 하며 결과를 기다렸다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;너무 기쁘게도 합격해서 소마 12기 연수생이 되었다. 열심히 과정을 이수할 것을 다짐해본다!&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthContent&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kCRdE/btq1V14Ydas/pujQcWAIn0SKEkVEkGyFd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kCRdE/btq1V14Ydas/pujQcWAIn0SKEkVEkGyFd0/img.png&quot; data-alt=&quot;SW마에스트로 최종 합격 안내 메일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kCRdE/btq1V14Ydas/pujQcWAIn0SKEkVEkGyFd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkCRdE%2Fbtq1V14Ydas%2FpujQcWAIn0SKEkVEkGyFd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;804&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SW마에스트로 최종 합격 안내 메일&lt;/figcaption&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;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;+) 진행했던 소마 프로젝트가 궁금하다면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://toadhome.tistory.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://toadhome.tistory.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1668672762448&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;toadhome.tistory.com&quot; data-og-source-url=&quot;https://toadhome.tistory.com/&quot; data-og-url=&quot;https://toadhome.tistory.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bhwh7r/hyQBWdRohh/nrENT0v1YCOxoR6yr0CAx0/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/lmMCE/hyQA4EzC7g/y9JLVV1SGJNFVdwlyQaK11/img.png?width=1024&amp;amp;height=500&amp;amp;face=0_0_1024_500&quot;&gt;&lt;a href=&quot;https://toadhome.tistory.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://toadhome.tistory.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bhwh7r/hyQBWdRohh/nrENT0v1YCOxoR6yr0CAx0/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/lmMCE/hyQA4EzC7g/y9JLVV1SGJNFVdwlyQaK11/img.png?width=1024&amp;amp;height=500&amp;amp;face=0_0_1024_500');&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;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;toadhome.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;&lt;a href=&quot;https://toadhome.co&quot;&gt;https://toadhome.co&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1668672779760&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;toadhome.co&quot; data-og-source-url=&quot;https://toadhome.co&quot; data-og-url=&quot;https://toadhome.co&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bb7gbi/hyQA3TdObA/GwBJAHKUDcR4F568cffhUK/img.png?width=1024&amp;amp;height=500&amp;amp;face=0_0_1024_500,https://scrap.kakaocdn.net/dn/bzPP10/hyQBgE15di/Jv0FB7NFjArqPAz6BBar61/img.png?width=646&amp;amp;height=250&amp;amp;face=0_0_646_250&quot;&gt;&lt;a href=&quot;https://toadhome.co&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://toadhome.co&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bb7gbi/hyQA3TdObA/GwBJAHKUDcR4F568cffhUK/img.png?width=1024&amp;amp;height=500&amp;amp;face=0_0_1024_500,https://scrap.kakaocdn.net/dn/bzPP10/hyQBgE15di/Jv0FB7NFjArqPAz6BBar61/img.png?width=646&amp;amp;height=250&amp;amp;face=0_0_646_250');&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;toadhome.co&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 설치 링크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f5f6f8; color: #000000;&quot;&gt;&lt;a href=&quot;https://toadhome.onelink.me/JJLY/68228513&quot;&gt;https://toadhome.onelink.me/JJLY/68228513&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>회고</category>
      <category>SW마에스트로</category>
      <author>개발하는 크롱</author>
      <guid isPermaLink="true">https://crong-dev.tistory.com/66</guid>
      <comments>https://crong-dev.tistory.com/66#entry66comment</comments>
      <pubDate>Mon, 5 Apr 2021 20:57:11 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 SQL 고득점 Kit 코드 총정리 (MySQL) : String, Date</title>
      <link>https://crong-dev.tistory.com/65</link>
      <description>&lt;h2&gt;String, Date&lt;/h2&gt;
&lt;h3&gt;루시와 엘라 찾기 (Level 2)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT ANIMAL_ID, NAME,    SEX_UPON_INTAKE
FROM ANIMAL_INS
WHERE NAME IN (&amp;#39;Lucy&amp;#39;,&amp;#39;Ella&amp;#39;,&amp;#39;Pickle&amp;#39;,&amp;#39;Rogan&amp;#39;,&amp;#39;Sabrina&amp;#39;,&amp;#39;Mitty&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;이름에 el이 들어가는 동물 찾기 (Level 2)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT ANIMAL_ID, NAME
FROM ANIMAL_INS
WHERE NAME LIKE &amp;#39;%el%&amp;#39; AND ANIMAL_TYPE = &amp;#39;Dog&amp;#39;
ORDER BY NAME&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;LIKE를 사용하면 원하는 패턴의 문자열을 찾을 수 있다.&lt;br&gt;LIKE 다음에 찾고자 하는 문자열 패턴을 적으면 된다.&lt;br&gt;% 기호는 아무 부분 문자열에 대입된다. 길이가 0인 문자열도 포함이다.&lt;br&gt;_ 는 아무 한 글자에 대입 된다.&lt;br&gt;패턴은 case sensitive하니 주의하자!&lt;/p&gt;
&lt;h3&gt;중성화 여부 파악하기 (Level 2)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT ANIMAL_ID, NAME, IF (SEX_UPON_INTAKE LIKE &amp;#39;Neutered%&amp;#39; OR SEX_UPON_INTAKE LIKE &amp;#39;Spayed%&amp;#39;,&amp;#39;O&amp;#39;, &amp;#39;X&amp;#39;) AS 중성화
FROM ANIMAL_INS&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;IF (조건, 조건 만족시 값, 불만족 시 값)으로 컬럼을 구성 가능하다.&lt;/p&gt;
&lt;h3&gt;오랜 기간 보호한 동물(2) (Level 3)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT I.ANIMAL_ID, I.NAME
FROM ANIMAL_INS I INNER JOIN ANIMAL_OUTS O ON I.ANIMAL_ID = O.ANIMAL_ID
ORDER BY (O.DATETIME - I.DATETIME) DESC
LIMIT 2&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;DATETIME에서 DATE로 형 변환  (Level 2)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;SELECT ANIMAL_ID, NAME, DATE_FORMAT(DATETIME, &amp;#39;%Y-%m-%d&amp;#39;) AS 날짜
FROM ANIMAL_INS
ORDER BY ANIMAL_ID&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/프로그래머스</category>
      <category>MySQL</category>
      <category>sql</category>
      <category>프로그래머스</category>
      <author>개발하는 크롱</author>
      <guid isPermaLink="true">https://crong-dev.tistory.com/65</guid>
      <comments>https://crong-dev.tistory.com/65#entry65comment</comments>
      <pubDate>Fri, 19 Mar 2021 02:03:45 +0900</pubDate>
    </item>
  </channel>
</rss>