<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>꿀벌개발일지</title>
    <link>https://ohgyun.com/</link>
    <description>천천히, 그러나 끊임없이.</description>
    <language>ko</language>
    <pubDate>Sat, 4 Apr 2026 11:48:17 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>ohgyun</managingEditor>
    <image>
      <title>꿀벌개발일지</title>
      <url>https://t1.daumcdn.net/cfile/tistory/26683A49563F745123</url>
      <link>https://ohgyun.com</link>
    </image>
    <item>
      <title>홈어시스턴스 이미지 생성 시 오류</title>
      <link>https://ohgyun.com/810</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;발생일:&lt;/b&gt; 2021.05.25&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&amp;nbsp;&lt;/b&gt;홈어시스턴트, Home Assistant, HA&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;홈어시스턴트 이미지를 SD 카드에 구워서 라즈베리 파이에서 구동하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.home-assistant.io/installation/raspberrypi&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;가이드&lt;/a&gt;에 나온대로 SD&amp;nbsp;카드를&amp;nbsp;포맷하고&amp;nbsp;balenaEtcher&amp;nbsp;에서&amp;nbsp;타겟을&amp;nbsp;설정했는데,&amp;nbsp;아래와&amp;nbsp;같은&amp;nbsp;오류가&amp;nbsp;발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;EBUSY: resource busy or locked, open '/dev/rdisk2 on Mac&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;br /&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;SD 카드를 Disk Utility 에서 포맷했는데, 이 때 APFS 로 포맷한 것이 문제였다.&lt;br /&gt;Mac&amp;nbsp;OS&amp;nbsp;Extended&amp;nbsp;(Journaled)&amp;nbsp;으로&amp;nbsp;포맷해야&amp;nbsp;한다.&lt;br /&gt;&lt;br /&gt;APFS&amp;nbsp;로&amp;nbsp;포맷된&amp;nbsp;상태에서는&amp;nbsp;디스크&amp;nbsp;유틸리티에&amp;nbsp;다른&amp;nbsp;포맷&amp;nbsp;옵션이&amp;nbsp;나오지&amp;nbsp;않는다.&lt;br /&gt;아래와&amp;nbsp;같이&amp;nbsp;커맨드라인에서&amp;nbsp;실행하면&amp;nbsp;된다.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;$&amp;nbsp;sudo&amp;nbsp;diskutil&amp;nbsp;eraseDisk&amp;nbsp;HFS+&amp;nbsp;NEWNAME&amp;nbsp;/dev/disk2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/Etc</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/810</guid>
      <comments>https://ohgyun.com/810#entry810comment</comments>
      <pubDate>Tue, 25 May 2021 00:39:32 +0900</pubDate>
    </item>
    <item>
      <title>DynamoDB: on-demand 모드의 오토 스케일링</title>
      <link>https://ohgyun.com/809</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2021.04.21&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; 다이나모디비, 온디맨드, 오토 스케일링, auto scaling&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;다이나모디비를 온디맨드 모드로 쓰고 있다.&lt;/p&gt;
&lt;p&gt;배치 작업으로 평소보다 많은 데이터를 PUT 했는데, 아래와 같은 에러가 나온다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;Throughput&amp;nbsp;exceeds&amp;nbsp;the&amp;nbsp;current&amp;nbsp;capacity&amp;nbsp;for&amp;nbsp;one&amp;nbsp;or&amp;nbsp;more&amp;nbsp;global&amp;nbsp;secondary&amp;nbsp;indexes.&amp;nbsp;DynamoDB&amp;nbsp;is&amp;nbsp;automatically&amp;nbsp;scaling&amp;nbsp;your&amp;nbsp;index&amp;nbsp;so&amp;nbsp;please&amp;nbsp;try&amp;nbsp;again&amp;nbsp;shortly.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;온디맨드 모드는 스케일 제한이 없는 걸로 알았는데 왜 그런걸까?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;테이블 생성 후, 이전 트래픽 대비 많은 데이터를 넣어서 발생한 문제였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요약 먼저:&lt;br /&gt;온디맨드도 내부적으론 오토스케일링으로 동작하는데, 스케일링 기준을 초과한 요청을 보내서 쓰로틀이 발생한 것이었다.&lt;/p&gt;
&lt;p&gt;나중엔 스케일링 기준(이전 트래픽 피크치의 2배)를 임의로 필요한만큼 높여두면 쓰로틀링 이슈는 크게 걱정하지 않아도 되겠다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;온디맨드의&amp;nbsp;스케일링:&lt;br /&gt;-&amp;nbsp;온디맨드는&amp;nbsp;트래픽&amp;nbsp;볼륨에&amp;nbsp;따라&amp;nbsp;자동으로&amp;nbsp;확장됨&amp;nbsp;(Auto&amp;nbsp;Scaling)&lt;br /&gt;-&amp;nbsp;테이블은&amp;nbsp;이전&amp;nbsp;최대&amp;nbsp;트래픽의&amp;nbsp;최대&amp;nbsp;2배&amp;nbsp;용량을&amp;nbsp;즉시&amp;nbsp;수용할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;예:&amp;nbsp;이전&amp;nbsp;트래픽의&amp;nbsp;최대값이&amp;nbsp;초당&amp;nbsp;5만&amp;nbsp;읽기였으면,&amp;nbsp;초당&amp;nbsp;10만&amp;nbsp;읽기까지&amp;nbsp;즉시&amp;nbsp;수용&amp;nbsp;가능&lt;br /&gt;-&amp;nbsp;트래픽이&amp;nbsp;2배보다&amp;nbsp;더&amp;nbsp;커지는&amp;nbsp;경우&amp;nbsp;오토&amp;nbsp;스케일링&amp;nbsp;되면서&amp;nbsp;더&amp;nbsp;많은&amp;nbsp;용량을&amp;nbsp;할당함&lt;br /&gt;-&amp;nbsp;단,&amp;nbsp;30분&amp;nbsp;이내에&amp;nbsp;이전&amp;nbsp;피크의&amp;nbsp;2배&amp;nbsp;이상을&amp;nbsp;초과할&amp;nbsp;경우&amp;nbsp;쓰로틀링이&amp;nbsp;걸릴&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;테이블의&amp;nbsp;최대&amp;nbsp;피크치는&amp;nbsp;계속&amp;nbsp;지속됨&lt;br /&gt;-&amp;nbsp;온디맨드&amp;nbsp;테이블의&amp;nbsp;최초&amp;nbsp;설정은&lt;br /&gt;-&amp;nbsp;기존&amp;nbsp;테이블을&amp;nbsp;전환한&amp;nbsp;경우,&amp;nbsp;기존&amp;nbsp;테이블의&amp;nbsp;피크&amp;nbsp;트래픽의&amp;nbsp;2배&lt;br /&gt;-&amp;nbsp;새로&amp;nbsp;생성한&amp;nbsp;경우:&amp;nbsp;최대&amp;nbsp;초당&amp;nbsp;4000&amp;nbsp;쓰기,&amp;nbsp;12000읽기&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;팁:&lt;br /&gt;-&amp;nbsp;온디맨드&amp;nbsp;테이블의&amp;nbsp;최대&amp;nbsp;피크는&amp;nbsp;히스토리는&amp;nbsp;지속되므로&amp;nbsp;트래픽을&amp;nbsp;발생시켜서&amp;nbsp;pre-wraming&amp;nbsp;하는&amp;nbsp;요령도&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;최대&amp;nbsp;2배&amp;nbsp;이상의&amp;nbsp;트래픽이&amp;nbsp;30분&amp;nbsp;이내에&amp;nbsp;오지&amp;nbsp;않는&amp;nbsp;경우라면&amp;nbsp;신경쓰지&amp;nbsp;않아도&amp;nbsp;될&amp;nbsp;듯&lt;br /&gt;&lt;br /&gt;현재&amp;nbsp;상황:&lt;br /&gt;-&amp;nbsp;현재&amp;nbsp;테이블은&amp;nbsp;기존&amp;nbsp;테이블을&amp;nbsp;온디맨드로&amp;nbsp;변경한&amp;nbsp;거라서&amp;nbsp;기본&amp;nbsp;설정인&amp;nbsp;초당&amp;nbsp;4000&amp;nbsp;쓰기보다&amp;nbsp;낮았음.&lt;br /&gt;-&amp;nbsp;메트릭스를&amp;nbsp;보니&amp;nbsp;아마도&amp;nbsp;기존의&amp;nbsp;최대치는&amp;nbsp;초당&amp;nbsp;500&amp;nbsp;정도였는&amp;nbsp;듯.&lt;br /&gt;-&amp;nbsp;테스트할&amp;nbsp;때&amp;nbsp;기존&amp;nbsp;피크의&amp;nbsp;2배&amp;nbsp;이상인&amp;nbsp;초당&amp;nbsp;1200&amp;nbsp;쓰기&amp;nbsp;정도를&amp;nbsp;호출해서&amp;nbsp;쓰로틀이&amp;nbsp;걸린&amp;nbsp;것이었음&lt;br /&gt;&lt;br /&gt;결론:&lt;br /&gt;- 기본 설정도 초당 12000 읽기 정도라 초반에는 큰 문제는 없을 듯&lt;br /&gt;- 나중에 더 많은 트래픽이 예상될 땐, pre-warming 해둬서 최대 트래픽을 올려두면 문제 없을 듯&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- &lt;a href=&quot;https://theburningmonk.com/2019/03/understanding-the-scaling-behaviour-of-dynamodb-ondemand-tables/&quot;&gt;https://theburningmonk.com/2019/03/understanding-the-scaling-behaviour-of-dynamodb-ondemand-tables/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;온디맨드 읽기/용량 모드: &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html#HowItWorks.InitialThroughput&quot;&gt;https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html#HowItWorks.InitialThroughput&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/DB</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/809</guid>
      <comments>https://ohgyun.com/809#entry809comment</comments>
      <pubDate>Fri, 16 Apr 2021 10:58:22 +0900</pubDate>
    </item>
    <item>
      <title>DynamoDB: 고민했던 것들에 대한 정리</title>
      <link>https://ohgyun.com/808</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2021.03.14&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; DynamoDB, 다이나모디비&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;다이나모디비를 써보면서 헷갈렸던 것들, 생각과 달랐거나 고민했던 내용, 중요하다고 생각되는 것들에 대한 정리&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;해결:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&amp;lt;비용&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;핫파티션은 프로비저닝 모드에서만 해당됨&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- 과금 체계는 프로비저닝 모드와 온디맨드 모드가 있고&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;- 프로비저닝 모드&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 프로비저닝 모드는 RCU와 WCU를 직접 설정하는데, '초당 처리량' 기준임&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; &amp;nbsp; - 핫파티션 이슈는 프로비저닝 모드에서만 해당됨&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - 초당 요청이 할당 받은 처리량을 초과하지 않게 하기 위해 설계해야 함&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 예약 요금은 프로비저닝 모드에서 쓸 RCU,WCU를 미리 사두는 개념&lt;/p&gt;
&lt;p&gt;- 온디맨드 모드&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 사용한 RCU와 WCU 만큼 과금하는 모델&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 사용한 만큼 과금되기 때문에, 처리량의 한계와 핫파티션 이슈가 없음&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 더 비쌈. 비용 절감을 위해 서비스가 안정화되면 프로비저닝 모드로 전환하는 게 필요하다고 함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;파티션 설계를 할 때 중요한 것은 '처리량'의 분산 (프로비저닝 모드만)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- 내가 설정한 처리량을 최대한 활용하는 것이 좋음&lt;/p&gt;
&lt;p&gt;- '저장공간'이 아니라 '처리량'이 분산되도록 하는 것이 중요&lt;/p&gt;
&lt;p&gt;- 물론, 프로비저닝 모드인 경우에 해당됨&lt;/p&gt;
&lt;p&gt;- 설정한 RCU/WCU 는 파티션 별로 골고루 할당됨&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 예: 100RCU 에 파티션이 5개라면, 각 파티션에 20RCU 씩 분산됨&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 특정 파티션에 20RCU 요청를 초과하는 요청이 들어오면(핫파티션) 병목이 생기고 전체 테이블에 영향이 감&lt;/p&gt;
&lt;p&gt;- 경우에 따라 다르겠지만, 상시 요청이 오는 서비스라면 (&lt;span style=&quot;color: #333333;&quot;&gt;온디맨드로 시작하더라도) &lt;/span&gt;결국 비용 절감을 위해 프로비저닝으로 변경해야 할 것 같음. 즉, 고민해두긴 해야 함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;한 번 요청에 여러 RCU가 소진될 수 있음&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- eventual consisten 기준으로 4K 당 0.5RCU 가 소진됨 (즉, 8K 당 1RCU)&lt;/p&gt;
&lt;p&gt;- 스캔이나 쿼리로 한 번 읽어올 때 최대 1MB 까지 가능한데, 1MB를 읽어왔다면 1024 / 8 = 128RCU를 소진하는 셈&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;scan() 으로 테이블을 읽어오면 문제가 없을까? 과금 모델에 따라 다름&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- 전체 테이블을 scan() 하면서 한 번에 1MB를 읽어왔다면, 128RCU 를 소진함&lt;/p&gt;
&lt;p&gt;- 프로비저닝 모드라면,&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 초당 처리량이 문제가 될 수 있으므로, 적당히 나눠서 요청하는 것이 필요함&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 예: 100RCU 를 설정해뒀는데, 한 번에 1MB 씩 읽어오면 이미 테이블 처리량을 초과하게 됨.&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 이럴 땐, 1초에 10RCU(80K)만 쓰게 나누는 식으로 분산&lt;/p&gt;
&lt;p&gt;- 온디맨드 모드라면, 걱정 없이 그냥 읽어오면 됨&lt;/p&gt;
&lt;p&gt;- 병렬 스캔을 하면(TotalSegments, Segmant 파라미터), 빨리 가져올 수 있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 처리량이 크거나 온디맨드 모드라면(= 돈 걱정이 없다면),&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 테이블이 커도 세그먼트 수를 늘려서(예: 10만개) 수초 만에 가져올 수 있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Projection 을 해도 비용은 동일함&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- 쿼리나 스캔할 때, Projection 을 설정해 특정 속성만 가져오도록 하더라도 RCU 비용은 동일함&lt;/p&gt;
&lt;p&gt;- 네트워크 비용이 줄어들지만 뭔가 속은 느낌-_-&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;LSI를 설정하지 않으면 파티션 사이즈를 고민하지 않아도 됨&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- 파티션 최대 크기에 대한 문서가 조금 애매하긴 하지만, LSI를 설정하지 않으면 파티션 크기는 고민하지 않아도 되는 듯&lt;/p&gt;
&lt;p&gt;- LSI를 설정한 경우, 파티션의 최대 크기가 10GB로 제한됨&lt;/p&gt;
&lt;p&gt;- LSI는 대체로 잘 활용하지 않는 것 같음. 리인벤트 강의를 여러 개 들어봤는데, 아무도 LSI의 활용에 대해서는 언급하지 않았음 (GSI를 잘 설계하면 될 듯)&lt;/p&gt;
&lt;p&gt;- LSI를 설정하지 않은 상태에서 10GB를 넘으면?&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 문제 없는 것으로 파악함&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 명시되어 있진 않은데 내부적으로 처리하지 않을까 예상해봄&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 파티션 1개의 크기에 한계가 있더라도 정렬키가 명확하니까 처리 자체가 복잡하지 않을 듯&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&amp;lt;싱글 테이블 디자인&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;싱글 테이블 디자인에서 테이블을 나누는 기준&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- (아직 확신이 없지만, 적어도 지금까지는) 전부 싱글 테이블로 해도 괜찮을 것 같음&lt;/p&gt;
&lt;p&gt;- 다만, MSA 로 서비스를 나눈다고 가정했을 때 완전 다른 도메인은 별도로 분리해도 좋을 듯&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;싱글 테이블 설계에서 데이터를 가져올 땐, &quot;시간복잡도&quot;를 줄이는 것이 중요&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- 스케일이 커지더라도 시간 복잡도를 줄이는 것이 강점&lt;/p&gt;
&lt;p&gt;- 복잡한 키 설계를 통해, 여러 타입의 데이터를 한 번에 가져올 수 있는 것이 장점&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;싱글 테이블인데 다른 엔터티를 함께 읽지 않으면 비효율&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- 작업하다보니 싱글 테이블의 아이템을 조회할 때, 습관적으로 RDS에서 했던 것처럼 별개로 조회하고 있었음&lt;/p&gt;
&lt;p&gt;- 여러 타입의 아이템을 한 번에 가져올 수 있게 GSI를 설계하는 것이 중요할 듯&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;자주 변경되지 않는 데이터는 중복하고 변경될 때 업데이트함&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- 참조하는 아이템의 중복을 얼마나 허용해야 할까? (예: 유저 닉네임)&lt;/p&gt;
&lt;p&gt;- 빈번하게 참조하는 대상도 중복해야 할까?&lt;/p&gt;
&lt;p&gt;- 싱글 테이블의 베스트 프랙티스에서는 바뀌지 않거나(immutable) 자주 변경되지 않으면 중복해서 넣으라고 함&lt;/p&gt;
&lt;p&gt;- 여기서 자주는 하루에 1~2번 정도. 발표자는 1천번까지도 괜찮다고 생각한다고 함&lt;/p&gt;
&lt;p&gt;- 중복하더라도 스토리지를 조금 더 쓸 뿐이라고. 스토리지는 저렴함.&lt;/p&gt;
&lt;p&gt;- 중복 데이터는 스트림과 람다를 활용해서 업데이트 하면 되고, 예전처럼 고통스럽지 않을 거라고 함&lt;/p&gt;
&lt;p&gt;- 지금은 1980년대가 아니라면서...&lt;/p&gt;
&lt;p&gt;- 참고: &lt;a href=&quot;https://www.youtube.com/watch?v=MF9a1UNOAQo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS&amp;nbsp;re:Invent&amp;nbsp;2020:&amp;nbsp;Amazon&amp;nbsp;DynamoDB&amp;nbsp;advanced&amp;nbsp;design&amp;nbsp;patterns&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/DB</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/808</guid>
      <comments>https://ohgyun.com/808#entry808comment</comments>
      <pubDate>Sun, 14 Mar 2021 13:51:12 +0900</pubDate>
    </item>
    <item>
      <title>DynamoDB: Building NoSQL Database-Driven Applications  노트</title>
      <link>https://ohgyun.com/807</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2021.02.16&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; DynamoDB, 다이나모디비&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;DynamoDB에 코세라 강좌가 있어 들으면서 정리했던 노트다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.coursera.org/learn/dynamodb-nosql-database-driven-apps&quot;&gt;https://www.coursera.org/learn/dynamodb-nosql-database-driven-apps&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;지금보면 너무 기초적인 것 같은데, 당시 흐릿하게 개념이 잡혔던 터라 전체 컨셉을 이해하기에 좋았다.&lt;/p&gt;
&lt;p&gt;지금 보더라도 유심히 기억해야할 만한 특징인 볼드로 표시해뒀다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;내용:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Relational&amp;nbsp;Databases&amp;nbsp;and&amp;nbsp;the&amp;nbsp;Problem&amp;nbsp;that&amp;nbsp;Need&amp;nbsp;Solving&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;RDS(Relational&amp;nbsp;Data&amp;nbsp;System)의&amp;nbsp;특징&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;정형&amp;nbsp;데이터를&amp;nbsp;분석하기&amp;nbsp;좋음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;스토리지를&amp;nbsp;많이&amp;nbsp;차지하고&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;컴퓨팅&amp;nbsp;비용이&amp;nbsp;큼&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;- CAP 정리: 다음과 같은 세 가지 조건을 모두 만족하는 분산 컴퓨터 시스템이 존재하지 않음을 증명한 정리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;일관성(Consistency):&amp;nbsp;모든&amp;nbsp;노드가&amp;nbsp;같은&amp;nbsp;순간에&amp;nbsp;같은&amp;nbsp;데이터를&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;가용성(Availability):&amp;nbsp;모든&amp;nbsp;요청이&amp;nbsp;성공&amp;nbsp;또는&amp;nbsp;실패&amp;nbsp;결과를&amp;nbsp;반환할&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;분할내성(Partition&amp;nbsp;tolerance):&amp;nbsp;메시지&amp;nbsp;전달이&amp;nbsp;실패하거나&amp;nbsp;시스템&amp;nbsp;일부가&amp;nbsp;망가져도&amp;nbsp;시스템이&amp;nbsp;계속&amp;nbsp;동작할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;Partiton&amp;nbsp;Tolerance&amp;nbsp;는&amp;nbsp;시스템에서&amp;nbsp;보장해야&amp;nbsp;함.&amp;nbsp;그래서&amp;nbsp;C&amp;nbsp;와&amp;nbsp;A&amp;nbsp;중&amp;nbsp;둘&amp;nbsp;중&amp;nbsp;하나를&amp;nbsp;선택해야&amp;nbsp;함.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;- NoSQL 이 유리하지 않은 경우&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;피크를&amp;nbsp;예상할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;self-managed&amp;nbsp;&amp;nbsp;환경.&amp;nbsp;수평&amp;nbsp;확장이&amp;nbsp;항상&amp;nbsp;옵션은&amp;nbsp;아님&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;정형&amp;nbsp;데이터로만&amp;nbsp;구성된&amp;nbsp;경우&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;여러&amp;nbsp;테이블의&amp;nbsp;조인이&amp;nbsp;필요한&amp;nbsp;경우.&amp;nbsp;NoSQL은&amp;nbsp;cross-table&amp;nbsp;관계가&amp;nbsp;없음.&amp;nbsp;조인을&amp;nbsp;할&amp;nbsp;수가&amp;nbsp;없음&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;NoSQL&amp;nbsp;디비&amp;nbsp;타입&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Key-Value&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;key-value&amp;nbsp;기반으로&amp;nbsp;키&amp;nbsp;조합으로&amp;nbsp;데이터를&amp;nbsp;가져올&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;DynamoDB&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;유저&amp;nbsp;세션&amp;nbsp;데이터&amp;nbsp;저장하기에&amp;nbsp;적합&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Column-based&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;많은&amp;nbsp;데이터를&amp;nbsp;빨리&amp;nbsp;가져오는데&amp;nbsp;유리함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Cassandra,&amp;nbsp;HBase&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Document-based&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;tagged&amp;nbsp;elements로&amp;nbsp;저장&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;MongoDB&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;DynamoDB&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Managed&amp;nbsp;System&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;read/write&amp;nbsp;capacity&amp;nbsp;units&amp;nbsp;을&amp;nbsp;조정해서&amp;nbsp;성능&amp;nbsp;레벨을&amp;nbsp;관리할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;스트림,&amp;nbsp;트랜잭션,&amp;nbsp;액셀러레이터,&amp;nbsp;글로벌&amp;nbsp;테이블&amp;nbsp;등의&amp;nbsp;기능&amp;nbsp;제공&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;low&amp;nbsp;latency,&amp;nbsp;redundant&amp;nbsp;storage,&amp;nbsp;managed&amp;nbsp;durability,&amp;nbsp;fault&amp;nbsp;tolerance&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;1일&amp;nbsp;10조&amp;nbsp;콜,&amp;nbsp;초당&amp;nbsp;2000만&amp;nbsp;콜&amp;nbsp;처리&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;TTL(Time&amp;nbsp;To&amp;nbsp;Live)&amp;nbsp;설정&amp;nbsp;가능&amp;nbsp;&lt;br /&gt;&lt;br /&gt;DynamoDB&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;테이블&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- primary key = partition key + (sort key)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- primary key가 중복되면 안됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- attirbutes&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;- read request 의 consistent&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;strongly:&amp;nbsp;무거움&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;eventually&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;제한&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;한&amp;nbsp;레코드는&amp;nbsp;400KB&amp;nbsp;제한&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;더&amp;nbsp;큰&amp;nbsp;건&amp;nbsp;S3에&amp;nbsp;저장해야&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;key&amp;nbsp;길이&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Partition&amp;nbsp;Key:&amp;nbsp;1byte&amp;nbsp;이상,&amp;nbsp;2048&amp;nbsp;byte&amp;nbsp;이하&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Sort&amp;nbsp;key:&amp;nbsp;1byte&amp;nbsp;~&amp;nbsp;1024byte&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;테이블&amp;nbsp;생성&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;Partition&amp;nbsp;Key와&amp;nbsp;Sort&amp;nbsp;Key&amp;nbsp;조합이&amp;nbsp;Unique&amp;nbsp;하면&amp;nbsp;됨&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;테이블&amp;nbsp;목록&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;$&amp;nbsp;aws&amp;nbsp;dynamodb&amp;nbsp;list-tables&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;아이템&amp;nbsp;추가&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- $ aws dynamodb put-item --table-name Music --item file://song1.json&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;조회&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- $ aws dynamodb query --table-name Music --key-condition-expression &quot;Artist = :v1 AND SongTiItle = :v2&quot; --expression-attribute-values file://values1.json&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;v1,&amp;nbsp;v2&amp;nbsp;=&amp;nbsp;변수&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;&quot;S&quot;:&amp;nbsp;&quot;Rock&quot;&amp;nbsp;의&amp;nbsp;의미&amp;nbsp;=&amp;nbsp;타입:&amp;nbsp;값&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;b&gt;pk 와 sk 를 제외한 다른 속성은 쿼리(query)할 수 없음&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;삭제&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- $ aws dynamodb delete-item --table-name Music --key file://key.json&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;{&amp;nbsp;&quot;Artist&quot;:&amp;nbsp;{&amp;nbsp;&quot;S&quot;:&amp;nbsp;&quot;David&amp;nbsp;Bowie&quot;&amp;nbsp;},&amp;nbsp;&quot;SongTitle&quot;:&amp;nbsp;{&amp;nbsp;&quot;S&quot;:&amp;nbsp;&quot;Changes&quot;&amp;nbsp;}&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;Query&amp;nbsp;and&amp;nbsp;Scan&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;Query:&amp;nbsp;PK&amp;nbsp;또는&amp;nbsp;PK&amp;nbsp;+&amp;nbsp;SK로&amp;nbsp;조회&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;정렬키만&amp;nbsp;있으면&amp;nbsp;조회&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;Scan:&amp;nbsp;테이블의&amp;nbsp;전체&amp;nbsp;테이블&amp;nbsp;조회&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;$&amp;nbsp;aws&amp;nbsp;dynamodb&amp;nbsp;scan&amp;nbsp;--table-name&amp;nbsp;Music&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;필터:&amp;nbsp;속성으로&amp;nbsp;조회해야&amp;nbsp;하는데,&amp;nbsp;키가&amp;nbsp;아니라서&amp;nbsp;쿼리를&amp;nbsp;할&amp;nbsp;수&amp;nbsp;없는&amp;nbsp;경우&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;key-condition&amp;nbsp;과&amp;nbsp;동일하게&amp;nbsp;--filter-expression&amp;nbsp;을&amp;nbsp;쓰면&amp;nbsp;됨&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;Scan&amp;nbsp;+&amp;nbsp;Filter&amp;nbsp;로&amp;nbsp;키가&amp;nbsp;아닌&amp;nbsp;걸&amp;nbsp;조회할&amp;nbsp;수&amp;nbsp;있으나,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;scan&amp;nbsp;은&amp;nbsp;전체&amp;nbsp;테이블을&amp;nbsp;읽는&amp;nbsp;것이므로&amp;nbsp;주의해야&amp;nbsp;함&lt;br /&gt;&lt;br /&gt;Secondary&amp;nbsp;Index&lt;br /&gt;- &lt;b&gt;다른 속성을 키로 갖는 테이블 복제본을 만드는 것&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;Local&amp;nbsp;secondary&amp;nbsp;index:&amp;nbsp;다른&amp;nbsp;정렬키를&amp;nbsp;생성&lt;br /&gt;-&amp;nbsp;Global&amp;nbsp;secondary&amp;nbsp;index:&amp;nbsp;다른&amp;nbsp;파티션키와&amp;nbsp;정렬키를&amp;nbsp;생성&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;백업&lt;br /&gt;-&amp;nbsp;on-demand&amp;nbsp;backup&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;람다나,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AWS&amp;nbsp;Backup&amp;nbsp;으로&amp;nbsp;스케쥴링&amp;nbsp;가능&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;point-in-time&amp;nbsp;recovery&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;자동&amp;nbsp;백업&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;35일&amp;nbsp;이내&amp;nbsp;복원&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;비동기,&amp;nbsp;성능&amp;nbsp;저하&amp;nbsp;없음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;복원할&amp;nbsp;땐&amp;nbsp;read/write&amp;nbsp;capacity가&amp;nbsp;동일해야&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;복원&amp;nbsp;시간은&amp;nbsp;상황에&amp;nbsp;따라&amp;nbsp;다름&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;$&amp;nbsp;aws&amp;nbsp;dynamodb&amp;nbsp;create-backup&amp;nbsp;--table-name&amp;nbsp;Music&amp;nbsp;--backup-name&amp;nbsp;MusicBackup&lt;br /&gt;-&amp;nbsp;$&amp;nbsp;aws&amp;nbsp;dynamodb&amp;nbsp;describe-backup&amp;nbsp;--backup-arn&amp;nbsp;[BackupArn]&lt;br /&gt;-&amp;nbsp;$&amp;nbsp;aws&amp;nbsp;dynamodb&amp;nbsp;restore-table-from-backup&amp;nbsp;--target-table-name&amp;nbsp;MusicRestored&amp;nbsp;--backup-arn&amp;nbsp;[BackupArn]&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;스캔&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;$&amp;nbsp;aws&amp;nbsp;dynamodb&amp;nbsp;scan&amp;nbsp;--table-name&amp;nbsp;Music&amp;nbsp;--max-items&amp;nbsp;3&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;&lt;b&gt;최대&amp;nbsp;1MB&amp;nbsp;까지&amp;nbsp;가져옴&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;last&amp;nbsp;process&amp;nbsp;key&amp;nbsp;로&amp;nbsp;다음&amp;nbsp;배치를&amp;nbsp;가져올&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;페이지네이션처럼&amp;nbsp;동작함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;BatchWriteItem&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;여러&amp;nbsp;테이블에&amp;nbsp;여러&amp;nbsp;레코드를&amp;nbsp;쓸&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;b&gt;추가하거나 삭제할 수 있음. 업데이트는 안됨&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;요청&amp;nbsp;중에&amp;nbsp;하나가&amp;nbsp;실패하더라도&amp;nbsp;나머지&amp;nbsp;항목은&amp;nbsp;실행됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;실패된&amp;nbsp;아이템은&amp;nbsp;unprocessed&amp;nbsp;items&amp;nbsp;응답으로&amp;nbsp;내려옴&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- { 테이블명: [ { 커맨드: { ... }, 커맨드: { ... }] }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- $ aws dynamodb batch-write-item --request-items file://batch.json&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;BatchGetItem&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;여러&amp;nbsp;테이블에서&amp;nbsp;여러&amp;nbsp;레코드를&amp;nbsp;가져올&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;&lt;b&gt;기본적으로&amp;nbsp;eventually&amp;nbsp;consistent&amp;nbsp;임&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;필요하다면&amp;nbsp;strongly&amp;nbsp;consistent&amp;nbsp;를&amp;nbsp;테이블&amp;nbsp;단위로&amp;nbsp;설정할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- $ aws dynamodb batch-get-items --request-items file://...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- { 테이블명: { Keys: [ ... ] } }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;특정&amp;nbsp;테이블이&amp;nbsp;실패했다면&amp;nbsp;unprocessed&amp;nbsp;keys&amp;nbsp;응답에&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;BatchWriteItem&amp;nbsp;과&amp;nbsp;BatchGetItem&amp;nbsp;실패&amp;nbsp;시,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;unprocessed&amp;nbsp;응답을&amp;nbsp;보고&amp;nbsp;retry&amp;nbsp;하면&amp;nbsp;됨&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;- scan 시 주의&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;전체&amp;nbsp;데이터를&amp;nbsp;훑음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 1메가 단위로 끊어서 읽음. LastEvalueatedKey 로 나머지 이어서 가져올 수 있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;필터가&amp;nbsp;적용되어&amp;nbsp;있으면,&amp;nbsp;읽어온&amp;nbsp;데이터에&amp;nbsp;값이&amp;nbsp;없을&amp;nbsp;수도&amp;nbsp;있음&amp;nbsp;(마지막이&amp;nbsp;아니더라도)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;옵션&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;ExpressionAttributeNames&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;속성&amp;nbsp;키가&amp;nbsp;예약어와&amp;nbsp;겹치는&amp;nbsp;등의&amp;nbsp;이유로&amp;nbsp;다른&amp;nbsp;이름으로&amp;nbsp;매핑하려는&amp;nbsp;경우&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;#&amp;nbsp;캐릭터를&amp;nbsp;씀&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;&quot;#family&quot;:&amp;nbsp;&quot;family&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;ProjectionExpression&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;필요한&amp;nbsp;속성만&amp;nbsp;가져오려는&amp;nbsp;경우&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;&quot;dragon_name,&amp;nbsp;#family,&amp;nbsp;protection,&amp;nbsp;damage,&amp;nbsp;description&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Segment&amp;nbsp;나&amp;nbsp;TotalSegments&amp;nbsp;로&amp;nbsp;병렬&amp;nbsp;처리&amp;nbsp;가능&amp;nbsp;(가이드)&lt;br /&gt;&lt;br /&gt;모니터링&lt;br /&gt;-&amp;nbsp;CloudTrail&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;테이블&amp;nbsp;액션,&amp;nbsp;role,&amp;nbsp;service&amp;nbsp;모니터링&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이벤트&amp;nbsp;로그를&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;CloudWatch&amp;nbsp;Logs&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;알람&amp;nbsp;설정&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;API에&amp;nbsp;따라&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;&lt;b&gt;X-Ray&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;바틀넥이나&amp;nbsp;에러&amp;nbsp;처리할&amp;nbsp;때&amp;nbsp;쓸&amp;nbsp;수&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;CloudWatch&amp;nbsp;Metric&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;각종&amp;nbsp;지표&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;RCU,&amp;nbsp;WCU&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;1&amp;nbsp;RCU&amp;nbsp;=&amp;nbsp;1&amp;nbsp;strongly&amp;nbsp;consistent&amp;nbsp;read&amp;nbsp;=&amp;nbsp;2&amp;nbsp;eventually&amp;nbsp;consistent&amp;nbsp;read&amp;nbsp;(4KB&amp;nbsp;이내,&amp;nbsp;초당&amp;nbsp;처리&amp;nbsp;기준)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;1&amp;nbsp;WCU&amp;nbsp;=&amp;nbsp;1&amp;nbsp;write&amp;nbsp;(1KB&amp;nbsp;이내,&amp;nbsp;초당&amp;nbsp;처리&amp;nbsp;기준)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- dynamodb provisioned capacity 테이블을 보면 정보가 있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 메트릭스 상세: &lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/metrics-dimensions.html&quot;&gt;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/metrics-dimensions.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;RCU,&amp;nbsp;WCU&amp;nbsp;도&amp;nbsp;파티션에&amp;nbsp;따라&amp;nbsp;분산됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;5000RCU&amp;nbsp;가&amp;nbsp;있고&amp;nbsp;파티션이&amp;nbsp;5개이면,&amp;nbsp;한&amp;nbsp;파티션에&amp;nbsp;1000RCU&amp;nbsp;씩&amp;nbsp;분산됨&lt;br /&gt;&lt;br /&gt;RCU,&amp;nbsp;WCU가&amp;nbsp;적절한&amp;nbsp;것&amp;nbsp;같은데도&amp;nbsp;퍼포먼스가&amp;nbsp;제대로&amp;nbsp;안나올&amp;nbsp;땐?&amp;nbsp;(프로비전인&amp;nbsp;경우)&lt;br /&gt;-&amp;nbsp;CloudWatch에서&amp;nbsp;capacity&amp;nbsp;관련&amp;nbsp;메트릭스&amp;nbsp;확인&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;테이블에서&amp;nbsp;시간당&amp;nbsp;쓰인&amp;nbsp;RCU,&amp;nbsp;WCU를&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있음&amp;nbsp;(글로벌&amp;nbsp;인덱스나&amp;nbsp;세컨더리&amp;nbsp;인덱스도)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;메트릭스에서&amp;nbsp;설정한&amp;nbsp;것보다&amp;nbsp;더&amp;nbsp;많이&amp;nbsp;쓰였다면&amp;nbsp;올려줘야&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;알람&amp;nbsp;맞춰두면&amp;nbsp;좋음&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;RCU와&amp;nbsp;WCU도&amp;nbsp;오토스케일링&amp;nbsp;정책을&amp;nbsp;적용해서&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;그래도&amp;nbsp;병목이&amp;nbsp;있으면?&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;키&amp;nbsp;스키마&amp;nbsp;관련&amp;nbsp;이슈일&amp;nbsp;수&amp;nbsp;있음&amp;nbsp;(파티션&amp;nbsp;키&amp;nbsp;최적화가&amp;nbsp;필요함)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;large&amp;nbsp;scan&amp;nbsp;하지&amp;nbsp;않게&amp;nbsp;쿼리&amp;nbsp;최적화가&amp;nbsp;필요함&lt;br /&gt;&lt;br /&gt;파티션키와&amp;nbsp;최적화&lt;br /&gt;-&amp;nbsp;테이블을&amp;nbsp;생성하면&amp;nbsp;데이터는&amp;nbsp;파티션에&amp;nbsp;저장됨&lt;br /&gt;-&amp;nbsp;파티션은&amp;nbsp;스토리지&amp;nbsp;단위이고&amp;nbsp;물리적&amp;nbsp;공간(SSD)에&amp;nbsp;저장됨&lt;br /&gt;-&amp;nbsp;멀티&amp;nbsp;리전에&amp;nbsp;자동으로&amp;nbsp;복제됨&lt;br /&gt;-&amp;nbsp;테이블&amp;nbsp;생성하면&amp;nbsp;처리량(RCU와&amp;nbsp;WCU)에&amp;nbsp;맞춰&amp;nbsp;적당한&amp;nbsp;개수의&amp;nbsp;파티션을&amp;nbsp;생성함&lt;br /&gt;-&amp;nbsp;테이블이&amp;nbsp;커지면&amp;nbsp;파티션이&amp;nbsp;추가됨&lt;br /&gt;&lt;br /&gt;&lt;b&gt;파티션키&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;모든&amp;nbsp;테이블은&amp;nbsp;파티션키가&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;파티션키만&amp;nbsp;있으면&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;파티션키에&amp;nbsp;해시함수를&amp;nbsp;적용한&amp;nbsp;값으로&amp;nbsp;파티션&amp;nbsp;구분&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;정렬키가&amp;nbsp;같이&amp;nbsp;있으면,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;파티션키에&amp;nbsp;해시함수를&amp;nbsp;적용한&amp;nbsp;값으로&amp;nbsp;파티션을&amp;nbsp;구분하고&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;여기에서&amp;nbsp;sort&amp;nbsp;key&amp;nbsp;로&amp;nbsp;데이터를&amp;nbsp;정렬함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;키&amp;nbsp;스키마&amp;nbsp;디자인할&amp;nbsp;때&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;파티션키가&amp;nbsp;저장&amp;nbsp;위치를&amp;nbsp;결정함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;모든&amp;nbsp;파티션에&amp;nbsp;골고루&amp;nbsp;분산되게&amp;nbsp;파티션키를&amp;nbsp;디자인해야&amp;nbsp;함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;RCU,&amp;nbsp;WCU를&amp;nbsp;직접&amp;nbsp;설정한&amp;nbsp;경우&amp;nbsp;&lt;b&gt;(on-demand&amp;nbsp;는&amp;nbsp;해당&amp;nbsp;안됨)&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;400RCU가&amp;nbsp;있고&amp;nbsp;파티션이&amp;nbsp;4개라면,&amp;nbsp;각&amp;nbsp;파티션은&amp;nbsp;100RCU를&amp;nbsp;가짐&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;200RCU&amp;nbsp;만큼의&amp;nbsp;요청이&amp;nbsp;들어왔을&amp;nbsp;때,&amp;nbsp;파티션키가&amp;nbsp;골고루&amp;nbsp;분산되어&amp;nbsp;있다면&amp;nbsp;각&amp;nbsp;파티션은&amp;nbsp;50RCU&amp;nbsp;씩&amp;nbsp;처리함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;만약&amp;nbsp;한&amp;nbsp;파티션에&amp;nbsp;200RCU만큼의&amp;nbsp;요청이&amp;nbsp;몰린다면(hot-partition),&amp;nbsp;처리량을&amp;nbsp;넘어서게&amp;nbsp;됨&amp;nbsp;(provision&amp;nbsp;throughput&amp;nbsp;exceeded&amp;nbsp;exception&amp;nbsp;발생)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;즉,&amp;nbsp;전체&amp;nbsp;RCU를&amp;nbsp;초과하지&amp;nbsp;않았는데도&amp;nbsp;에러가&amp;nbsp;발생하게&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;핫파티션이&amp;nbsp;생기지&amp;nbsp;않게&amp;nbsp;파티션키를&amp;nbsp;디자인해야&amp;nbsp;함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;adaptive&amp;nbsp;capacity&amp;nbsp;기능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;핫파티션이&amp;nbsp;생겼을&amp;nbsp;때,&amp;nbsp;사용되지&amp;nbsp;않은&amp;nbsp;쓰루풋을&amp;nbsp;핫파티션이&amp;nbsp;사용하게&amp;nbsp;하는&amp;nbsp;옵션&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;기본&amp;nbsp;적용되어&amp;nbsp;있음&amp;nbsp;(아마도&amp;nbsp;2019년부터?)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;burst&amp;nbsp;capacity&amp;nbsp;기능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;5분&amp;nbsp;동안&amp;nbsp;사용하지&amp;nbsp;않는&amp;nbsp;용량을&amp;nbsp;남겨두었다가&amp;nbsp;초과분에&amp;nbsp;사용함&lt;br /&gt;&lt;br /&gt;&lt;b&gt;베스트 프랙티스. 파티션 키 디자인&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-partition-key-uniform-load.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-partition-key-uniform-load.html&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;골고루&amp;nbsp;분산되는&amp;nbsp;속성을&amp;nbsp;파티션키로&amp;nbsp;설정해야&amp;nbsp;함&lt;br /&gt;-&amp;nbsp;좋은&amp;nbsp;예&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;사용자가&amp;nbsp;많은&amp;nbsp;경우,&amp;nbsp;사용자&amp;nbsp;아이디&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;디바이스가&amp;nbsp;비슷한&amp;nbsp;빈도로&amp;nbsp;접속하는&amp;nbsp;경우,&amp;nbsp;디바이스&amp;nbsp;아이디&lt;br /&gt;-&amp;nbsp;나쁜&amp;nbsp;예&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;HTTP&amp;nbsp;상태&amp;nbsp;코드:&amp;nbsp;200이&amp;nbsp;대부분을&amp;nbsp;차지하기&amp;nbsp;때문&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;날짜&amp;nbsp;단위의&amp;nbsp;아이템&amp;nbsp;생성&amp;nbsp;날짜:&amp;nbsp;요청이&amp;nbsp;한&amp;nbsp;곳에&amp;nbsp;몰림&lt;br /&gt;-&amp;nbsp;예를&amp;nbsp;들어,&amp;nbsp;파티션키를&amp;nbsp;생성&amp;nbsp;날짜,&amp;nbsp;정렬키를&amp;nbsp;아이템&amp;nbsp;아이디로&amp;nbsp;만들면,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;오늘&amp;nbsp;온&amp;nbsp;요청(파티션키&amp;nbsp;=&amp;nbsp;'2021-02-18')은&amp;nbsp;한&amp;nbsp;파티션(물리적&amp;nbsp;공간)에&amp;nbsp;몰리게&amp;nbsp;됨&lt;br /&gt;-&amp;nbsp;마찬가지로,&amp;nbsp;파티션키&amp;nbsp;종류가&amp;nbsp;적은&amp;nbsp;경우엔&amp;nbsp;분산되는&amp;nbsp;속성을&amp;nbsp;파티션키로&amp;nbsp;하는&amp;nbsp;게&amp;nbsp;나음&lt;br /&gt;&lt;br /&gt;다른&amp;nbsp;우회&amp;nbsp;방법&lt;br /&gt;-&amp;nbsp;날짜를&amp;nbsp;꼭&amp;nbsp;파티션키로&amp;nbsp;써야하는&amp;nbsp;경우,&amp;nbsp;날짜&amp;nbsp;뒤에&amp;nbsp;랜덤한&amp;nbsp;값을&amp;nbsp;붙여서&amp;nbsp;골고루&amp;nbsp;분산되게&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 2021-02-18.123 처럼, 0~200 사이의 값을 넣는 것&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;근데&amp;nbsp;이렇게&amp;nbsp;하면&amp;nbsp;쿼리로&amp;nbsp;해당&amp;nbsp;날짜의&amp;nbsp;데이터를&amp;nbsp;조회할&amp;nbsp;수&amp;nbsp;없음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 0~200까지 조합해서 다 합해야 함&lt;br /&gt;-&amp;nbsp;조회할&amp;nbsp;때,&amp;nbsp;날짜&amp;nbsp;+&amp;nbsp;주문&amp;nbsp;아이디&amp;nbsp;조합&amp;nbsp;같은&amp;nbsp;경우라면,&amp;nbsp;주문&amp;nbsp;아이디&amp;nbsp;기반의&amp;nbsp;난수를&amp;nbsp;생성해서&amp;nbsp;골고루&amp;nbsp;분산되게&amp;nbsp;하는&amp;nbsp;방법도&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 2021-02-18 에 주문번호 10423번이면, 10 % 100 을 적용해서, 2021-02-18.423 처럼 하는 방법&lt;br /&gt;-&amp;nbsp;시계열&amp;nbsp;처리가&amp;nbsp;필요한&amp;nbsp;경우,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;하루에&amp;nbsp;한&amp;nbsp;개의&amp;nbsp;테이블을&amp;nbsp;생성하고,&amp;nbsp;해당&amp;nbsp;테이블의&amp;nbsp;RCU/WCU을&amp;nbsp;고정함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;파티션키는&amp;nbsp;날짜,&amp;nbsp;정렬키를&amp;nbsp;시간으로&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;하루에&amp;nbsp;한&amp;nbsp;테이블에만&amp;nbsp;처리가&amp;nbsp;몰리도록&amp;nbsp;해서&amp;nbsp;초과되지&amp;nbsp;않게&amp;nbsp;관리함&lt;br /&gt;&lt;br /&gt;보안&lt;br /&gt;-&amp;nbsp;암호화가&amp;nbsp;필요한&amp;nbsp;데이터(개인정보)&lt;br /&gt;-&amp;nbsp;기본적으로&amp;nbsp;암호화됨&lt;br /&gt;-&amp;nbsp;암호화&amp;nbsp;키로&amp;nbsp;별도로&amp;nbsp;암호화할&amp;nbsp;수&amp;nbsp;있음&amp;nbsp;(암호화&amp;nbsp;적용해도&amp;nbsp;성능&amp;nbsp;영향&amp;nbsp;거의&amp;nbsp;없음)&lt;br /&gt;-&amp;nbsp;EncryptionClient&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;아이템별로&amp;nbsp;보안&amp;nbsp;적용할&amp;nbsp;수&amp;nbsp;있음&amp;nbsp;(사이닝)&lt;br /&gt;-&amp;nbsp;예:&amp;nbsp;정부의&amp;nbsp;개인정보에&amp;nbsp;맞추기&amp;nbsp;위해&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;전화번호&amp;nbsp;같은&amp;nbsp;것들이&amp;nbsp;암호화돼서&amp;nbsp;저장됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;client&amp;nbsp;수준의&amp;nbsp;암호화도&amp;nbsp;있음&lt;br /&gt;&lt;br /&gt;TTL&lt;br /&gt;-&amp;nbsp;성능에&amp;nbsp;영향&amp;nbsp;없음&lt;br /&gt;-&amp;nbsp;Manage&amp;nbsp;TTL&amp;nbsp;에서&amp;nbsp;켜주면&amp;nbsp;됨&lt;br /&gt;-&amp;nbsp;아이템에서&amp;nbsp;TTL&amp;nbsp;대상&amp;nbsp;속성을&amp;nbsp;정해주면&amp;nbsp;됨&lt;br /&gt;-&amp;nbsp;세션&amp;nbsp;구현&amp;nbsp;예제&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;{&amp;nbsp;PK:&amp;nbsp;username,&amp;nbsp;SK:&amp;nbsp;sessionId,&amp;nbsp;TTL:&amp;nbsp;expireTime&amp;nbsp;}&lt;br /&gt;-&amp;nbsp;TTL&amp;nbsp;적용&amp;nbsp;권한에&amp;nbsp;IAM을&amp;nbsp;설정할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;백업할&amp;nbsp;때에도&amp;nbsp;적용됨&lt;br /&gt;-&amp;nbsp;만료된&amp;nbsp;아이템은&amp;nbsp;48시간&amp;nbsp;이내에&amp;nbsp;삭제됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;만료되었는데&amp;nbsp;삭제되지&amp;nbsp;않은&amp;nbsp;경우,&amp;nbsp;쿼리,&amp;nbsp;스캔&amp;nbsp;결과에&amp;nbsp;나올&amp;nbsp;수&amp;nbsp;있음?&lt;br /&gt;&lt;br /&gt;글로벌&amp;nbsp;테이블&lt;br /&gt;-&amp;nbsp;여러&amp;nbsp;리전&amp;nbsp;간&amp;nbsp;리플리케이션&amp;nbsp;제공&lt;br /&gt;-&amp;nbsp;다이나모디비&amp;nbsp;스트림으로&amp;nbsp;리얼&amp;nbsp;타임으로&amp;nbsp;전달&lt;br /&gt;-&amp;nbsp;각&amp;nbsp;리전&amp;nbsp;간&amp;nbsp;동일한&amp;nbsp;스킴의&amp;nbsp;테이블을&amp;nbsp;만들고,&amp;nbsp;글로벌&amp;nbsp;테이블로&amp;nbsp;묶으면&amp;nbsp;됨&lt;br /&gt;-&amp;nbsp;eventually&amp;nbsp;consistent&amp;nbsp;로&amp;nbsp;동기화됨&lt;br /&gt;&lt;br /&gt;&lt;b&gt;스트림&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;time-ordered&amp;nbsp;sequence&lt;br /&gt;-&amp;nbsp;&lt;b&gt;24시간&amp;nbsp;동안&amp;nbsp;저장됨&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;스트림&amp;nbsp;이벤트를&amp;nbsp;받아서&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;스트림&amp;nbsp;데이터는&amp;nbsp;별도로&amp;nbsp;저장됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;다이나모디비&amp;nbsp;스트림에&amp;nbsp;액세스가&amp;nbsp;필요함&lt;br /&gt;-&amp;nbsp;프라이머리키&amp;nbsp;정보가&amp;nbsp;전달됨&lt;br /&gt;-&amp;nbsp;ES&amp;nbsp;인덱스&amp;nbsp;갱신&amp;nbsp;예&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Optimistic&amp;nbsp;Locking&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;동시&amp;nbsp;업데이트&amp;nbsp;이슈&lt;br /&gt;-&amp;nbsp;다이나모디비는&amp;nbsp;락킹을&amp;nbsp;지원하지&amp;nbsp;않음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;Optimistic&amp;nbsp;Locking&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;작업&amp;nbsp;중인&amp;nbsp;항목이&amp;nbsp;마지막으로&amp;nbsp;읽은&amp;nbsp;이후&amp;nbsp;덮어씌워지지&amp;nbsp;않은지&amp;nbsp;확인하는&amp;nbsp;전략&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;conditional&amp;nbsp;write&amp;nbsp;로&amp;nbsp;구현&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;PutItem,&amp;nbsp;UpdateItem,&amp;nbsp;DeleteItem&amp;nbsp;메서드의&amp;nbsp;옵션으로&amp;nbsp;&amp;nbsp;지원&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;ConditionExpression&amp;nbsp;파라미터&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;키가&amp;nbsp;없을&amp;nbsp;때에만&amp;nbsp;허용&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;속성이&amp;nbsp;특정&amp;nbsp;값인&amp;nbsp;경우에만&amp;nbsp;허용&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;버전&amp;nbsp;넘버로&amp;nbsp;관리하는&amp;nbsp;예&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;아이템에&amp;nbsp;버전을&amp;nbsp;명시하는&amp;nbsp;속성을&amp;nbsp;별도로&amp;nbsp;둠&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;업데이트&amp;nbsp;전에&amp;nbsp;아이템의&amp;nbsp;버전을&amp;nbsp;가져옴&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;업데이트할&amp;nbsp;때,&amp;nbsp;동일한&amp;nbsp;버전일&amp;nbsp;때만&amp;nbsp;업데이트&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 버전이 바뀌었다면 실패. 성공할 때까지 다시 수행&lt;br /&gt;&lt;br /&gt;LSI&lt;br /&gt;-&amp;nbsp;파티션&amp;nbsp;내에서&amp;nbsp;새로운&amp;nbsp;인덱스를&amp;nbsp;만듬&lt;br /&gt;-&amp;nbsp;LSI는&amp;nbsp;파티션에&amp;nbsp;대해서&amp;nbsp;고유할&amp;nbsp;필요가&amp;nbsp;없음&lt;br /&gt;-&amp;nbsp;consistent&amp;nbsp;를&amp;nbsp;선택해서&amp;nbsp;조회할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;테이블&amp;nbsp;생성할&amp;nbsp;때만&amp;nbsp;추가됨&lt;br /&gt;&lt;br /&gt;GSI&lt;br /&gt;-&amp;nbsp;파티션&amp;nbsp;키로&amp;nbsp;새로&amp;nbsp;정렬해야&amp;nbsp;할&amp;nbsp;때&amp;nbsp;사용&lt;br /&gt;-&amp;nbsp;비동기로&amp;nbsp;업데이트됨&amp;nbsp;(eventually&amp;nbsp;consistent)&lt;br /&gt;-&amp;nbsp;GSI&amp;nbsp;를&amp;nbsp;위한&amp;nbsp;RCU/WCU를&amp;nbsp;설정해야&amp;nbsp;함&lt;br /&gt;&lt;br /&gt;보조키&lt;br /&gt;-&amp;nbsp;사용하되&amp;nbsp;최소로&amp;nbsp;유지할&amp;nbsp;것&lt;br /&gt;-&amp;nbsp;인덱스&amp;nbsp;사이즈를&amp;nbsp;최대한&amp;nbsp;작게&amp;nbsp;유지할&amp;nbsp;것&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;project&amp;nbsp;item&lt;br /&gt;-&amp;nbsp;자주&amp;nbsp;사용되는&amp;nbsp;쿼리인&amp;nbsp;경우,&amp;nbsp;모든&amp;nbsp;속성을&amp;nbsp;project&amp;nbsp;할&amp;nbsp;것&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;LSI의&amp;nbsp;projected&amp;nbsp;attribute&amp;nbsp;에&amp;nbsp;없는&amp;nbsp;경우,&amp;nbsp;다이나모디비가&amp;nbsp;전체&amp;nbsp;테이블을&amp;nbsp;스캔함&lt;br /&gt;&lt;br /&gt;&lt;b&gt;싱글&amp;nbsp;테이블&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;연관된&amp;nbsp;데이터를&amp;nbsp;하나의&amp;nbsp;테이블에&amp;nbsp;넣는&amp;nbsp;것을&amp;nbsp;추천&lt;br /&gt;-&amp;nbsp;&lt;b&gt;단,&amp;nbsp;싱글테이블로&amp;nbsp;만들&amp;nbsp;땐,&amp;nbsp;테이블을&amp;nbsp;만들기&amp;nbsp;전에&amp;nbsp;쿼리를&amp;nbsp;설계해야&amp;nbsp;함&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;특히,&amp;nbsp;LSI&amp;nbsp;는&amp;nbsp;테이블&amp;nbsp;생성할&amp;nbsp;때만&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있기&amp;nbsp;때문이기도&amp;nbsp;함&lt;br /&gt;-&amp;nbsp;먼저&amp;nbsp;관계&amp;nbsp;모델을&amp;nbsp;생성한&amp;nbsp;후에&amp;nbsp;싱글&amp;nbsp;테이블로&amp;nbsp;설계&lt;br /&gt;-&amp;nbsp;성능과&amp;nbsp;쿼리를&amp;nbsp;위한&amp;nbsp;것임&lt;br /&gt;-&amp;nbsp;쿼리를&amp;nbsp;먼저&amp;nbsp;생성&lt;br /&gt;&lt;br /&gt;음원&amp;nbsp;테이블&amp;nbsp;설계&amp;nbsp;예:&lt;br /&gt;-&amp;nbsp;작가,&amp;nbsp;노래,&amp;nbsp;앨범&amp;nbsp;관계&amp;nbsp;모델을&amp;nbsp;설계&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;쿼리를&amp;nbsp;설계&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;아티스트별&amp;nbsp;곡&amp;nbsp;찾기&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;장르별&amp;nbsp;앨범&amp;nbsp;찾기&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;연도별&amp;nbsp;아티스트의&amp;nbsp;앨범&amp;nbsp;찾기&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 노래 이름으로 찾기&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;쿼리를&amp;nbsp;설계&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;아티스트별&amp;nbsp;곡&amp;nbsp;찾기:&amp;nbsp;'PK&amp;nbsp;=&amp;nbsp;아티스트'로&amp;nbsp;조회&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;장르별&amp;nbsp;앨범&amp;nbsp;찾기:&amp;nbsp;장르를&amp;nbsp;GSI로&amp;nbsp;만들어서&amp;nbsp;조회&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;연도별&amp;nbsp;아티스트의&amp;nbsp;앨범&amp;nbsp;찾기:&amp;nbsp;LSI로&amp;nbsp;출시연도를&amp;nbsp;설정해서&amp;nbsp;조회&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;노래&amp;nbsp;이름으로&amp;nbsp;찾기:&amp;nbsp;노래&amp;nbsp;이름을&amp;nbsp;GSI로&amp;nbsp;만들어서&amp;nbsp;조회&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;아티스트&amp;nbsp;이름을&amp;nbsp;변경하려고&amp;nbsp;한다면?&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 데이터가 여러 번 중복되게 됨. 데이터를 비정규화하기 때문.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;업데이트하기&amp;nbsp;위해선&amp;nbsp;트랜잭션이&amp;nbsp;필요함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;한&amp;nbsp;번의&amp;nbsp;오퍼레이션으로&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&lt;br /&gt;시계열&amp;nbsp;데이터&lt;br /&gt;-&amp;nbsp;한&amp;nbsp;시간&amp;nbsp;단위에&amp;nbsp;하나의&amp;nbsp;테이블을&amp;nbsp;두는&amp;nbsp;게&amp;nbsp;나음&lt;br /&gt;-&amp;nbsp;스캐닝이&amp;nbsp;자주&amp;nbsp;필요한&amp;nbsp;경우가&amp;nbsp;특히&amp;nbsp;그럼&amp;nbsp;(예:&amp;nbsp;분기별&amp;nbsp;집계)&lt;br /&gt;-&amp;nbsp;아니면&amp;nbsp;AWS&amp;nbsp;TimeStream&amp;nbsp;을&amp;nbsp;써도&amp;nbsp;됨&lt;br /&gt;&lt;br /&gt;싱글&amp;nbsp;테이블을&amp;nbsp;만들려고&amp;nbsp;너무&amp;nbsp;무리하진&amp;nbsp;말&amp;nbsp;것&lt;br /&gt;-&amp;nbsp;드래곤&amp;nbsp;카드&amp;nbsp;게임&amp;nbsp;서비스라면,&amp;nbsp;아래&amp;nbsp;두&amp;nbsp;테이블은&amp;nbsp;분리하는&amp;nbsp;것이&amp;nbsp;좋음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;드래곤&amp;nbsp;정보&amp;nbsp;테이블&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;게임&amp;nbsp;정보&amp;nbsp;테이블&lt;br /&gt;&lt;br /&gt;DAX&lt;br /&gt;-&amp;nbsp;한자리수&amp;nbsp;밀리세컨드&lt;br /&gt;- 마이크로세컨드로 원하는 경우. in-memory 캐시&lt;br /&gt;-&amp;nbsp;DAX&amp;nbsp;액셀러레이터&lt;br /&gt;-&amp;nbsp;인메모리&amp;nbsp;캐시임&lt;br /&gt;-&amp;nbsp;API&amp;nbsp;호환됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;테이블&amp;nbsp;대신&amp;nbsp;클러스터만&amp;nbsp;보면&amp;nbsp;됨&lt;br /&gt;-&amp;nbsp;쓰기보다&amp;nbsp;리드가&amp;nbsp;훨씬&amp;nbsp;큰&amp;nbsp;경우&amp;nbsp;유리함&lt;br /&gt;-&amp;nbsp;작은&amp;nbsp;아이템이&amp;nbsp;다른&amp;nbsp;아이템보다&amp;nbsp;많이&amp;nbsp;조회되는&amp;nbsp;경우&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;쇼핑몰의&amp;nbsp;오늘의&amp;nbsp;인기&amp;nbsp;아이템&lt;br /&gt;-&amp;nbsp;별도&amp;nbsp;비용&amp;nbsp;있음&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이후에 다이나모디비를 적용하면서 헷갈렸던 개념들을 정리해둠&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ohgyun.tistory.com/808&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ohgyun.tistory.com/808&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/DB</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/807</guid>
      <comments>https://ohgyun.com/807#entry807comment</comments>
      <pubDate>Sun, 14 Mar 2021 12:57:26 +0900</pubDate>
    </item>
    <item>
      <title>구글 앱스 스크립트에서 비동기 작업 추가하기</title>
      <link>https://ohgyun.com/806</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2021.03.10&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; 구글 앱스 스크립트, 구글 앱 스크립트, Google Apps Script, gas, background, setTimeout&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;구글 스프레드시트와 슬랙, 구글 앱스 스크립트로 간단한 자동화 태스크를 구축해뒀다.&lt;/p&gt;
&lt;p&gt;슬랙을 API 창구로, 구글 앱스 스크립트를 앱 서버로, 스프레드시트를 디비로 사용하고 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들면 이런 작업이다.&lt;/p&gt;
&lt;p&gt;1. 슬랙에서 /find_stock_price 같은 슬래시 명령을 치면&lt;/p&gt;
&lt;p&gt;2. 구글 앱스 스크립트에서 요청을 받아서&lt;/p&gt;
&lt;p&gt;3. 스프레드시트의 값을 수정하거나 조회한 후에 슬랙으로 응답해주는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;태스크가 조금 복잡해지면서 작업의 응답 시간이 길어지게 됐다.&lt;/p&gt;
&lt;p&gt;문제는 슬랙 커맨드의 타임아웃이 3초라는 것...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;슬랙 요청에 대해 짧은 응답을 먼저 주고,&lt;/p&gt;
&lt;p&gt;긴 작업을 수행한 후에 파라미터로 받은 response_url 로 다시 메시지를 주면 간단한 일이지만,&lt;/p&gt;
&lt;p&gt;안타깝게도 구글 앱스 스크립트는 모든 스크립트가 동기로 동작한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;setTimeout()을 지원하지 않는다.&lt;/p&gt;
&lt;p&gt;즉, 일반적인 자바스크립트처럼 비동기로 백그라운드 작업을 실행하고 응답을 먼저 줄 수가 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;V8 런타임이 추가되면서 Promise 나 async 함수가 추가됐지만,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;내부적인 동작일 뿐 함수 자체는 동기로 동작한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;어떻게 하면 될까?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ClockTrigger 를 사용하는 방법으로 우회했다.&lt;/p&gt;
&lt;p&gt;요청이 들어오면 바로 직후에 실행되는 타임 트리거를 생성하고 먼저 응답한다.&lt;/p&gt;
&lt;p&gt;슬랙 메시지에 응답하기 위한 URL은 스프레드시트에 저장해두는 방법.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;... 장황하지만 동작은 잘 된다.&lt;/p&gt;
&lt;p&gt;아래는 코드 스니핏.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;function doPost(e) {&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; const command = e.parameter.command;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; const responseUrl = e.parameter.response_url;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; let outputText = '';&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; if (command === '/long_task') {&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; runAsyncTask('longTask', responseUrl);&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; outputText = '작업 중...';&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; } ... 중략&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; const output = {&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;text:&amp;nbsp;outputText&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; return ContentService.createTextOutput(JSON.stringify(output))&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .setMimeType(ContentService.MimeType.JSON);&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;function&amp;nbsp;runAsyncTask(funcName,&amp;nbsp;responseUrl)&amp;nbsp;{&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;spreadsheet&amp;nbsp;=&amp;nbsp;SpreadsheetApp.getActiveSpreadsheet();&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const sheet = spreadsheet.getSheetByName('sample');&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; // responseUrl 을 스프레드시트에 기록해둔다&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sheet.getRange('A1').setValue(responseUrl);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #781b33;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ScriptApp.newTrigger(funcName)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #781b33;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.timeBased()&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #781b33;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.after(10)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #781b33;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.create();&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;function longTask() {&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; // 오래 걸리는 작업&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; doSomethingLongTask();&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; // responseUrl 을 가져온다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const&amp;nbsp;spreadsheet&amp;nbsp;=&amp;nbsp;SpreadsheetApp.getActiveSpreadsheet();&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const sheet = spreadsheet.getSheetByName('sample');&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; const responseUrl = sheet.getRange('A1').getValue();&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; // 슬랙으로 다시 전달&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; UrlFetchApp.fetch(responseUrl, {&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; method: 'post',&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; contentType: 'application/json',&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; payload: JSON.stringify({ text: '작업 완료' })&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;br /&gt;- Slack Message Interaction: &lt;a href=&quot;https://api.slack.com/legacy/interactive-messages&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;api.slack.com/legacy/interactive-messages&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;- ClockTrigger API: &lt;a href=&quot;https://developers.google.com/apps-script/reference/script/clock-trigger-builder#after(Integer)&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;developers.google.com/apps-script/reference/script/clock-trigger-builder#after(Integer)&lt;/a&gt;&lt;/p&gt;</description>
      <category>Daylogs/Etc</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/806</guid>
      <comments>https://ohgyun.com/806#entry806comment</comments>
      <pubDate>Wed, 10 Mar 2021 01:34:15 +0900</pubDate>
    </item>
    <item>
      <title>DynamoDB: 파티션키와 정렬키의 이해</title>
      <link>https://ohgyun.com/805</link>
      <description>&lt;p&gt;&lt;b&gt;발생일: &lt;/b&gt;2021.03.09&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; DynamoDB, 다이나모디비, Partition Key, Sort Key, 파티션키, 정렬키, PK, SK, GSI&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;동료들이 다이나모디비의 파티션키(Partition Key)와 정렬키(Sort Key)의 관계를 헷갈려한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;처음엔 개념이 잘 잡히진 않았는데, 알고보니 간단하고 명확한 개념이었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다이나모디비로 설계하면서 자꾸 복잡하다고 느끼는 건,&lt;/p&gt;
&lt;p&gt;설계 시점부터 스케일 아웃을 고려하고 있기 때문인 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;RDS에선 스케일을 고려했다기보단, 이름 그대로 관계(relation)에 집중해 설계했던 것 같다.&lt;/p&gt;
&lt;p&gt;사용할 땐 쿼리를 조합하면 되니, 설계 시점엔 확장이나 호출 샘플보단 관계를 고민했다.&lt;/p&gt;
&lt;p&gt;(다른 디비를 설계할 때에도 지금처럼 스케일을 고려해 설계했다면 어땠을까 싶다...)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여튼, 다이나모디비는 스케일 아웃에 중점을 둔 디비라는 걸 염두해두면 파티션키와 정렬키의 관계를 이해하기 쉽다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;파티션키는 물리적 공간인 파티션을 특정하는 키다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- 스케일이 아무리 커져도 주소를 알고 있어서 빠르게 가져올 수 있다.&lt;/p&gt;
&lt;p&gt;- 파티션키로는 일치하는 값만 가져올 수 있고, 조건문으로 작성할 수 없는 이유이기도 하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;정렬키는 파티션 내에서 정렬하는 기준 값이다. (검색을 위한 최소의 조건이다)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- Number, Binary, String 타입을 지원한다. String 이라면 utf-8 기준으로 정렬된다.&lt;/p&gt;
&lt;p&gt;- 단순한 (문자열) 인덱스라고 생각하면 될 듯 하다.&lt;/p&gt;
&lt;p&gt;- 단순정렬이기 때문에 파티션의 사이즈가 커도 빠르게 가져올 수 있다. &lt;span style=&quot;color: #333333;&quot;&gt;(eq, lt, gt 등의 비교 조건과 between, begin_with 만 지원한다)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이런 구조의 디비라면 빠르게 가져올 수 밖에 없을 것 같다.&lt;/p&gt;
&lt;p&gt;대신, 제약 사항이 크고 유연하게 원하는 아이템을 조회하기 어렵다.&lt;/p&gt;
&lt;p&gt;기본적인 조회 방법은 일치하는 파티션키를 주고, 정렬키로 특정 범위를 조회하는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;테이블에 보조 인덱스인 GSI(Global Secondary Index)를 만들면,&lt;/p&gt;
&lt;p&gt;아이템을 조회하기 위한 또 다른 PK와 SK를 추가할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;GSI를 만들면서 파티션키와 정렬키를 정할 수 있는데,&lt;/p&gt;
&lt;p&gt;저장할 아이템의 속성 중 하나를 파티션키로, 다른 하나를 정렬키로 정하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;GSI을 추가하면 원본 테이블을 복제해 새로운 테이블을 만드는 방식이다.&lt;/p&gt;
&lt;p&gt;복제한 원본 테이블의 아이템을&lt;/p&gt;
&lt;p&gt;- GSI의 파티션키로 새로운 물리 공간을 할당해 저장하고,&lt;/p&gt;
&lt;p&gt;- GSI의 정렬키로 정렬해 저장해둔다.&lt;/p&gt;
&lt;p&gt;- 원본 테이블이 변경되면 GSI의 테이블도 동기화하는 구조다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;원본 테이블과 마찬가지로,&lt;/p&gt;
&lt;p&gt;GSI도 파티션키와 정렬키로 특정 공간에 정렬돼 저장되어 있기 때문에,&lt;/p&gt;
&lt;p&gt;원본 테이블과 동일한 성능 수준으로 조회할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;물론, 빠른 확장과 안정적인 성능을 위해 다양한 제약이 있다.&lt;/p&gt;
&lt;p&gt;- 아이템 하나의 크기는 400KB&lt;/p&gt;
&lt;p&gt;- 한 번 조회할 때 최대 크기는 1MB&lt;/p&gt;
&lt;p&gt;- 배치로 가져올 때에도, 최대 100개, 16MB까지&lt;/p&gt;
&lt;p&gt;- 배치로 쓸 때에도, 최대 25개, 16MB까지&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 내용과 관련해 좀 더 자세한 내용은 아래 키워드로 검색해보면 된다.&lt;/p&gt;
&lt;p&gt;- DynamoDB Stream&lt;/p&gt;
&lt;p&gt;- GSI Sparse Index&lt;/p&gt;
&lt;p&gt;- Eventual, Strong Consistent&lt;/p&gt;
&lt;p&gt;- Hot Partition&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;제약 사항이 많아서 설계 과정이 어렵긴 하지만,&lt;/p&gt;
&lt;p&gt;구조를 알고 나니 스케일 확장에도 성능은 걱정되지 않는다. :D&lt;/p&gt;</description>
      <category>Daylogs/DB</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/805</guid>
      <comments>https://ohgyun.com/805#entry805comment</comments>
      <pubDate>Tue, 9 Mar 2021 00:42:26 +0900</pubDate>
    </item>
    <item>
      <title>npm registry 를 찾지 못하는 이슈</title>
      <link>https://ohgyun.com/804</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2021.02.22&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; yarn config, npm config, registry, npmrc, private registry&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;갑자기 전체 태스크가 실패했고, 건너자리 A가 코드 원복까지 했는데도 계속 지속됐다.&lt;/p&gt;
&lt;p&gt;에러 문구를 보니 특정 npm 모듈의 리파지터리를 찾지 못하는 문제다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;node_modules 디렉토리를 날리고 새로 설치해도 동일한 문제가 발생했다고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이번에 npm registry 를 변경하는 작업을 준비하고 있었는데,&lt;/p&gt;
&lt;p&gt;새로 설치된 모듈이 아직 준비되지 않은 새 레지스트리를 바라본 것이 문제였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;왜 그런걸까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이번에 옮기려던 레지스트리는 도메인과 권한이 기존과 모두 달랐다.&lt;/p&gt;
&lt;p&gt;예를 들어, https://&lt;b&gt;old_registry&lt;/b&gt;.example.com 에서 https://&lt;b&gt;new_registry&lt;/b&gt;.example.com 으로 옮기는 셈이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;개발자들이 레지스트리 설정을 깜빡하는 경우가 있어서,&lt;/p&gt;
&lt;p&gt;아래처럼 package.json 의 preinstall 스크립트로 config 를 설정하는 걸 미리 넣어뒀었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;[package.json]&lt;/p&gt;
&lt;p&gt;{&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; ...&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; &quot;preinstall&quot;: &quot;yarn config set registry https://&lt;b&gt;old_registry.&lt;/b&gt;example.com&quot;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; ...&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;스크립트가 있어서 문제될 게 없다고 생각했는데,&lt;/p&gt;
&lt;p&gt;문제는 테스트하던 개발자가 로컬 디바이스에서 CLI로 레지스트리를 설정했기 때문이었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; $ npm config set registry https://new_registry.example.com&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;npm 의 설정이 yarn 의 설정에 우선한 것이 문제였다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다른 포스트를 찾아보니, config 에 대한 우선순위가 아래처럼 된다고 한다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;local&amp;nbsp;npmrc&amp;nbsp;&amp;gt;&amp;nbsp;global&amp;nbsp;npmrc&amp;nbsp;&amp;gt;&amp;nbsp;local&amp;nbsp;yarnrc&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여튼, 우리는 아래처럼 정리했다.&lt;/p&gt;
&lt;p&gt;- preinstall 에서 처리하던 설정은 모두 제거&lt;/p&gt;
&lt;p&gt;- 레지스트리는 .npmrc 파일에 정의&lt;/p&gt;
&lt;p&gt;- 각 개발자들의 로컬 npm config 는 정리&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;.npmrc 파일에 레지스트리를 추가하려면, 파일을 생성해서 아래 내용을 넣어주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;registry=https://npm.bar.com&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt; &lt;/span&gt;참고로 우리는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/verdaccio/verdaccio&quot;&gt;버다씨오(verdaccio)&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 사용해서 레지스트리를 구축해뒀었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;헌데, 버다씨오에서 해주던 프라이빗 리파지터리 기능을 이제는 깃헙에서도 할 수 있다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리 팀의 레지스트리도 깃헙으로 다 옮겼다!&lt;/p&gt;
&lt;p&gt;- 깃헙 패키지: &lt;a href=&quot;https://github.com/features/packages&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;github.com/features/packages&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;yarnrc, npmrc 우선순위:&amp;nbsp;&lt;a href=&quot;https://qkrdudwls99.medium.com/priority-of-yarnrc-npmrc-a47dc01797e6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;qkrdudwls99.medium.com/priority-of-yarnrc-npmrc-a47dc01797e6&lt;/a&gt;&lt;/p&gt;</description>
      <category>Daylogs/Javascript</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/804</guid>
      <comments>https://ohgyun.com/804#entry804comment</comments>
      <pubDate>Tue, 9 Mar 2021 00:04:30 +0900</pubDate>
    </item>
    <item>
      <title>효과적인 DynamoDB 디자인 및 활용</title>
      <link>https://ohgyun.com/803</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2020.02.18&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; nosql, dynamodb, 다이나모디비&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;이번엔 디비를 NoSQL로 AWS의 DynamoDB를 사용해서 구성해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아키텍처 디자인을 어떻게 해야할까 고민하던 차에, A가 리인벤트 동영상을 추천해줬는데 내용이 좋다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/8rEsuvdL17s&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;youtu.be/8rEsuvdL17s&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 보면서 정리해둔 내용.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;- 한 애플리케이션에 한 개의 테이블&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;파티션&amp;nbsp;키는&amp;nbsp;데이터를&amp;nbsp;골고루&amp;nbsp;분산하는&amp;nbsp;용도로&amp;nbsp;(데이터&amp;nbsp;분배&amp;nbsp;결정)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;고유&amp;nbsp;값이&amp;nbsp;많은&amp;nbsp;속성(카디널리티가&amp;nbsp;높은&amp;nbsp;속성)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;균일한&amp;nbsp;비율로&amp;nbsp;무작위로&amp;nbsp;요청되는&amp;nbsp;속성&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;정렬&amp;nbsp;키는&amp;nbsp;기존&amp;nbsp;RDS에서&amp;nbsp;인덱스를&amp;nbsp;사용하는&amp;nbsp;느낌으로&amp;nbsp;(쿼리,&amp;nbsp;다양화)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;1:n,&amp;nbsp;m:n&amp;nbsp;관계&amp;nbsp;모델링에&amp;nbsp;활용&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;효율적/선택적&amp;nbsp;조회&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;범위&amp;nbsp;조회&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;GSI&amp;nbsp;(Global&amp;nbsp;Secondary&amp;nbsp;Index)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;별도의&amp;nbsp;속성으로&amp;nbsp;파티션과&amp;nbsp;정렬키&amp;nbsp;설정&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;인덱스&amp;nbsp;크기&amp;nbsp;제한&amp;nbsp;없고,&amp;nbsp;기존&amp;nbsp;테이블&amp;nbsp;대상으로&amp;nbsp;추가/삭제&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Eventual&amp;nbsp;consistent&amp;nbsp;read&amp;nbsp;만&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;테이블&amp;nbsp;당&amp;nbsp;5개까지&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;테이블과&amp;nbsp;별도의&amp;nbsp;읽기/쓰기&amp;nbsp;용량(RCU/WCU)&amp;nbsp;할당&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;즉,&amp;nbsp;테이블&amp;nbsp;capacity는&amp;nbsp;충분한데&amp;nbsp;GSI&amp;nbsp;capacity&amp;nbsp;가&amp;nbsp;부족하면&amp;nbsp;병목&amp;nbsp;발생&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;LSI&amp;nbsp;(Local&amp;nbsp;Secondary&amp;nbsp;Index)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;테이블과&amp;nbsp;동일한&amp;nbsp;파티션키.&amp;nbsp;파티션&amp;nbsp;내에서&amp;nbsp;정렬&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;인덱스는&amp;nbsp;10기가&amp;nbsp;단위로&amp;nbsp;데이터와&amp;nbsp;함께&amp;nbsp;저장&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;테이블을&amp;nbsp;생성할&amp;nbsp;때만&amp;nbsp;설정할&amp;nbsp;수&amp;nbsp;있음&amp;nbsp;(추가/삭제&amp;nbsp;불가)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;테이블에&amp;nbsp;할당된&amp;nbsp;RCU/WCU&amp;nbsp;사용&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Eventual&amp;nbsp;+&amp;nbsp;Strong&amp;nbsp;consistent&amp;nbsp;선택&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;테이블&amp;nbsp;당&amp;nbsp;5개까지&amp;nbsp;가능&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;읽기&amp;nbsp;용량&amp;nbsp;단위&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;1&amp;nbsp;RCU&amp;nbsp;=&amp;nbsp;초당&amp;nbsp;4KB&amp;nbsp;처리&amp;nbsp;단위&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Eventual&amp;nbsp;consistent&amp;nbsp;=&amp;nbsp;2회&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Strong&amp;nbsp;consistent&amp;nbsp;=&amp;nbsp;1회&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;4KB로&amp;nbsp;올림&amp;nbsp;처리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;item&amp;nbsp;크기가&amp;nbsp;7KB&amp;nbsp;면&amp;nbsp;8KB로&amp;nbsp;올림&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;즉,&amp;nbsp;가능하면&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;많이&amp;nbsp;읽어와야&amp;nbsp;함&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;쓰기&amp;nbsp;용량&amp;nbsp;단위&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;1&amp;nbsp;WCU&amp;nbsp;=&amp;nbsp;초당&amp;nbsp;1KB&amp;nbsp;처리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;1KB&amp;nbsp;단위로&amp;nbsp;올림&amp;nbsp;처리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;100B&amp;nbsp;를&amp;nbsp;써도&amp;nbsp;1KB로&amp;nbsp;처리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;즉,&amp;nbsp;가능하면&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;많이&amp;nbsp;써야&amp;nbsp;함&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;조회&amp;nbsp;(Get,&amp;nbsp;Query)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;eventual,&amp;nbsp;strong&amp;nbsp;consistent&amp;nbsp;선택&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;1MB&amp;nbsp;단위로&amp;nbsp;응답&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;결과량이&amp;nbsp;1MB를&amp;nbsp;넘을&amp;nbsp;땐&amp;nbsp;LastEvaluatedKey&amp;nbsp;를&amp;nbsp;ExclusiveStartKey&amp;nbsp;로&amp;nbsp;활용&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;PK&amp;nbsp;조회&amp;nbsp;(동일한&amp;nbsp;값&amp;nbsp;조회)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;PK&amp;nbsp;=&amp;nbsp;1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;SK&amp;nbsp;조회&amp;nbsp;(범위&amp;nbsp;값&amp;nbsp;조회)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;SK&amp;nbsp;in&amp;nbsp;10~20,&amp;nbsp;SK&amp;nbsp;beginwith&amp;nbsp;'foo.'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;LSI&amp;nbsp;조회&amp;nbsp;(테이블&amp;nbsp;용량&amp;nbsp;소진)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;PK&amp;nbsp;=&amp;nbsp;1,&amp;nbsp;LSI_SK&amp;nbsp;=&amp;nbsp;foo@bar&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;GSI&amp;nbsp;조회&amp;nbsp;(별도&amp;nbsp;용량)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;GSI_PK:&amp;nbsp;name&amp;nbsp;=&amp;nbsp;'foo',&amp;nbsp;GSI_SK:&amp;nbsp;age&amp;nbsp;&amp;gt;&amp;nbsp;20&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;조회&amp;nbsp;(Scan)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;전체&amp;nbsp;테이블&amp;nbsp;필터&amp;nbsp;적용해서&amp;nbsp;선택적&amp;nbsp;조회&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;병렬&amp;nbsp;스캔&amp;nbsp;가능하지만&amp;nbsp;RCU&amp;nbsp;유의해야&amp;nbsp;함&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;모델링&amp;nbsp;(집계&amp;nbsp;처리의&amp;nbsp;예)&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 예를 들어, 음원의 상세 정보와 월별 다운로드 수를 함께 집계한다면,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;음원&amp;nbsp;정보&amp;nbsp;아이템&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;{&amp;nbsp;PK:&amp;nbsp;'song-129',&amp;nbsp;SK:&amp;nbsp;'Details',&amp;nbsp;Title,&amp;nbsp;Artist,&amp;nbsp;...&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;음원&amp;nbsp;집계&amp;nbsp;아이템&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;{&amp;nbsp;PK:&amp;nbsp;'song-129',&amp;nbsp;SK:&amp;nbsp;'Month-2020-02',&amp;nbsp;Month:&amp;nbsp;'2020-02',&amp;nbsp;Downloads:&amp;nbsp;482320&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;여기서&amp;nbsp;가장&amp;nbsp;많이&amp;nbsp;다운로드&amp;nbsp;된&amp;nbsp;음원을&amp;nbsp;찾으려고&amp;nbsp;한다면,&amp;nbsp;GSI&amp;nbsp;설정&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;GSI_PK:&amp;nbsp;Month&amp;nbsp;=&amp;nbsp;'2020-02',&amp;nbsp;ScanIndexForwarded&amp;nbsp;=&amp;nbsp;False,&amp;nbsp;Limit&amp;nbsp;=&amp;nbsp;1&amp;nbsp;로&amp;nbsp;조회&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;모델링&amp;nbsp;(키를&amp;nbsp;다양하게&amp;nbsp;조합하고&amp;nbsp;싶은&amp;nbsp;경우)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;PK&amp;nbsp;=&amp;nbsp;아이디,&amp;nbsp;SK&amp;nbsp;=&amp;nbsp;집계를&amp;nbsp;원하는&amp;nbsp;공통&amp;nbsp;이름으로&amp;nbsp;설정&amp;nbsp;(타입),&amp;nbsp;속성은&amp;nbsp;타입에&amp;nbsp;따라&amp;nbsp;다름&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;{&amp;nbsp;PK:&amp;nbsp;'emp-100',&amp;nbsp;SK:&amp;nbsp;'master',&amp;nbsp;이름,&amp;nbsp;기타&amp;nbsp;기본&amp;nbsp;정보}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;{&amp;nbsp;PK:&amp;nbsp;'emp-100',&amp;nbsp;SK:&amp;nbsp;'state:CA',&amp;nbsp;이름,&amp;nbsp;캘리포니아&amp;nbsp;근속&amp;nbsp;정보&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;{&amp;nbsp;PK:&amp;nbsp;'emp-100,&amp;nbsp;SK:&amp;nbsp;'state:NY',&amp;nbsp;이름,&amp;nbsp;뉴욕&amp;nbsp;근속&amp;nbsp;정보&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;{&amp;nbsp;PK:&amp;nbsp;'emp-100',&amp;nbsp;SK:&amp;nbsp;'prevtitle:A',&amp;nbsp;이름,&amp;nbsp;이전&amp;nbsp;타이틀&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;{&amp;nbsp;PK:&amp;nbsp;'emp-100',&amp;nbsp;SK:&amp;nbsp;currtitle:B,&amp;nbsp;이름,&amp;nbsp;현재&amp;nbsp;타이틀&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;SK&amp;nbsp;+&amp;nbsp;이름&amp;nbsp;조합을&amp;nbsp;GSI로&amp;nbsp;설정&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;모델링&amp;nbsp;(결합&amp;nbsp;정렬키)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;계층&amp;nbsp;관계를&amp;nbsp;적용해서&amp;nbsp;조회하고&amp;nbsp;싶다면,&amp;nbsp;정렬키&amp;nbsp;이름을&amp;nbsp;계층으로&amp;nbsp;설정&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;&amp;nbsp;SK:&amp;nbsp;'foo#bar#baz'&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;쓰기&amp;nbsp;샤딩&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예를&amp;nbsp;들어,&amp;nbsp;한&amp;nbsp;디바이스에&amp;nbsp;요청이&amp;nbsp;몰릴&amp;nbsp;때&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;디바이스가&amp;nbsp;쓸&amp;nbsp;때,&amp;nbsp;샤드&amp;nbsp;개수를&amp;nbsp;서픽스로&amp;nbsp;붙여서&amp;nbsp;PK로&amp;nbsp;할당&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;디바이스&amp;nbsp;-&amp;nbsp;샤드&amp;nbsp;개수를&amp;nbsp;기록&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;비용&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;쓰기가&amp;nbsp;가장&amp;nbsp;비싼&amp;nbsp;작업&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;TTL&amp;nbsp;을&amp;nbsp;활용한&amp;nbsp;삭제와&amp;nbsp;테이블&amp;nbsp;삭제는&amp;nbsp;무료&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;모든&amp;nbsp;데이터를&amp;nbsp;보관하려고&amp;nbsp;하지&amp;nbsp;말고,&amp;nbsp;장기간&amp;nbsp;보관이나&amp;nbsp;큰&amp;nbsp;데이터는&amp;nbsp;S3를&amp;nbsp;활용&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;eventually&amp;nbsp;consistent&amp;nbsp;read&amp;nbsp;는&amp;nbsp;50%&amp;nbsp;저렴.&amp;nbsp;앱&amp;nbsp;단에서&amp;nbsp;잘&amp;nbsp;설계해서&amp;nbsp;활용하면&amp;nbsp;좋음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;프리티어의&amp;nbsp;유효기간이&amp;nbsp;없음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;25GB&amp;nbsp;스토리지&amp;nbsp;+&amp;nbsp;25&amp;nbsp;WCU,&amp;nbsp;25&amp;nbsp;RCU&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;월&amp;nbsp;2억&amp;nbsp;건&amp;nbsp;정도의&amp;nbsp;요청량임&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;규모가&amp;nbsp;커졌을&amp;nbsp;땐&amp;nbsp;예약&amp;nbsp;용량을&amp;nbsp;활용해서&amp;nbsp;절약하는&amp;nbsp;방법&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;필요한&amp;nbsp;데이터만&amp;nbsp;읽기&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;GSI는&amp;nbsp;Sparse&amp;nbsp;Index&amp;nbsp;임&amp;nbsp;(즉,&amp;nbsp;데이터가&amp;nbsp;있는&amp;nbsp;것만&amp;nbsp;인덱스를&amp;nbsp;만듦)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;아이템에&amp;nbsp;타입에&amp;nbsp;따라&amp;nbsp;필요한&amp;nbsp;정보를&amp;nbsp;넣은&amp;nbsp;후,&amp;nbsp;GSI를&amp;nbsp;활용&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;최대한&amp;nbsp;많이&amp;nbsp;쓰고,&amp;nbsp;필요한&amp;nbsp;것만&amp;nbsp;읽는&amp;nbsp;것이&amp;nbsp;좋음&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;- 필요에 따라 auto scaling 쓰면 절약할 수 있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;한 애플리케이션에 한 개의 테이블을 추천하다고 하는데, 적합한 애플리케이션의 단위는 뭘까?&lt;/p&gt;
&lt;p&gt;발표에서는 음원 사이트를 예로 들었는데, 이 테이블에 유저 정보도 포함된 걸까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&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;/p&gt;
&lt;p&gt;- 아티스트&lt;/p&gt;
&lt;p&gt;- 플레이리스트&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;음원과 아티스트는 한 테이블에 넣는 게 좋을까... 애매하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2020.02.20 추가&lt;/p&gt;
&lt;p&gt;이후에 좀 더 리서치를 해봤다.&lt;/p&gt;
&lt;p&gt;- 발표에서 의미했던 건, 도메인 또는 MSA 당 한 개의 테이블이라고 이해하면 좋을 것 같다.&lt;/p&gt;
&lt;p&gt;- 즉, 위에서 언급했던, 유저, 음원, 아티스트, 플레이리스트는 한 개의 테이블로 구성해도 좋겠다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다이나모디비와 싱글 테이블 디자인에 대한 건 나중에 정리.&lt;/p&gt;</description>
      <category>Daylogs/DB</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/803</guid>
      <comments>https://ohgyun.com/803#entry803comment</comments>
      <pubDate>Thu, 18 Feb 2021 15:55:18 +0900</pubDate>
    </item>
    <item>
      <title>JWT 개념 이해하기</title>
      <link>https://ohgyun.com/802</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt;&amp;nbsp;2021.02.09&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt;&amp;nbsp;JWT,&amp;nbsp;JSON&amp;nbsp;Web&amp;nbsp;Token,&amp;nbsp;세션&amp;nbsp;기반,&amp;nbsp;stateless,&amp;nbsp;Access&amp;nbsp;Token,&amp;nbsp;Refresh&amp;nbsp;Token,&amp;nbsp;액세스&amp;nbsp;토큰,&amp;nbsp;리프레시&amp;nbsp;토큰&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;이번엔&amp;nbsp;인증&amp;nbsp;과정에&amp;nbsp;JWT를&amp;nbsp;사용해보기로&amp;nbsp;했다.&lt;br /&gt;건너자리 B가 코드를 다 짜왔고, 자세한 설명까지 보태서 이해가 쏙 되게 공유해줬다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(좋은 내용을 쉽게 이해하고 왔으니^^)&lt;br /&gt;레거시만&amp;nbsp;운영해왔던&amp;nbsp;J에게도&amp;nbsp;JWT를&amp;nbsp;이용한&amp;nbsp;인증&amp;nbsp;관리를&amp;nbsp;설명해주려고&amp;nbsp;한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기존&amp;nbsp;환경과&amp;nbsp;비교해서&amp;nbsp;이해하기&amp;nbsp;쉽게&amp;nbsp;정리해봤다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;JWT(JSON&amp;nbsp;Web&amp;nbsp;Token)는&amp;nbsp;인증&amp;nbsp;토큰을&amp;nbsp;정의하는&amp;nbsp;방법에&amp;nbsp;대한&amp;nbsp;표준(RFC&amp;nbsp;7519)이다.&lt;br /&gt;&lt;br /&gt;(여러&amp;nbsp;서버&amp;nbsp;간에)&amp;nbsp;안전하게&amp;nbsp;인증&amp;nbsp;정보를&amp;nbsp;주고&amp;nbsp;받을&amp;nbsp;수&amp;nbsp;있도록,&amp;nbsp;&lt;b&gt;인증&amp;nbsp;토큰을&amp;nbsp;생성하고&amp;nbsp;해석하는&amp;nbsp;방법&lt;/b&gt;을&amp;nbsp;규정한다.&lt;br /&gt;토큰은&amp;nbsp;JSON&amp;nbsp;포맷의&amp;nbsp;정보와&amp;nbsp;이&amp;nbsp;정보의&amp;nbsp;위변조를&amp;nbsp;막기&amp;nbsp;위해&amp;nbsp;서명한&amp;nbsp;값으로&amp;nbsp;구성돼있다.&lt;br /&gt;&lt;br /&gt;처음이라면&amp;nbsp;익숙하지&amp;nbsp;않아서&amp;nbsp;JWT를&amp;nbsp;사용하는&amp;nbsp;용도가&amp;nbsp;헷갈릴&amp;nbsp;수&amp;nbsp;있는데,&lt;br /&gt;기존&amp;nbsp;세션&amp;nbsp;기반의&amp;nbsp;인증&amp;nbsp;관리&amp;nbsp;방법과&amp;nbsp;비교해보면&amp;nbsp;이해하기&amp;nbsp;쉽다.&lt;br /&gt;&lt;br /&gt;구현&amp;nbsp;방식은&amp;nbsp;다양할&amp;nbsp;수&amp;nbsp;있지만,&amp;nbsp;이해를&amp;nbsp;위해&amp;nbsp;대략&amp;nbsp;설명해보면&amp;nbsp;아래와&amp;nbsp;같은&amp;nbsp;차이가&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;세션&amp;nbsp;기반&amp;nbsp;인증&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;클라이언트가&amp;nbsp;로그인에&amp;nbsp;성공하면,&lt;br /&gt;-&amp;nbsp;임의의&amp;nbsp;토큰을&amp;nbsp;생성한&amp;nbsp;후,&amp;nbsp;토큰을&amp;nbsp;키로&amp;nbsp;인증&amp;nbsp;정보(사용자&amp;nbsp;아이디&amp;nbsp;등)를&amp;nbsp;값으로&amp;nbsp;세션에&amp;nbsp;저장&lt;br /&gt;-&amp;nbsp;클라이언트에&amp;nbsp;해당&amp;nbsp;토큰을&amp;nbsp;응답&lt;br /&gt;&lt;br /&gt;클라이언트가&amp;nbsp;토큰과&amp;nbsp;함께&amp;nbsp;요청을&amp;nbsp;보내면,&lt;br /&gt;-&amp;nbsp;서버는&amp;nbsp;세션에서&amp;nbsp;해당&amp;nbsp;토큰으로&amp;nbsp;저장된&amp;nbsp;인증&amp;nbsp;정보가&amp;nbsp;있는지&amp;nbsp;확인&lt;br /&gt;- 유효하다면 인증된 요청이라고 판단&lt;br /&gt;&lt;br /&gt;구체적인&amp;nbsp;구현&amp;nbsp;방식은&amp;nbsp;다양할&amp;nbsp;것이다.&lt;br /&gt;-&amp;nbsp;인증&amp;nbsp;서버를&amp;nbsp;서비스와&amp;nbsp;별도로&amp;nbsp;뒀을&amp;nbsp;수도&amp;nbsp;있고,&amp;nbsp;한&amp;nbsp;서버에서&amp;nbsp;모두&amp;nbsp;처리할&amp;nbsp;수도&amp;nbsp;있다.&lt;br /&gt;- 자체 인증 서버라면 토큰을 임의로 생성했을 테고,&lt;/p&gt;
&lt;p&gt;- OAuth를 사용해 인증 받았다면, 발급받은 액세스 토큰으로 직접 관리하거나 또는 인증 후에 별도로 관리했을 수도 있다.&lt;br /&gt;-&amp;nbsp;세션에&amp;nbsp;저장하는&amp;nbsp;정보도&amp;nbsp;서비스마다&amp;nbsp;다를&amp;nbsp;테고,&lt;br /&gt;-&amp;nbsp;별도의&amp;nbsp;세션&amp;nbsp;스토리지(예:&amp;nbsp;레디스)에&amp;nbsp;저장했을&amp;nbsp;수&amp;nbsp;있고,&amp;nbsp;서버의&amp;nbsp;메모리에&amp;nbsp;저장했거나&amp;nbsp;분산해서&amp;nbsp;클러스터링으로&amp;nbsp;관리했을&amp;nbsp;수도&amp;nbsp;있다.&lt;br /&gt;-&amp;nbsp;클라이언트에&amp;nbsp;응답할&amp;nbsp;때엔,&amp;nbsp;쿠키로&amp;nbsp;내려줬을&amp;nbsp;수도&amp;nbsp;있고&amp;nbsp;별도의&amp;nbsp;형태로&amp;nbsp;응답했을&amp;nbsp;수도&amp;nbsp;있다.&lt;br /&gt;-&amp;nbsp;클라이언트가&amp;nbsp;요청할&amp;nbsp;땐,&amp;nbsp;쿠키에&amp;nbsp;포함해서&amp;nbsp;보낼&amp;nbsp;수도&amp;nbsp;있고&amp;nbsp;헤더에&amp;nbsp;넣어서&amp;nbsp;보냈을&amp;nbsp;수도&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;중요한&amp;nbsp;건,&amp;nbsp;&lt;b&gt;세션에&amp;nbsp;상태(토큰과&amp;nbsp;인증&amp;nbsp;정보)를&amp;nbsp;저장하고&amp;nbsp;있었다&lt;/b&gt;는&amp;nbsp;것이다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;JWT&amp;nbsp;기반&amp;nbsp;인증&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;클라이언트가&amp;nbsp;로그인에&amp;nbsp;성공하면&lt;br /&gt;-&amp;nbsp;인증&amp;nbsp;정보(사용자&amp;nbsp;아이디&amp;nbsp;등)와&amp;nbsp;이&amp;nbsp;정보가&amp;nbsp;유효한지를&amp;nbsp;검증하는&amp;nbsp;값을&amp;nbsp;포함한&amp;nbsp;토큰을&amp;nbsp;생성&amp;nbsp;(예:&amp;nbsp;인증정보+유효함)&lt;br /&gt;-&amp;nbsp;검증키는&amp;nbsp;서버에&amp;nbsp;별도로&amp;nbsp;저장한&amp;nbsp;비밀키로&amp;nbsp;생성&lt;br /&gt;-&amp;nbsp;생성한&amp;nbsp;토큰을&amp;nbsp;&lt;b&gt;(서버에는&amp;nbsp;저장하지&amp;nbsp;않고)&amp;nbsp;클라이언트에&amp;nbsp;응답&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;클라이언트가&amp;nbsp;토큰과&amp;nbsp;함께&amp;nbsp;요청을&amp;nbsp;보내면,&lt;br /&gt;-&amp;nbsp;서버의&amp;nbsp;비밀키로&amp;nbsp;유효한&amp;nbsp;토큰인지&amp;nbsp;확인&lt;br /&gt;-&amp;nbsp;토큰이&amp;nbsp;유효하다면,&amp;nbsp;토큰에&amp;nbsp;저장된&amp;nbsp;인증&amp;nbsp;정보로&amp;nbsp;처리&lt;br /&gt;&lt;br /&gt;큰&amp;nbsp;차이점은,&amp;nbsp;&lt;b&gt;세션에&amp;nbsp;상태를&amp;nbsp;저장하지&amp;nbsp;않고&amp;nbsp;토큰만&amp;nbsp;활용해&amp;nbsp;인증을&amp;nbsp;처리&lt;/b&gt;하는&amp;nbsp;것이다.&lt;br /&gt;&lt;br /&gt;역시&amp;nbsp;구현&amp;nbsp;방식은&amp;nbsp;다양할&amp;nbsp;수&amp;nbsp;있으며,&amp;nbsp;중요한&amp;nbsp;특징은&amp;nbsp;다음과&amp;nbsp;같다.&lt;br /&gt;-&amp;nbsp;토큰에&amp;nbsp;인증&amp;nbsp;정보가&amp;nbsp;포함되어&amp;nbsp;있고,&amp;nbsp;공개되어&amp;nbsp;있다.&lt;br /&gt;-&amp;nbsp;서버에서는&amp;nbsp;비밀키로&amp;nbsp;유효성만&amp;nbsp;확인한다.&lt;br /&gt;-&amp;nbsp;인증&amp;nbsp;서버를&amp;nbsp;포함한&amp;nbsp;다른&amp;nbsp;서버는&amp;nbsp;동일한&amp;nbsp;비밀키를&amp;nbsp;소유하고&amp;nbsp;있어야&amp;nbsp;한다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;주로 여러 서비스가 하나의 인증 서버를 사용하는 환경에서 사용되고,&lt;br /&gt;각 서비스에서 인증을 위해 매번 인증 서버에 요청하지 않아도 되는 장점이 있다.&lt;br /&gt;&lt;br /&gt;위에서&amp;nbsp;설명했듯이,&amp;nbsp;JWT&amp;nbsp;는&amp;nbsp;이&amp;nbsp;토큰을&amp;nbsp;정의하는&amp;nbsp;방법에&amp;nbsp;대한&amp;nbsp;표준이고,&lt;br /&gt;토큰을&amp;nbsp;생성하는&amp;nbsp;방법은&amp;nbsp;홈페이지에&amp;nbsp;잘&amp;nbsp;설명되어&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;간단하게&amp;nbsp;설명하면,&amp;nbsp;토큰은&amp;nbsp;아래처럼&amp;nbsp;정의한다.&lt;br /&gt;-&amp;nbsp;A.B.C&amp;nbsp;의&amp;nbsp;형태&amp;nbsp;(각&amp;nbsp;값을&amp;nbsp;dot&amp;nbsp;으로&amp;nbsp;구분)&lt;br /&gt;- A = Base64Url(header). 헤더는 JSON 포맷으로, 타입과 검증 알고리즘 정보가 있음&lt;br /&gt;- B = Base64Url(payload). 페이로드는 JSON 포맷으로, 인증 정보를 포함&lt;br /&gt;-&amp;nbsp;C&amp;nbsp;=&amp;nbsp;HMAC(A.B,&amp;nbsp;비밀키).&amp;nbsp;서명값&lt;br /&gt;-&amp;nbsp;Base64Url&amp;nbsp;은&amp;nbsp;Base64에서&amp;nbsp;+,&amp;nbsp;/&amp;nbsp;를&amp;nbsp;각각&amp;nbsp;-,_&amp;nbsp;로&amp;nbsp;대체한&amp;nbsp;것&lt;br /&gt;&lt;br /&gt;서버에서&amp;nbsp;요청을&amp;nbsp;받으면,&amp;nbsp;토큰을&amp;nbsp;생성하는&amp;nbsp;것과&amp;nbsp;같은&amp;nbsp;방법으로&amp;nbsp;검증한다.&lt;br /&gt;- A.B.C 의 토큰을 받으면,&lt;br /&gt;-&amp;nbsp;HMAC(A.B,&amp;nbsp;비밀키)&amp;nbsp;=&amp;nbsp;C&amp;nbsp;와&amp;nbsp;동일한지&amp;nbsp;확인&lt;br /&gt;&lt;br /&gt;더불어,&amp;nbsp;토큰의&amp;nbsp;추가적인&amp;nbsp;관리를&amp;nbsp;위해&amp;nbsp;일반적으로&amp;nbsp;payload&amp;nbsp;에는&amp;nbsp;발급자,&amp;nbsp;발급시간,&amp;nbsp;만료시간&amp;nbsp;정보를&amp;nbsp;포함한다.&lt;br /&gt;&lt;br /&gt;JWT에&amp;nbsp;대한&amp;nbsp;자료는&amp;nbsp;워낙&amp;nbsp;많아서,&lt;br /&gt;개념과 용도를 파악했다면 다른 자료를 검색해서 보면 상세히 이해될 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;발급한 토큰의 보안과 관련해 몇 가지 선택적으로 관리해야 할 사항이 있다.&lt;/p&gt;
&lt;p&gt;이건 다음 포스트에...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- JWT 홈페이지의 토큰 구성 예제: &lt;a href=&quot;https://jwt.io/#debugger-io&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;jwt.io/#debugger-io&lt;/a&gt;&lt;/p&gt;</description>
      <category>Daylogs/Security</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/802</guid>
      <comments>https://ohgyun.com/802#entry802comment</comments>
      <pubDate>Thu, 11 Feb 2021 11:34:37 +0900</pubDate>
    </item>
    <item>
      <title>Salted Hashes vs. HMAC</title>
      <link>https://ohgyun.com/801</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2020.05.25&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; HMAC, salted hash&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;HMAC 과 hash 에 salt 를 적용한 게 어떤 차이가 있을까?&lt;/p&gt;
&lt;p&gt;실제로 쓰다보면 큰 차이가 없는 것 같은데...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;잘 정리된 글이 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://security.stackexchange.com/questions/29951/salted-hashes-vs-hmac&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;security.stackexchange.com/questions/29951/salted-hashes-vs-hmac&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요약하면,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;- 해시 함수는 기본적으로 길이에 제한이 있는 취약점(length extension)이 있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp; (예: 모든 해시값에 대한 사전을 만들어두고 역으로 조회)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;- 이런 사전형 복호화를 방어하기 위해 salt를 적용하는데, (예: hash(원본 + salt))&lt;/p&gt;
&lt;p&gt;&amp;nbsp; 이렇게 해도 사전으로 복호화하면 몇 가지 패턴으로 원본과 salt를 구별해낼 수 있음&lt;br /&gt;&lt;br /&gt;- 원래 HMAC(메시지 인증 코드, &lt;span style=&quot;color: #333333;&quot;&gt;hash-based&amp;nbsp;message&amp;nbsp;authentication&amp;nbsp;code&lt;/span&gt;)의 목적은 비밀번호의 단방향 해시를 생성하는 것과는 다름.&lt;/p&gt;
&lt;p&gt;&amp;nbsp; HMAC은 메시지가 공개되어 있고, key가 비밀임. 메시지의 무결성과 송신자 여부를 확인하기 위한 것임. 그래서 salt 대신 key라는 용어를 씀. 예) hmac(message, key)&lt;br /&gt;&lt;br /&gt;- hmac은 중첩적으로 수십~수만번 hash 처리함(물론 salt를 써야함). 사전으로 복호화하더라도 많은 비용이 듬.&lt;br /&gt;&lt;br /&gt;결론:&amp;nbsp;hmac&amp;nbsp;이&amp;nbsp;단순히&amp;nbsp;hash&amp;nbsp;+&amp;nbsp;salt&amp;nbsp;하는&amp;nbsp;것보단&amp;nbsp;약칸&amp;nbsp;느리지만&amp;nbsp;안전하겠음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/Security</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/801</guid>
      <comments>https://ohgyun.com/801#entry801comment</comments>
      <pubDate>Tue, 9 Feb 2021 00:49:13 +0900</pubDate>
    </item>
    <item>
      <title>lock=none, algorithm=inplace 으로 테이블 변경하기</title>
      <link>https://ohgyun.com/800</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2020.07.30&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; mysql, aurora, DB 컬럼 추가, DB 인덱스 추가, add index without lock, add column&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;크기가 큰 MySQL (Aurora) 테이블에 컬럼이나 인덱스를 추가하려고 한다.&lt;/p&gt;
&lt;p&gt;테이블 크기가 큰 경우, 락 타임이 길어져서 운영 중일 땐 문제가 발생할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;어떻게 하면 될까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;algorithm 절과 lock 절을 이용하면, 락을 걸지 않고 컬럼이나 인덱스를 추가 또는 삭제할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;컬럼 추가&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;규모가 큰 larget_table 에 new_date 란 이름으로 DATETIME 형식의 컬럼을 추가한다고 가정하면, 아래와 같이 하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; &amp;nbsp; ALTER TABLE large_table&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; &amp;nbsp; ADD new_date DATETIME null, ALGORITH = INPLACE, LOCK = NONE;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;인덱스 추가&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;large_table 의 new_date 컬럼에 new_date_index 를 추가한다고 가정하면, 아래와 같이 하면 된다.&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; ALTER TABLE large_table&lt;br /&gt;&amp;nbsp; &amp;nbsp; ADD INDEX new_date_index (new_date), ALGORITH = INPLACE, LOCK = NONE;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;시간이 조금 걸리더라도 인내를 갖고(?) 기다리면 된다.&lt;/p&gt;
&lt;p&gt;운영 중인 5천만 행 정도 되는 테이블을 수정했는데, 3분 정도 후에 특별한 문제 없이 적용됐다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;추가하려는 컬럼이나 인덱스가 여러 개라면, 한 번에 하나씩 하는 게 안전하다.&lt;/p&gt;
&lt;p&gt;ALTER TABLE 명령에서 여러 태스크를 실행하면 복잡도가 높아지면서 INPLACE 대신 COPY 로 전환될 수 있나보다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- 자세한 설명: &lt;span style=&quot;color: #333333;&quot;&gt;&lt;a href=&quot;http://www.gurubee.net/lecture/4184&quot;&gt;http://www.gurubee.net/lecture/4184&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;- 여러 컬럼은 나눠서 하는 게 좋음: &lt;span style=&quot;color: #333333;&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/46970401/mysql-add-multiple-column-to-large-table-in-optimised-way&quot;&gt;https://stackoverflow.com/questions/46970401/mysql-add-multiple-column-to-large-table-in-optimised-way&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/DB</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/800</guid>
      <comments>https://ohgyun.com/800#entry800comment</comments>
      <pubDate>Tue, 9 Feb 2021 00:39:58 +0900</pubDate>
    </item>
    <item>
      <title>DeepLearning.AI TensorFlow Developer 노트</title>
      <link>https://ohgyun.com/799</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2020.11.25&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; tensorflow, coursera, 코세라&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;내용:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;코세라의 TensorFlow Developer 강좌 노트&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.coursera.org/professional-certificates/tensorflow-in-practice&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.coursera.org/professional-certificates/tensorflow-in-practice&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(간단하게 기억을 더듬는 용도로 적어둔 노트임)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;CNN&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;DNN&amp;nbsp;(Densed&amp;nbsp;Neural&amp;nbsp;Network),&amp;nbsp;CNN&amp;nbsp;(Convolutional&amp;nbsp;Neural&amp;nbsp;Network)&lt;br /&gt;-&amp;nbsp;loss&amp;nbsp;functions:&amp;nbsp;binary_crossentropy,&amp;nbsp;categorical_corssentropy&lt;br /&gt;-&amp;nbsp;activation&amp;nbsp;functions:&amp;nbsp;relu,&amp;nbsp;softmax,&amp;nbsp;sigmoid&lt;br /&gt;- optimizer: sgd, rmsprops, adam, ...&lt;br /&gt;-&amp;nbsp;callbacks&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;on_epoch_end&lt;br /&gt;-&amp;nbsp;Convolution&amp;nbsp;(Conv2D),&amp;nbsp;Max&amp;nbsp;Pooling&lt;br /&gt;- History: history = &lt;a href=&quot;http://model.fit()&quot;&gt;model.fit()&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;Transfer&amp;nbsp;Learning:&amp;nbsp;Pre&amp;nbsp;Trained&amp;nbsp;Model,&amp;nbsp;In&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- x = &lt;a href=&quot;http://keras.layers.Flatten(last_output)&quot;&gt;keras.layers.Flatten(last_output)&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Model(pre_trained_model.input, x)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- lock train: model.trainable = False&lt;br /&gt;-&amp;nbsp;Drop&amp;nbsp;Out&lt;br /&gt;-&amp;nbsp;ImageDataGenerator&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;rescale&amp;nbsp;(normalize&amp;nbsp;image&amp;nbsp;size)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;flow_from_directory&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Image Augmentation (shear, zoom, flip, fill_mode, ..)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;class_mode:&amp;nbsp;binary,&amp;nbsp;categorical&lt;br /&gt;&lt;br /&gt;&lt;b&gt;NLP&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;Token,&amp;nbsp;Corpus&lt;br /&gt;-&amp;nbsp;Dictionary&amp;nbsp;(=word_index)&lt;br /&gt;-&amp;nbsp;Text&amp;nbsp;(=&amp;nbsp;Sentences)&amp;nbsp;-&amp;gt;&amp;nbsp;Sequences&lt;br /&gt;-&amp;nbsp;oov_token&amp;nbsp;=&amp;nbsp;out&amp;nbsp;of&amp;nbsp;vocabulary&lt;br /&gt;-&amp;nbsp;Padding:&amp;nbsp;pad_sequences&lt;br /&gt;-&amp;nbsp;Word&amp;nbsp;Embedding&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;단어를&amp;nbsp;숫자로&amp;nbsp;표현하는&amp;nbsp;다른&amp;nbsp;방식&amp;nbsp;중의&amp;nbsp;하나&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;label&amp;nbsp;에&amp;nbsp;따라&amp;nbsp;비슷한&amp;nbsp;의미지를&amp;nbsp;가진&amp;nbsp;단어를&amp;nbsp;시맨틱하게&amp;nbsp;벡터&amp;nbsp;공간에&amp;nbsp;표시&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;corpus&amp;nbsp;에서&amp;nbsp;특징&amp;nbsp;추출&lt;br /&gt;-&amp;nbsp;Embedding&amp;nbsp;Weights&amp;nbsp;=&amp;nbsp;(vocab_size,&amp;nbsp;embedding_dimension)&lt;br /&gt;-&amp;nbsp;GlobalPooling1D&amp;nbsp;=&amp;nbsp;단어&amp;nbsp;수에&amp;nbsp;관계없이&amp;nbsp;pooling&lt;br /&gt;- &lt;a href=&quot;https://projector.tensorflow.org&quot;&gt;https://projector.tensorflow.org&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 벡터(vecs.tsv), 메타데이터(meta.tsv) 를 저장해서 시각화할 수 있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;강의&amp;nbsp;깃헙&lt;br /&gt;-&amp;nbsp;Sub-words:&amp;nbsp;단어를&amp;nbsp;n-gram&amp;nbsp;으로&amp;nbsp;토크나이징해주는&amp;nbsp;기능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;단어의&amp;nbsp;순서가&amp;nbsp;존재만큼&amp;nbsp;중요하다&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;oov가&amp;nbsp;없다&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;설명링크&lt;br /&gt;-&amp;nbsp;RNN&amp;nbsp;(Recurrent&amp;nbsp;Neural&amp;nbsp;Network)&lt;br /&gt;-&amp;nbsp;Sequence&amp;nbsp;Model:&amp;nbsp;RNN&amp;nbsp;같은&amp;nbsp;걸&amp;nbsp;시퀀스&amp;nbsp;모델이라고&amp;nbsp;함&lt;br /&gt;-&amp;nbsp;LSTM&amp;nbsp;(=&amp;nbsp;Long&amp;nbsp;Short&amp;nbsp;-&amp;nbsp;Term&amp;nbsp;Memory)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;RNN의&amp;nbsp;한&amp;nbsp;종류&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 이전 결과를 유지하면서 학습&lt;br /&gt;-&amp;nbsp;GRU&amp;nbsp;(=&amp;nbsp;Gated&amp;nbsp;Recurrent&amp;nbsp;Units)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;RNN의&amp;nbsp;한&amp;nbsp;종류&lt;br /&gt;-&amp;nbsp;CNN을&amp;nbsp;사용해서&amp;nbsp;분류할&amp;nbsp;수도&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;Bidirectional(LSTM())&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Sequences&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;Time&amp;nbsp;Series&amp;nbsp;data&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;정해진&amp;nbsp;시간&amp;nbsp;단위의&amp;nbsp;데이터&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Univarite,&amp;nbsp;Multivarite&amp;nbsp;해당&amp;nbsp;시간에&amp;nbsp;값이&amp;nbsp;1개,&amp;nbsp;여러&amp;nbsp;개&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이동&amp;nbsp;경로&amp;nbsp;같은&amp;nbsp;것도&amp;nbsp;해당(예:&amp;nbsp;자동차의&amp;nbsp;시간에&amp;nbsp;따른&amp;nbsp;위경도&amp;nbsp;위치)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;데이터&amp;nbsp;특징,&amp;nbsp;Trend,&amp;nbsp;Seasonaliy,&amp;nbsp;Autocorellation,&amp;nbsp;Noise&lt;br /&gt;-&amp;nbsp;시계열&amp;nbsp;데이터의&amp;nbsp;데이터&amp;nbsp;분리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;시즈널한&amp;nbsp;특징을&amp;nbsp;갖는&amp;nbsp;경우,&amp;nbsp;연도나&amp;nbsp;월&amp;nbsp;단위로&amp;nbsp;쪼개는&amp;nbsp;것이&amp;nbsp;유리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;과거&amp;nbsp;1을&amp;nbsp;학습&amp;nbsp;데이터,&amp;nbsp;과거&amp;nbsp;2를&amp;nbsp;검증&amp;nbsp;데이터,&amp;nbsp;가장&amp;nbsp;최근을&amp;nbsp;테스트&amp;nbsp;데이터로&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;1&amp;nbsp;+&amp;nbsp;2&amp;nbsp;를&amp;nbsp;학습데이터로,&amp;nbsp;최근을&amp;nbsp;검증데이터로&amp;nbsp;한&amp;nbsp;번&amp;nbsp;더&amp;nbsp;학습&amp;nbsp;가능&lt;br /&gt;- Fixed partitioning vs. Roll-forward partitioning&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;롤포워드는&amp;nbsp;기간을&amp;nbsp;잘게&amp;nbsp;쪼개서,&amp;nbsp;하루,&amp;nbsp;주간&amp;nbsp;단위로&amp;nbsp;테스트&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;짧은&amp;nbsp;기간부터&amp;nbsp;시작해&amp;nbsp;늘려가면서&amp;nbsp;테스트하는&amp;nbsp;방식&lt;br /&gt;-&amp;nbsp;에러&amp;nbsp;종류&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- mse = mean square error, 에러 총합이 마이너스가 되는 것 방지. 에러가 큰 경우 제곱으로 가중치가 적용됨.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;rmse&amp;nbsp;=&amp;nbsp;root&amp;nbsp;mse,&amp;nbsp;&amp;nbsp;루트를&amp;nbsp;적용해&amp;nbsp;스케일을&amp;nbsp;원래&amp;nbsp;오류와&amp;nbsp;동일하게&amp;nbsp;할&amp;nbsp;목적&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- mae = mean absolute error, 절대값 오류의 합을 평균. 큰 에러나 작은 에러의 편차가 중요하지 않은 경우&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;mape&amp;nbsp;=&amp;nbsp;mean&amp;nbsp;absolute&amp;nbsp;percentage&amp;nbsp;error,&amp;nbsp;오류의&amp;nbsp;퍼센트의&amp;nbsp;절대값으로&amp;nbsp;평균&lt;br /&gt;-&amp;nbsp;naive&amp;nbsp;forecast&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;단순히&amp;nbsp;이전&amp;nbsp;값으로&amp;nbsp;예측&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이&amp;nbsp;값을&amp;nbsp;기준값으로&amp;nbsp;설정할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;Moving&amp;nbsp;Average&amp;nbsp;(이동평균)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;단순하지만&amp;nbsp;노이즈를&amp;nbsp;줄일&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;방법&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;트렌드나&amp;nbsp;주기를&amp;nbsp;예측하진&amp;nbsp;않음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;naive&amp;nbsp;forecast&amp;nbsp;보다&amp;nbsp;정확도&amp;nbsp;낮음&lt;br /&gt;-&amp;nbsp;Moving&amp;nbsp;Average&amp;nbsp;of&amp;nbsp;Differencing&amp;nbsp;(기간&amp;nbsp;차의&amp;nbsp;이동평균을&amp;nbsp;활용)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예를&amp;nbsp;들어,&amp;nbsp;1년&amp;nbsp;전의&amp;nbsp;값의&amp;nbsp;차이를&amp;nbsp;계산하고,&amp;nbsp;Vdiff&amp;nbsp;=&amp;nbsp;(Vt&amp;nbsp;-&amp;nbsp;V(t-365))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Vdiff&amp;nbsp;의&amp;nbsp;이동평균을&amp;nbsp;계산&amp;nbsp;(Vt_diff_average)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이전&amp;nbsp;날짜에&amp;nbsp;Vdiff를&amp;nbsp;이동평균한&amp;nbsp;값을&amp;nbsp;더해&amp;nbsp;다음&amp;nbsp;값을&amp;nbsp;예측:&amp;nbsp;V(t-365)&amp;nbsp;+&amp;nbsp;Vt_diff_average&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Forecasts&amp;nbsp;=&amp;nbsp;moving&amp;nbsp;averaged&amp;nbsp;differenced&amp;nbsp;series&amp;nbsp;+&amp;nbsp;series(t&amp;nbsp;-&amp;nbsp;365)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이래도&amp;nbsp;노이즈는&amp;nbsp;남아있는데,&amp;nbsp;이럴&amp;nbsp;때&amp;nbsp;V(t-365)&amp;nbsp;대신&amp;nbsp;과거값을&amp;nbsp;이동평균한&amp;nbsp;값으로&amp;nbsp;사용하면&amp;nbsp;부드러워짐&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;즉,&amp;nbsp;forecasts&amp;nbsp;=&amp;nbsp;Vmoving_agerage(t-365)&amp;nbsp;+&amp;nbsp;Vt_diff_average&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Forecasts&amp;nbsp;=&amp;nbsp;trailing&amp;nbsp;moving&amp;nbsp;average&amp;nbsp;of&amp;nbsp;differenced&amp;nbsp;series&amp;nbsp;+&amp;nbsp;centered&amp;nbsp;moving&amp;nbsp;average&amp;nbsp;of&amp;nbsp;past&amp;nbsp;series(t-365)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 과거 값에 centered 이동 평균을 사용했고 (예: 시간 + 5와, 시간 - 5 사이의 평균) 이게 더 정확함. 현재값에 적용할 땐 trailing 이동 평균을 적용했는데, 이건 미래의 값을 알 수 없기 때문&lt;br /&gt;-&amp;nbsp;Deep&amp;nbsp;Learning&amp;nbsp;for&amp;nbsp;Time&amp;nbsp;Series&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;특정&amp;nbsp;윈도우&amp;nbsp;사이즈에&amp;nbsp;해당하는&amp;nbsp;시간&amp;nbsp;&amp;nbsp;x&amp;nbsp;의&amp;nbsp;y&amp;nbsp;값이&amp;nbsp;input,&amp;nbsp;다음&amp;nbsp;시간의&amp;nbsp;y&amp;nbsp;값이&amp;nbsp;label&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- tf.data.Dataset: range, flat_map, map, batch, shuffle...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Sequence bias: 시퀀스에서의 순서 편향이 있을 수 있다. 예를 들어, 첫 번째 나온 게 더 익숙하게 느껴질 수 있다. 훈련 데이터는 섞어서(shuffle) 쓰는 게 좋다.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- callbacks.LearningRateScheduler 를 콜백으로 해서, 적합한 러닝 레이트를 찾을 수 있다.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;근데&amp;nbsp;그냥&amp;nbsp;adam&amp;nbsp;으로&amp;nbsp;설정한&amp;nbsp;게&amp;nbsp;더&amp;nbsp;나은&amp;nbsp;것&amp;nbsp;같다?&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- numpy array를 새로 생성할 때: np_array[np.newaxis] 로 생성할 수 있음&lt;br /&gt;-&amp;nbsp;RNN&amp;nbsp;for&amp;nbsp;Time&amp;nbsp;Series&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;RNN에서&amp;nbsp;텐서플로우의&amp;nbsp;기본&amp;nbsp;유닛&amp;nbsp;개수는&amp;nbsp;3개&amp;nbsp;(해서,&amp;nbsp;배치사이즈&amp;nbsp;x&amp;nbsp;유닛&amp;nbsp;개수의&amp;nbsp;행렬이&amp;nbsp;셀의&amp;nbsp;아웃풋이&amp;nbsp;됨)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;셀의&amp;nbsp;중간&amp;nbsp;아웃풋을&amp;nbsp;생략하고&amp;nbsp;최종만&amp;nbsp;가져가는&amp;nbsp;걸&amp;nbsp;sequence&amp;nbsp;to&amp;nbsp;vector&amp;nbsp;RNN&amp;nbsp;이라고&amp;nbsp;하는데,&amp;nbsp;이게&amp;nbsp;기본&amp;nbsp;동작임&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;전체&amp;nbsp;시쿼스를&amp;nbsp;전달하려면&amp;nbsp;(sequence&amp;nbsp;to&amp;nbsp;sequence&amp;nbsp;RNN),&amp;nbsp;return_sequences&amp;nbsp;=&amp;nbsp;True&amp;nbsp;옵션을&amp;nbsp;쓰면&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;RNN의&amp;nbsp;input&amp;nbsp;은&amp;nbsp;3&amp;nbsp;dimension&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;batch_size:&amp;nbsp;정해지지&amp;nbsp;않은&amp;nbsp;경우&amp;nbsp;None&amp;nbsp;으로&amp;nbsp;설정하면&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;number_of_timestamp:&amp;nbsp;None&amp;nbsp;으로&amp;nbsp;설정하면&amp;nbsp;길이에&amp;nbsp;무관하게&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;변수&amp;nbsp;개수:&amp;nbsp;univariable&amp;nbsp;인&amp;nbsp;경우&amp;nbsp;1,&amp;nbsp;여러&amp;nbsp;개인&amp;nbsp;경우&amp;nbsp;숫자만큼&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;RNN의&amp;nbsp;기본&amp;nbsp;activation&amp;nbsp;function&amp;nbsp;은&amp;nbsp;tan&amp;nbsp;H&amp;nbsp;임&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;-1&amp;nbsp;~&amp;nbsp;1&amp;nbsp;값임&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예제에선&amp;nbsp;실제&amp;nbsp;학습&amp;nbsp;데이터가&amp;nbsp;0~100&amp;nbsp;사이라서,&amp;nbsp;람다&amp;nbsp;레이어에서&amp;nbsp;결과&amp;nbsp;값은&amp;nbsp;100배&amp;nbsp;해서&amp;nbsp;스케일을&amp;nbsp;비슷하게&amp;nbsp;맞췄음&lt;br /&gt;-&amp;nbsp;Lambda&amp;nbsp;Layer&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- keras.layers.Lambda: 전처리/후처리를 한 플로우 안에서 처리할 수 있음&lt;br /&gt;-&amp;nbsp;Humber&amp;nbsp;loss&amp;nbsp;function&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 아웃라이어에 덜 민감한 로스 함수&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;MAE&amp;nbsp;보다는&amp;nbsp;민감하고,&amp;nbsp;MSE&amp;nbsp;보다는&amp;nbsp;덜&amp;nbsp;민감한&amp;nbsp;듯&lt;br /&gt;-&amp;nbsp;SimpleRNN&amp;nbsp;vs&amp;nbsp;LSTM&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;SimpleRNN&amp;nbsp;은&amp;nbsp;상태값이&amp;nbsp;다음&amp;nbsp;셀에만&amp;nbsp;적용이&amp;nbsp;돼서&amp;nbsp;시간이&amp;nbsp;지날수록(다음&amp;nbsp;단계로&amp;nbsp;갈수록)&amp;nbsp;처음&amp;nbsp;값의&amp;nbsp;영향이&amp;nbsp;작아짐&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- LSTM 은 셀의 상태(Cell State)를 정해진 기간동안 유지할 수 있고, 양방향으로도 처리할 수 있음. 즉 처음 값의 영향이 시간이 지나도 유지됨&lt;br /&gt;-&amp;nbsp;실습&amp;nbsp;과제&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Conv1D&amp;nbsp;+&amp;nbsp;2&amp;nbsp;LSTM&amp;nbsp;+&amp;nbsp;3&amp;nbsp;Dense&amp;nbsp;layer&amp;nbsp;로&amp;nbsp;예측&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/AI</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/799</guid>
      <comments>https://ohgyun.com/799#entry799comment</comments>
      <pubDate>Tue, 9 Feb 2021 00:25:41 +0900</pubDate>
    </item>
    <item>
      <title>강연: 호갱노노 이렇게 만듭니다</title>
      <link>https://ohgyun.com/798</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2020.11.26&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; 호갱노노, 삼성전자&lt;br /&gt;&lt;br /&gt;&lt;b&gt;내용:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;삼성전자 R&amp;amp;D 센터에 초청받아 강연하고 왔다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;R&amp;amp;D 센터는 이번이 두 번째 방문이다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;한 때 나름 인기가 많았던ㅎㅎ 코드리뷰 경험을 주제로 강연을 부탁받아서 다녀왔었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;벌써 4년 전 일인데, 당시 환대해주셔서 특히 기억에 남는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;올해는 개발 문화를 주제로 하는 전사 사내 컨퍼런스라고 소개받았다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;최근에 시간적인 여유도 조금 생겼고, 마음 편하게 다녀왔던 생각에 큰 부담을 갖지 않고 수락했다.&lt;/p&gt;
&lt;p&gt;양재라서 가깝고 주차도 편했던 건 덤^^&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;막상 수락하고 보니 어떤 내용을 공유할까 고민이 많았다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;삼성전자와 호갱노노는 규모나 업종, 프로세스나 성향까지 크게 달라서, 개발 문화가 의미있을까 싶기도 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;고민 끝에, 호갱노노에서 개발자들이 직접 기획해서 개발하고, 운영까지 담당한 과정을 주제로 골랐다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;뜯어보면 특별할 것도 없고, 삼전에서는 직접 적용해보기도 어려운 내용이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;하지만, 이런 방식은 스타트업 같이 작은 규모에서만 가능할 것 같아서, 간접 경험을 제공한다는 생각으로 정리해봤다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 강연 슬라이드. (영상 녹화본은 외부 공개를 하지 않기로 했다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/2PYjxcuY5irFRf&quot; width=&quot;595&quot; height=&quot;485&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; allowfullscreen=&quot;&quot;&gt; &lt;/iframe&gt;&lt;/p&gt;
&lt;div style=&quot;margin-bottom: 5px;&quot;&gt;&lt;b&gt; &lt;a title=&quot;호갱노노 이렇게 만듭니다&quot; href=&quot;//www.slideshare.net/OhgyunAhn/ss-242423054&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;호갱노노 이렇게 만듭니다&lt;/a&gt; &lt;/b&gt; from &lt;b&gt;&lt;a href=&quot;https://www.slideshare.net/OhgyunAhn&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Ohgyun Ahn&lt;/a&gt;&lt;/b&gt;&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;후기:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;전사 컨퍼런스인데 코로나 때문에 온라인으로 진행한다고 해서, 당일엔 소수 인원으로 녹방으로 진행했다.&lt;/p&gt;
&lt;p&gt;작은 강의실에서 부담 없이 편한 마음으로 녹화했는데, 컨퍼런스 당일엔 1200명 정도 시청했다고 해서 놀랐다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;컨퍼런스 후에 온라인으로 피드백 받은 걸 보내주셔서 받았는데, 생각했던 것보다 좋게 봐주셔서 너무 좋았다.^^&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/생각_경험</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/798</guid>
      <comments>https://ohgyun.com/798#entry798comment</comments>
      <pubDate>Mon, 8 Feb 2021 23:50:09 +0900</pubDate>
    </item>
    <item>
      <title>클럽하우스와 음성 데이터</title>
      <link>https://ohgyun.com/797</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2020.02.03&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; 클럽하우스, clubhouse, voice data, voice recognition, 음성 인식&lt;br /&gt;&lt;br /&gt;&lt;b&gt;내용:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요새 클럽하우스가 인기인가보다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;음성 기반의 SNS 서비스이다.&lt;/p&gt;
&lt;p&gt;초대 받아 들어가서 잠깐 체험만 해보았는데, 음성 데이터의 가치가 엄청날 것 같아 보인다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;얼마 전에 1조 가치로 투자 받았다는데, 이런 특징들이 클럽하우스의 내재 가치를 올려주었던 게 아닐까 생각해봤다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;- 화자가 명확한 음성 데이터를 수집할 수 있다는 것&lt;br /&gt;- 다자간 음성 대화를 주제별로 분류할 수 있는 것&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;음성 인식 시장에 대해 잠깐 검색해봤는데, 생각보다 훨씬 규모가 크고 다양하게 활용되고 있는 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;음성 인식 시장의 가치&lt;br /&gt;-&amp;nbsp;음성&amp;nbsp;인식&amp;nbsp;시스템,&amp;nbsp;음성&amp;nbsp;지원&amp;nbsp;가상&amp;nbsp;비서,&amp;nbsp;소매,&amp;nbsp;은행,&amp;nbsp;커넥티드&amp;nbsp;장치,&amp;nbsp;스마트&amp;nbsp;홈,&amp;nbsp;의료&amp;nbsp;및&amp;nbsp;자동차&amp;nbsp;분야에&amp;nbsp;적용&amp;nbsp;가능&lt;br /&gt;-&amp;nbsp;2020년&amp;nbsp;108억&amp;nbsp;달러(12조),&amp;nbsp;2026년&amp;nbsp;27000억&amp;nbsp;달러(2970조&amp;nbsp;원)&amp;nbsp;예상&lt;br /&gt;-&amp;nbsp;특히,&amp;nbsp;BFSI&amp;nbsp;산업에서&amp;nbsp;수요&amp;nbsp;증가&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;BFSI:&amp;nbsp;Banking,&amp;nbsp;Financial&amp;nbsp;services,&amp;nbsp;Insurance&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;중국&amp;nbsp;생체&amp;nbsp;인식&amp;nbsp;기술&amp;nbsp;수요는&amp;nbsp;2021년까지&amp;nbsp;340억&amp;nbsp;위안(5조원)&amp;nbsp;예상&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;온라인&amp;nbsp;거래에서&amp;nbsp;음성으로&amp;nbsp;인식하는&amp;nbsp;쪽으로&amp;nbsp;사용하는&amp;nbsp;듯&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예를&amp;nbsp;들어,&amp;nbsp;고객이&amp;nbsp;전화&amp;nbsp;문의하는&amp;nbsp;동안&amp;nbsp;고객의&amp;nbsp;신원이&amp;nbsp;확인됨&lt;br /&gt;-&amp;nbsp;미국&amp;nbsp;분석회사&amp;nbsp;ComScore는&amp;nbsp;검색&amp;nbsp;트래픽의&amp;nbsp;50%가&amp;nbsp;음성으로&amp;nbsp;검색될&amp;nbsp;거라&amp;nbsp;예상&lt;br /&gt;-&amp;nbsp;구글에&amp;nbsp;따르면,&amp;nbsp;구글&amp;nbsp;모바일&amp;nbsp;앱&amp;nbsp;쿼리의&amp;nbsp;20%는&amp;nbsp;음석&amp;nbsp;검색임&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;클럽하우스와 음성 데이터의 머신러닝 활용에 대해서 검색해보니, AI 커뮤니티에서는 이런 식으로 활용될 거라고 예상하는 것 같다.&lt;br /&gt;- 개인의 신원 증명&lt;br /&gt;- 음성 대화로 감정 분석에 활용&lt;br /&gt;&amp;nbsp; &amp;nbsp; - (예: 대화를 듣고 이 사람이 슬픈지 기쁜지 알 수 있다는 것인 듯)&lt;br /&gt;- 다국어 다자간 대화의 음성 인식&lt;br /&gt;&amp;nbsp; &amp;nbsp; - (클럽하우스 이전에는 동일한 주제에 대해 다양한 언어로 대화하는 소스가 많지 않았을 것 같다)&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;- 클럽하우스: &lt;a href=&quot;https://www.joinclubhouse.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.joinclubhouse.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;- 클럽하우스 1조 가치로 투자 받음: &lt;a href=&quot;https://www.theinformation.com/articles/clubhouse-gets-investment-interest-at-1-billion-valuation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.theinformation.com/articles/clubhouse-gets-investment-interest-at-1-billion-valuation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;- 음성 인식 시장의 가치: &lt;a href=&quot;https://www.mordorintelligence.com/industry-reports/voice-recognition-market&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.mordorintelligence.com/industry-reports/voice-recognition-market&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;- 클럽하우스와 보이스 AI: &lt;a href=&quot;https://voicebot.ai/2021/01/31/clubhouse-social-audio-network-1b-valuation-3m-users-and-the-intersection-with-voice-ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;voicebot.ai/2021/01/31/clubhouse-social-audio-network-1b-valuation-3m-users-and-the-intersection-with-voice-ai/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Daylogs/개발뉴스</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/797</guid>
      <comments>https://ohgyun.com/797#entry797comment</comments>
      <pubDate>Thu, 4 Feb 2021 13:39:10 +0900</pubDate>
    </item>
    <item>
      <title>뉴스: GPT-Neo 를 개발하고 있다는 소식</title>
      <link>https://ohgyun.com/796</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2020.02.04&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; GTP-3, GTP-Neo, NLP&lt;br /&gt;&lt;br /&gt;&lt;b&gt;내용:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;화제가 됐던 NLP 모델인 GTP-3의 무료 복제본인 GPT-Neo 를 개발하고 있다는 소식&lt;br /&gt;&lt;br /&gt;GPT-Neo&lt;br /&gt;-&amp;nbsp;GTP-3&amp;nbsp;의&amp;nbsp;복제&amp;nbsp;버전을&amp;nbsp;무료&amp;nbsp;공개용으로&amp;nbsp;제작할&amp;nbsp;목적&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;GTP-3는&amp;nbsp;현재&amp;nbsp;마이크로소프트가&amp;nbsp;독점&amp;nbsp;라이선스를&amp;nbsp;가지고&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;올해&amp;nbsp;8월&amp;nbsp;초까지&amp;nbsp;완성할&amp;nbsp;계획으로&amp;nbsp;목표는&amp;nbsp;GTP-3와&amp;nbsp;동일한&amp;nbsp;수준으로&amp;nbsp;구현하는&amp;nbsp;것&lt;br /&gt;-&amp;nbsp;파라미터&amp;nbsp;1750억개의&amp;nbsp;버전이&amp;nbsp;목표이고,&amp;nbsp;현재&amp;nbsp;파라미터&amp;nbsp;1000억개까지는&amp;nbsp;완료함&lt;br /&gt;-&amp;nbsp;사회적&amp;nbsp;편견을&amp;nbsp;없애는데&amp;nbsp;주의를&amp;nbsp;기울이고&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;CoreWeave&amp;nbsp;라는&amp;nbsp;클라우드&amp;nbsp;서비스가&amp;nbsp;인프라를&amp;nbsp;무료로&amp;nbsp;제공해주기로&amp;nbsp;함&lt;br /&gt;-&amp;nbsp;학습&amp;nbsp;데이터는&amp;nbsp;825GB&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;텍스트&amp;nbsp;데이터셋,&amp;nbsp;IRC&amp;nbsp;챗로그,&amp;nbsp;유튜브&amp;nbsp;자막,&amp;nbsp;의학&amp;nbsp;연구&amp;nbsp;보고서&amp;nbsp;등&lt;br /&gt;-&amp;nbsp;성별,&amp;nbsp;종교,&amp;nbsp;인종&amp;nbsp;편견에&amp;nbsp;데이터를&amp;nbsp;평가하기&amp;nbsp;위해&amp;nbsp;감정&amp;nbsp;분석을&amp;nbsp;사용함&lt;br /&gt;-&amp;nbsp;너무&amp;nbsp;심한&amp;nbsp;수준의&amp;nbsp;편향&amp;nbsp;데이터는&amp;nbsp;학습&amp;nbsp;데이터에서&amp;nbsp;제거함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;파라미터는 뉴럴 네트워크 내 뉴런의 개수라고 이해하면 될 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여러 사례를 보면, NLP에서는 다양한 측면에서 감정 분석을 활용하고 있는 것 같다.&lt;/p&gt;
&lt;p&gt;문장의 의미 뿐 아니라 분위기를 이해하는 것이 모두에게 도전적이고 중요한 일인 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;얼마 전에 본 포스트에선 GTP-3를 학습하려고 쿠버네티스로 GPU 인스턴스를 동시에 7500대를 띄워서 작업했다는 내용도 있더라.&lt;br /&gt;위 소식의 파라미터 수도 그렇고, 인스턴스 수도 그렇고, 놀라울 따름이다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;물론, 기존 모델을 활용해서 적은 데이터로 의미있는 결과를 냈다는 뉴스도 많으니... ㅎㅎ&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;GTP-Neo: &lt;a href=&quot;https://www.eleuther.ai/projects/gpt-neo&quot;&gt;https://www.eleuther.ai/projects/gpt-neo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;GTP-3 쿠버네티스: &lt;a href=&quot;https://openai.com/blog/scaling-kubernetes-to-7500-nodes/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;openai.com/blog/scaling-kubernetes-to-7500-nodes/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Daylogs/개발뉴스</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/796</guid>
      <comments>https://ohgyun.com/796#entry796comment</comments>
      <pubDate>Thu, 4 Feb 2021 11:58:05 +0900</pubDate>
    </item>
    <item>
      <title>AWS: ElasticSearch 사이즈 최적화</title>
      <link>https://ohgyun.com/795</link>
      <description>&lt;p&gt;&lt;b&gt;발생일: &lt;/b&gt;2020.07.28&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&amp;nbsp;&lt;/b&gt;aws, elasticsearch size, 사이즈, node size, 노드 사이즈&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;AWS 엘라스틱서치에 몇 가지 검색을 추가해 넣으려고 한다.&lt;/p&gt;
&lt;p&gt;예전에 몇 번 다운됐던 적이 있어서, 미리 적합한 사이즈를 알아보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;어느 정도가 적합한 걸까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사이즈 찾는 가이드 문서가 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/sizing-domains.html&quot;&gt;https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/sizing-domains.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;적합한&amp;nbsp;스토리지&amp;nbsp;사이즈&lt;br /&gt;-&amp;nbsp;클러스터&amp;nbsp;&amp;gt;&amp;nbsp;노드&amp;nbsp;&amp;gt;&amp;nbsp;샤드&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;물리&amp;nbsp;서버에&amp;nbsp;여러&amp;nbsp;노드를&amp;nbsp;생성할&amp;nbsp;수&amp;nbsp;있지만,&amp;nbsp;AWS&amp;nbsp;ES에서는&amp;nbsp;한&amp;nbsp;인스턴스의&amp;nbsp;한&amp;nbsp;개의&amp;nbsp;노드만&amp;nbsp;있는&amp;nbsp;듯&lt;br /&gt;-&amp;nbsp;마스터&amp;nbsp;전용&amp;nbsp;노드(Dedicated&amp;nbsp;Master&amp;nbsp;Node)는&amp;nbsp;홀수로&amp;nbsp;3개&amp;nbsp;이상&amp;nbsp;권장&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Split Brain 이슈: &lt;a href=&quot;https://esbook.kimjmin.net/03-cluster/3.3-master-and-data-nodes&quot;&gt;https://esbook.kimjmin.net/03-cluster/3.3-master-and-data-nodes&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;각&amp;nbsp;노드의&amp;nbsp;복제본(리플리카)&amp;nbsp;개수는&amp;nbsp;1개&amp;nbsp;이상&amp;nbsp;권장&amp;nbsp;(기본값:&amp;nbsp;1)&lt;br /&gt;-&amp;nbsp;인덱스&amp;nbsp;사이즈는&amp;nbsp;보통&amp;nbsp;데이터&amp;nbsp;전체&amp;nbsp;사이즈보다&amp;nbsp;10%&amp;nbsp;정도&amp;nbsp;더&amp;nbsp;커짐&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;대상&amp;nbsp;데이터&amp;nbsp;100메가&amp;nbsp;-&amp;gt;&amp;nbsp;인덱스&amp;nbsp;포함&amp;nbsp;110메가,&amp;nbsp;여기에&amp;nbsp;리플리카&amp;nbsp;1개&amp;nbsp;하면&amp;nbsp;220메가&lt;br /&gt;-&amp;nbsp;리눅스&amp;nbsp;OS는&amp;nbsp;기본적으로&amp;nbsp;5%&amp;nbsp;정도의&amp;nbsp;공간을&amp;nbsp;주요&amp;nbsp;프로세스에&amp;nbsp;할당함&lt;br /&gt;-&amp;nbsp;Amazon&amp;nbsp;ES&amp;nbsp;시스템은&amp;nbsp;인스턴스&amp;nbsp;별로&amp;nbsp;20%&amp;nbsp;정도의&amp;nbsp;공간(최대&amp;nbsp;20GB)를&amp;nbsp;예약&amp;nbsp;점유함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;인스턴스&amp;nbsp;당&amp;nbsp;최대&amp;nbsp;20기가이기&amp;nbsp;때문에,&amp;nbsp;노드&amp;nbsp;개수에&amp;nbsp;따라&amp;nbsp;달라짐&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;노드가&amp;nbsp;10개면&amp;nbsp;200기가가&amp;nbsp;예약&amp;nbsp;공간으로&amp;nbsp;잡힘&lt;br /&gt;- 간단 계산 방식: 원본 데이터 크기 * (1 + 리플리카 개수) * 1.45 = 최소 필요 공간&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;자세히:&amp;nbsp;원본&amp;nbsp;데이터&amp;nbsp;크기&amp;nbsp;*&amp;nbsp;(1&amp;nbsp;+&amp;nbsp;리플리카)&amp;nbsp;*&amp;nbsp;(1&amp;nbsp;+&amp;nbsp;인덱스&amp;nbsp;크기)&amp;nbsp;/&amp;nbsp;(&amp;nbsp;1&amp;nbsp;-&amp;nbsp;OS&amp;nbsp;점유&amp;nbsp;비율)&amp;nbsp;/&amp;nbsp;(&amp;nbsp;1&amp;nbsp;-&amp;nbsp;AWS&amp;nbsp;ES&amp;nbsp;비중)&amp;nbsp;=&amp;nbsp;최소&amp;nbsp;필요&amp;nbsp;공간&lt;br /&gt;-&amp;nbsp;예:&amp;nbsp;원본이&amp;nbsp;100기가,&amp;nbsp;리플리카&amp;nbsp;1&amp;nbsp;이면,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 100GB * (1 + 1) * 1.45 = 290GB가 필요&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 현재: 1.3기가 = 1.3 * 2 * 1.45 = 3.7기가 (현재 스토리지 = 10기가)&lt;br /&gt;&lt;br /&gt;노드&amp;nbsp;모니터링&lt;br /&gt;-&amp;nbsp;GET&amp;nbsp;_cat/indices?v&amp;nbsp;로&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;health&amp;nbsp;-&amp;nbsp;인덱스&amp;nbsp;헬스&amp;nbsp;상태&amp;nbsp;(green,&amp;nbsp;yellow,&amp;nbsp;red)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;status&amp;nbsp;-&amp;nbsp;인덱스&amp;nbsp;open&amp;nbsp;상태&amp;nbsp;(open,&amp;nbsp;close)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;pri&amp;nbsp;-&amp;nbsp;프라이머리&amp;nbsp;샤드&amp;nbsp;갯수&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;rep&amp;nbsp;-&amp;nbsp;리플리카&amp;nbsp;샤드&amp;nbsp;갯수&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;a href=&quot;http://docs.count&quot;&gt;docs.count&lt;/a&gt; - 도큐멘트 갯수&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- docs.deleted - 삭제된 도큐멘트 갯수&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;a href=&quot;http://store.size&quot;&gt;store.size&lt;/a&gt; - 리플리카를 포함한 실제 저장된 사이즈&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;a href=&quot;http://pri.store.size&quot;&gt;pri.store.size&lt;/a&gt; - 프라이머리 실제 저장된 사이즈&lt;br /&gt;-&amp;nbsp;GET&amp;nbsp;/_cat/allocation?v&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;shards&amp;nbsp;-&amp;nbsp;샤드&amp;nbsp;갯수&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- disk.indices - 인덱스가 사용하고 있는 디스크 용량&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;a href=&quot;http://disk.used&quot;&gt;disk.used&lt;/a&gt; - 실제 시스템에서 사용된 디스크 용량&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;a href=&quot;http://disk.avail&quot;&gt;disk.avail&lt;/a&gt; - 실제 시스템에서 사용 가능한 디스크 용량&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;a href=&quot;http://disk.total&quot;&gt;disk.total&lt;/a&gt; - 실제 시스템에서 전체 디스크 용량&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- disk.percent - 실제 시스템의 디스크 용량 사용률&lt;br /&gt;&lt;br /&gt;샤드&amp;nbsp;개수&amp;nbsp;정하기&lt;br /&gt;-&amp;nbsp;프라이머리&amp;nbsp;샤드는&amp;nbsp;인덱스&amp;nbsp;생성&amp;nbsp;후&amp;nbsp;변경하기&amp;nbsp;어렵움&lt;br /&gt;-&amp;nbsp;샤드&amp;nbsp;개수를&amp;nbsp;설정하는&amp;nbsp;목적은&amp;nbsp;클러스터의&amp;nbsp;모든&amp;nbsp;노드에&amp;nbsp;인덱스를&amp;nbsp;고르게&amp;nbsp;분산시키는&amp;nbsp;것&lt;br /&gt;-&amp;nbsp;샤드&amp;nbsp;개수가&amp;nbsp;너무&amp;nbsp;많거나,&amp;nbsp;한&amp;nbsp;개의&amp;nbsp;샤드&amp;nbsp;사이즈가&amp;nbsp;너무&amp;nbsp;크면&amp;nbsp;안됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;용량이&amp;nbsp;너무&amp;nbsp;크면&amp;nbsp;실패&amp;nbsp;복구에&amp;nbsp;비용이&amp;nbsp;많이&amp;nbsp;듬&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;샤드&amp;nbsp;운영에&amp;nbsp;CPU와&amp;nbsp;메모리가&amp;nbsp;소모되므로,&amp;nbsp;작은&amp;nbsp;샤드가&amp;nbsp;너무&amp;nbsp;많은&amp;nbsp;경우라면&amp;nbsp;비효율적임&lt;br /&gt;-&amp;nbsp;대략&amp;nbsp;10~50기가&amp;nbsp;정도의&amp;nbsp;크기인&amp;nbsp;것이&amp;nbsp;좋음&lt;br /&gt;-&amp;nbsp;간단&amp;nbsp;계산법:&amp;nbsp;인덱스&amp;nbsp;크기&amp;nbsp;/&amp;nbsp;원하는&amp;nbsp;샤드&amp;nbsp;사이즈&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;데이터&amp;nbsp;사이즈가&amp;nbsp;60기가고,&amp;nbsp;샤드&amp;nbsp;하나의&amp;nbsp;크기를&amp;nbsp;30기가로&amp;nbsp;생각한다면,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- (60 * 1.1) / 30 = 2개 (1.1을 곱하는 건 인덱스 크기가 10% 정도 크기 때문)&lt;br /&gt;-&amp;nbsp;내년에&amp;nbsp;용량이&amp;nbsp;4배로&amp;nbsp;증가할&amp;nbsp;것&amp;nbsp;같다고&amp;nbsp;미리&amp;nbsp;샤드를&amp;nbsp;여러&amp;nbsp;개&amp;nbsp;만들어두는&amp;nbsp;것보다는,&amp;nbsp;적당한&amp;nbsp;개수로&amp;nbsp;설정해서&amp;nbsp;사용하다가&amp;nbsp;인덱스를&amp;nbsp;새로&amp;nbsp;생성하는&amp;nbsp;것이&amp;nbsp;효율적임&lt;br /&gt;-&amp;nbsp;엘라스틱서치&amp;nbsp;7버전은&amp;nbsp;노드&amp;nbsp;당&amp;nbsp;샤드&amp;nbsp;개수를&amp;nbsp;1000개로&amp;nbsp;제한하고&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 수정이 필요한 경우, cluster.max_shards_per_node 설정을 변경하면 됨&lt;br /&gt;-&amp;nbsp;또한,&amp;nbsp;1개의&amp;nbsp;노드에서&amp;nbsp;자바&amp;nbsp;힙&amp;nbsp;사이즈&amp;nbsp;기준으로&amp;nbsp;기가&amp;nbsp;당&amp;nbsp;샤드의&amp;nbsp;개수는&amp;nbsp;20개&amp;nbsp;이하여야&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 예를 들어, &lt;a href=&quot;http://m5.large.elasticsearch&quot;&gt;m5.large.elasticsearch&lt;/a&gt; 인스턴스는 4기가 힙을 가지고 있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이&amp;nbsp;노드의&amp;nbsp;샤드의&amp;nbsp;개수는&amp;nbsp;4&amp;nbsp;*&amp;nbsp;20&amp;nbsp;=&amp;nbsp;80개&amp;nbsp;이하여야&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이&amp;nbsp;계산에&amp;nbsp;따르면&amp;nbsp;샤드&amp;nbsp;사이즈가&amp;nbsp;5기가가&amp;nbsp;되는&amp;nbsp;거라&amp;nbsp;권장&amp;nbsp;사이즈보다&amp;nbsp;작게&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;보통&amp;nbsp;샤드의&amp;nbsp;권장&amp;nbsp;사이즈(10~50기가)를&amp;nbsp;따르면&amp;nbsp;이&amp;nbsp;문제는&amp;nbsp;발생하지&amp;nbsp;않을&amp;nbsp;것임&lt;br /&gt;&lt;br /&gt;인스턴스&amp;nbsp;타입&amp;nbsp;정하기&lt;br /&gt;- &lt;a href=&quot;https://aws.amazon.com/ko/elasticsearch-service/pricing/&quot;&gt;https://aws.amazon.com/ko/elasticsearch-service/pricing/&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;샤드가&amp;nbsp;많거나,&amp;nbsp;집계를&amp;nbsp;많이&amp;nbsp;하거나,&amp;nbsp;도큐먼트를&amp;nbsp;자주&amp;nbsp;업데이트하거나,&amp;nbsp;쿼리가&amp;nbsp;많거나&amp;nbsp;하는&amp;nbsp;경우엔&amp;nbsp;더&amp;nbsp;좋은&amp;nbsp;인스턴스가&amp;nbsp;필요함&lt;br /&gt;-&amp;nbsp;스토리지&amp;nbsp;100기가&amp;nbsp;당,&amp;nbsp;적어도&amp;nbsp;2&amp;nbsp;vCPU&amp;nbsp;+&amp;nbsp;8GiB&amp;nbsp;메모리로&amp;nbsp;시작하는&amp;nbsp;것을&amp;nbsp;권장함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;데이터가&amp;nbsp;200기가면&amp;nbsp;4&amp;nbsp;vCPU&amp;nbsp;+&amp;nbsp;16GiB&lt;br /&gt;-&amp;nbsp;2개의&amp;nbsp;데이터&amp;nbsp;노드와&amp;nbsp;3개의&amp;nbsp;마스터&amp;nbsp;노드로&amp;nbsp;시작하는&amp;nbsp;것을&amp;nbsp;권장함&amp;nbsp;(Split&amp;nbsp;brain&amp;nbsp;이슈&amp;nbsp;등을&amp;nbsp;피하기&amp;nbsp;위함)&lt;br /&gt;-&amp;nbsp;예:&amp;nbsp;전체&amp;nbsp;데이터가&amp;nbsp;184기가이고&amp;nbsp;노드가&amp;nbsp;3개라면,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;노드&amp;nbsp;당&amp;nbsp;184&amp;nbsp;/&amp;nbsp;3&amp;nbsp;=&amp;nbsp;61기가가&amp;nbsp;필요함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 이 정도면, &lt;a href=&quot;http://m5.large.elasticsearch&quot;&gt;m5.large.elasticsearch&lt;/a&gt; (2 vCPU, 8GiB)인스턴스가 적당함 (90GiB ESB 스토리지를 쓰는데, 늘어나는 것 고려하면 충분)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;그럼&amp;nbsp;총&amp;nbsp;6&amp;nbsp;vCPU,&amp;nbsp;24GiB를&amp;nbsp;쓰는&amp;nbsp;셈&lt;br /&gt;-&amp;nbsp;실제&amp;nbsp;데이터를&amp;nbsp;넣고&amp;nbsp;테스트했을&amp;nbsp;때,&amp;nbsp;CPUUtilization&amp;nbsp;이나&amp;nbsp;JVMMemoryPressure&amp;nbsp;수치가&amp;nbsp;높다면&amp;nbsp;더&amp;nbsp;좋은&amp;nbsp;인스턴스를&amp;nbsp;사용해야&amp;nbsp;함&lt;br /&gt;-&amp;nbsp;시스템이&amp;nbsp;다운되는&amp;nbsp;것보다는&amp;nbsp;오버파워되는&amp;nbsp;게&amp;nbsp;나으니까,&amp;nbsp;좋은&amp;nbsp;걸로&amp;nbsp;시작했다가&amp;nbsp;점차&amp;nbsp;줄이는&amp;nbsp;것을&amp;nbsp;추천함&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;br /&gt;- &lt;a href=&quot;https://github.com/itmare/es&quot;&gt;https://github.com/itmare/es&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/AWS</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/795</guid>
      <comments>https://ohgyun.com/795#entry795comment</comments>
      <pubDate>Wed, 3 Feb 2021 23:56:13 +0900</pubDate>
    </item>
    <item>
      <title>엘라스틱서치 시작하기 노트</title>
      <link>https://ohgyun.com/794</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2020.07.25&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; 엘라스틱서치, elastic search&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;엘라스틱서치로 검색을 구현하려고 한다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.elastic.co/kr/webinars/getting-started-elasticsearch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;시작하기 문서&lt;/a&gt;를 찾아서 봤는데, 너무 이해하기 쉽게 잘 설명해준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 노트해둔 것.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;엘라스틱&amp;nbsp;서비스&lt;br /&gt;-&amp;nbsp;Kibana&amp;nbsp;시각화&amp;nbsp;도구&lt;br /&gt;-&amp;nbsp;Elastic&amp;nbsp;Search&amp;nbsp;검색&amp;nbsp;도구&lt;br /&gt;-&amp;nbsp;Beats,&amp;nbsp;Logstash&amp;nbsp;수집&amp;nbsp;도구&lt;br /&gt;-&amp;nbsp;이거&amp;nbsp;합해서&amp;nbsp;엘라스틱&amp;nbsp;스택&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;기타&amp;nbsp;기능&lt;br /&gt;- 엘라스틱 사이트 서치: 사이트 URL 넣으면 색인 모두 만들어줌. 자동완성과 검색 제공하는 자바스크립트 모듈만 넣으면 된다고 함&lt;br /&gt;- 엘라스틱서치 사이트에서 support matrix 라고 검색하면 지원환경 알 수 있음&lt;br /&gt;- 엘라스틱서치 7버전부터 JDK를 같이 포함하고 있어서 자바 설치 안해도 됨&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;디렉토리&amp;nbsp;설명&lt;br /&gt;- 다운로드: &lt;a href=&quot;https://www.elastic.co/kr/downloads/elasticsearch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.elastic.co/kr/downloads/elasticsearch&lt;/a&gt; (MacOS 용 다운로드 받음)&lt;br /&gt;- config 내 &lt;a href=&quot;http://elasticsearch.yml&quot;&gt;elasticsearch.yml&lt;/a&gt; (야믈 파일)이 가장 중요한 설정&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;elasticsearch.yml&lt;br /&gt;- &lt;a href=&quot;http://network.host:&quot;&gt;network.host:&lt;/a&gt; 파일에 호스트를 설정해야 프로덕션으로 가능함&lt;br /&gt;- &lt;a href=&quot;http://http.port:&quot;&gt;http.port:&lt;/a&gt; 기본적으로 REST API로 통신함. 기본 포트는 9200&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;용어&lt;br /&gt;-&amp;nbsp;도큐먼트:&amp;nbsp;단일&amp;nbsp;데이터&amp;nbsp;&lt;br /&gt;- 인덱스: 도큐먼트 집합. 저장 단위인 인덱스는 indicies 라고 표현하기도 함&lt;br /&gt;-&amp;nbsp;색인:&amp;nbsp;저장&amp;nbsp;행위&lt;br /&gt;-&amp;nbsp;샤드:&amp;nbsp;인덱스는&amp;nbsp;기본적으로&amp;nbsp;샤드(shard)라는&amp;nbsp;단위로&amp;nbsp;분리되고,&amp;nbsp;각&amp;nbsp;노드에&amp;nbsp;분산되어&amp;nbsp;저장됨&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;실행&amp;nbsp;및&amp;nbsp;테스트&lt;br /&gt;- 실행할 땐 ` bin/elasticsearch` 으로 하면 됨. 노드 이름으로 실행됨&lt;br /&gt;-&amp;nbsp;9300번&amp;nbsp;포트로&amp;nbsp;다른&amp;nbsp;서비스와&amp;nbsp;통신,&amp;nbsp;9200번&amp;nbsp;포트로&amp;nbsp;클라이언트와&amp;nbsp;통신&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;$&amp;nbsp;curl&amp;nbsp;localhost:9200&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;데모&lt;br /&gt;- &lt;a href=&quot;https://github.com/kimjmin/elastic-demo/tree/master/demos/get-started&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kimjmin/elastic-demo/tree/master/demos/get-started&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;색인&amp;nbsp;생성&amp;nbsp;데모&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;a href=&quot;https://github.com/kimjmin/elastic-demo/blob/master/demos/get-started/elasticsearch-7x.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kimjmin/elastic-demo/blob/master/demos/get-started/elasticsearch-7x.md&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;PUT/GET&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;구조는&amp;nbsp;localhost:9200/[인덱스명]/_doc/[도규먼트ID]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 7.0부터는 타입 개념이 없어져서 _doc 으로 붙으면 됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;PUT으로&amp;nbsp;REST&amp;nbsp;API로&amp;nbsp;넣을&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;넣고&amp;nbsp;나서는&amp;nbsp;같은&amp;nbsp;이름으로&amp;nbsp;GET으로&amp;nbsp;가져올&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 엘라스틱서치는 한 URL이 한 도큐먼트를 가리키고 있음 (같은 URL로 넣으면 덮어써짐)&lt;br /&gt;-&amp;nbsp;POST&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;POST는&amp;nbsp;도큐먼트&amp;nbsp;ID를&amp;nbsp;자동으로&amp;nbsp;생성함&lt;br /&gt;-&amp;nbsp;DELETE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;DELETE&amp;nbsp;로&amp;nbsp;삭제하면&amp;nbsp;되고,&amp;nbsp;도큐먼트&amp;nbsp;또는&amp;nbsp;인덱스&amp;nbsp;단위로&amp;nbsp;모두&amp;nbsp;삭제할&amp;nbsp;수&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;키바나&amp;nbsp;데모&lt;br /&gt;- config/kinana.yml 이 주요 설정 파일&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 로컬 호스트가 아니라 엘라스틱서치와 다른 서버로 실행하고 싶다면, elastic_server 설정 변경하면 됨&lt;br /&gt;- bin/kinana 로 실행. 5601번 포트가 기본 포트&lt;br /&gt;-&amp;nbsp;메뉴&amp;nbsp;&amp;gt;&amp;nbsp;Dev&amp;nbsp;Tools&amp;nbsp;에&amp;nbsp;가면&amp;nbsp;여러&amp;nbsp;가지&amp;nbsp;테스트해볼&amp;nbsp;수&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;Bulk&amp;nbsp;API&lt;br /&gt;-&amp;nbsp;대용량인&amp;nbsp;경우,&amp;nbsp;기본가&amp;nbsp;bulk는&amp;nbsp;10배&amp;nbsp;이상&amp;nbsp;차이남&lt;br /&gt;-&amp;nbsp;POST&amp;nbsp;/_bulk&lt;br /&gt;-&amp;nbsp;index,&amp;nbsp;create,&amp;nbsp;update,&amp;nbsp;delete&amp;nbsp;동작&amp;nbsp;가능&lt;br /&gt;-&amp;nbsp;delete를&amp;nbsp;제외하고,&amp;nbsp;명령문과&amp;nbsp;데이터문을&amp;nbsp;한&amp;nbsp;줄씩&amp;nbsp;순서대로&amp;nbsp;입력&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;또한,&amp;nbsp;명령문과&amp;nbsp;데이터문은&amp;nbsp;반드시&amp;nbsp;한&amp;nbsp;줄&amp;nbsp;안에&amp;nbsp;입력되어&amp;nbsp;있어야&amp;nbsp;함&amp;nbsp;(줄바꿈을&amp;nbsp;허용하지&amp;nbsp;않음)&lt;br /&gt;-&amp;nbsp;모든&amp;nbsp;명령이&amp;nbsp;동일&amp;nbsp;인덱스에서&amp;nbsp;수행되는&amp;nbsp;경우엔,&amp;nbsp;인덱스명/_bulk&amp;nbsp;API&amp;nbsp;로&amp;nbsp;호출할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;- 엘라스틱서치는 커밋이나 롤백 등 트랜잭션 개념이 없음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;_bulk&amp;nbsp;작업&amp;nbsp;중&amp;nbsp;연결이&amp;nbsp;끊기거나&amp;nbsp;시스템&amp;nbsp;다운&amp;nbsp;등으로&amp;nbsp;동작이&amp;nbsp;중단된&amp;nbsp;경우,&amp;nbsp;어디서&amp;nbsp;중단됐는지&amp;nbsp;확인&amp;nbsp;불가함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이럴&amp;nbsp;땐&amp;nbsp;인덱스를&amp;nbsp;삭제하고&amp;nbsp;처음부터&amp;nbsp;다시&amp;nbsp;하는&amp;nbsp;것이&amp;nbsp;안전함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;Search&amp;nbsp;API&lt;br /&gt;-&amp;nbsp;GET&amp;nbsp;인덱스/_search&amp;nbsp;로&amp;nbsp;요청&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- q 파라미터로 검색어를 전달할 수 있음. /_search?q=value&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;q=&quot;a&amp;nbsp;and&amp;nbsp;b&quot;&amp;nbsp;나,&amp;nbsp;q=field:value&amp;nbsp;같은&amp;nbsp;패턴으로&amp;nbsp;검색&amp;nbsp;가능&lt;br /&gt;-&amp;nbsp;데이터&amp;nbsp;본문으로&amp;nbsp;검색:&amp;nbsp;query&amp;nbsp;옵션으로&amp;nbsp;검색&amp;nbsp;조건을&amp;nbsp;씀&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;match:&amp;nbsp;필드&amp;nbsp;+&amp;nbsp;검색어로&amp;nbsp;설정함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 검색어는 term 이라고 함. 공백으로 구분하면 기본적으로 OR 조건. 예) dog fox&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AND를&amp;nbsp;하려면&amp;nbsp;&quot;operator&quot;:&amp;nbsp;&quot;and&quot;&amp;nbsp;조건을&amp;nbsp;주면&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;match_all:&amp;nbsp;인덱스의&amp;nbsp;모든&amp;nbsp;것을&amp;nbsp;가져옴&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;match_phrase:&amp;nbsp;검색어를&amp;nbsp;하나의&amp;nbsp;구문으로&amp;nbsp;검색함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;slop:&amp;nbsp;1&amp;nbsp;옵션을&amp;nbsp;주면,&amp;nbsp;'스키&amp;nbsp;장갑'&amp;nbsp;같은&amp;nbsp;검색&amp;nbsp;시,&amp;nbsp;'스키&amp;nbsp;벙어리&amp;nbsp;장갑',&amp;nbsp;'스키&amp;nbsp;보드&amp;nbsp;장갑'도&amp;nbsp;검색&amp;nbsp;가능&lt;br /&gt;- 엘라스틱서치의 Query 구문은 모두 JSON 형식으로 입력함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;베이직&amp;nbsp;이상&amp;nbsp;라이렌스에서는&amp;nbsp;표준&amp;nbsp;SQL문을&amp;nbsp;사용해&amp;nbsp;검색&amp;nbsp;가능하지만,&amp;nbsp;기능에&amp;nbsp;제한이&amp;nbsp;있으므로&amp;nbsp;JSON&amp;nbsp;형식을&amp;nbsp;쓸&amp;nbsp;것을&amp;nbsp;권장함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;멀티테넌시&amp;nbsp;(Multitenancy)&lt;br /&gt;-&amp;nbsp;엘라스틱서치는&amp;nbsp;여러&amp;nbsp;인덱스를&amp;nbsp;한꺼번에&amp;nbsp;묶어&amp;nbsp;검색할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;멀티테넌시를&amp;nbsp;지원&lt;br /&gt;-&amp;nbsp;logs-2020,&amp;nbsp;logs-2019&amp;nbsp;같은&amp;nbsp;인덱스가&amp;nbsp;있다면,&amp;nbsp;logs-*/_search&amp;nbsp;명령으로&amp;nbsp;한꺼번에&amp;nbsp;검색&amp;nbsp;가능&lt;br /&gt;-&amp;nbsp;인덱스는&amp;nbsp;쉼표(,)나&amp;nbsp;와일드카드(*)로&amp;nbsp;묶을&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;GET&amp;nbsp;logs-2020,logs-2019/_search&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;GET&amp;nbsp;logs-*/_search&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;복합&amp;nbsp;쿼리&lt;br /&gt;- 복합 쿼리. query에서 &quot;bool&quot; 옵션으로 서브쿼리를 조합할 수 있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;must:&amp;nbsp;쿼리가&amp;nbsp;참인&amp;nbsp;도큐먼트들을&amp;nbsp;검색&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;must_not:&amp;nbsp;쿼리가&amp;nbsp;거짓인&amp;nbsp;도큐먼트들을&amp;nbsp;검색&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;should:&amp;nbsp;검색&amp;nbsp;결과&amp;nbsp;중&amp;nbsp;이&amp;nbsp;쿼리에&amp;nbsp;해당하는&amp;nbsp;도큐먼트의&amp;nbsp;점수를&amp;nbsp;높임&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- filter: 쿼리가 참인 도큐먼트를 검색하지만 스코어를 계산하지 않음. must 보다 검색 속도가 빠르고 캐싱됨.&lt;br /&gt;-&amp;nbsp;각&amp;nbsp;항목에&amp;nbsp;서브쿼리를&amp;nbsp;배열로&amp;nbsp;전달할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;예를&amp;nbsp;들어,&amp;nbsp;특정&amp;nbsp;아파트의&amp;nbsp;리뷰만&amp;nbsp;검색하려고&amp;nbsp;한다면,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;must&amp;nbsp;에는&amp;nbsp;키워드를,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;filter&amp;nbsp;에&amp;nbsp;아파트&amp;nbsp;아이디를&amp;nbsp;넣는&amp;nbsp;방법&amp;nbsp;(스코어에&amp;nbsp;영향을&amp;nbsp;주지&amp;nbsp;않고&amp;nbsp;더&amp;nbsp;빠르니까)&lt;br /&gt;-&amp;nbsp;should&amp;nbsp;는&amp;nbsp;match_phrase&amp;nbsp;와&amp;nbsp;함께&amp;nbsp;쓰면&amp;nbsp;유용함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예를&amp;nbsp;들면,&amp;nbsp;검색&amp;nbsp;결과&amp;nbsp;중&amp;nbsp;입력한&amp;nbsp;검색어&amp;nbsp;전체&amp;nbsp;문장이&amp;nbsp;정확히&amp;nbsp;일치하는&amp;nbsp;결과를&amp;nbsp;맨&amp;nbsp;상위에&amp;nbsp;위치시킬&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;나머지&amp;nbsp;결과는&amp;nbsp;누락하지&amp;nbsp;않은&amp;nbsp;채&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;복합쿼리&amp;nbsp;안의&amp;nbsp;배열은&amp;nbsp;AND&amp;nbsp;조건임&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;must:&amp;nbsp;[&amp;nbsp;{&amp;nbsp;queryA&amp;nbsp;},&amp;nbsp;{&amp;nbsp;queryB&amp;nbsp;}&amp;nbsp;]&amp;nbsp;-&amp;gt;&amp;nbsp;queryA&amp;nbsp;AND&amp;nbsp;queryB&amp;nbsp;를&amp;nbsp;의미&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;OR는&amp;nbsp;검색어를&amp;nbsp;공백으로&amp;nbsp;구분해&amp;nbsp;적용할&amp;nbsp;수&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;Relevancy&amp;nbsp;(정확도)&lt;br /&gt;- &lt;a href=&quot;https://esbook.kimjmin.net/05-search/5.3-relevancy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://esbook.kimjmin.net/05-search/5.3-relevancy&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;검색&amp;nbsp;결과의&amp;nbsp;스코어는&amp;nbsp;BM25&amp;nbsp;알고리즘을&amp;nbsp;사용함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;BM25&amp;nbsp;계산식은&amp;nbsp;TF,&amp;nbsp;IDF,&amp;nbsp;Field&amp;nbsp;Length를&amp;nbsp;사용함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- TF: Term Frequency. 도큐먼트 내에 검색된 텀(Term)의 개수가 많을수록 점수가 높음. BM25에서는 최대 25까지 증가함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- IDF: Inverse Document Frequency. 검색한 텀을 포함하고 있는 전체 도큐먼트의 개수가 많을수록, 해당 텀의 점수가 감소함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Field Length: 텀을 포함하고 있는 필드의 길이가 짧을수록 점수가 높음.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;Exact&amp;nbsp;value&amp;nbsp;query&amp;nbsp;(정확값&amp;nbsp;쿼리)&lt;br /&gt;-&amp;nbsp;풀&amp;nbsp;텍스트는&amp;nbsp;정확도를&amp;nbsp;측정해서&amp;nbsp;스코어로&amp;nbsp;정렬함&lt;br /&gt;-&amp;nbsp;exact&amp;nbsp;value&amp;nbsp;쿼리는&amp;nbsp;참/거짓&amp;nbsp;여부만&amp;nbsp;판별해서&amp;nbsp;결과를&amp;nbsp;가져옴&lt;br /&gt;-&amp;nbsp;term,&amp;nbsp;range&amp;nbsp;같은&amp;nbsp;쿼리가&amp;nbsp;이&amp;nbsp;대상임&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;문자열&amp;nbsp;데이터는&amp;nbsp;keyword&amp;nbsp;형식으로&amp;nbsp;저장하면&amp;nbsp;정확값&amp;nbsp;검색이&amp;nbsp;가능함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 예: message 필드가 있다면, message.keyword 필드를 검색 대상으로 잡으면 됨&lt;br /&gt;-&amp;nbsp;스코어를&amp;nbsp;계산하지&amp;nbsp;않기&amp;nbsp;때문에&amp;nbsp;보통&amp;nbsp;bool&amp;nbsp;쿼리의&amp;nbsp;filter&amp;nbsp;내부에서&amp;nbsp;사용함&lt;br /&gt;-&amp;nbsp;filter&amp;nbsp;안에&amp;nbsp;넣은&amp;nbsp;검색&amp;nbsp;조건들은&amp;nbsp;스코어를&amp;nbsp;계산하지&amp;nbsp;않지만&amp;nbsp;캐싱이&amp;nbsp;되기&amp;nbsp;때문에&amp;nbsp;쿼리가&amp;nbsp;더&amp;nbsp;가볍고&amp;nbsp;빠르게&amp;nbsp;실행됨&lt;br /&gt;-&amp;nbsp;스코어&amp;nbsp;계산이&amp;nbsp;필요하지&amp;nbsp;않은&amp;nbsp;쿼리들은&amp;nbsp;모두&amp;nbsp;filter&amp;nbsp;안에&amp;nbsp;넣어서&amp;nbsp;실행하는&amp;nbsp;것이&amp;nbsp;좋음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;Range&amp;nbsp;쿼리&lt;br /&gt;-&amp;nbsp;range&amp;nbsp;쿼리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;gte&amp;nbsp;(Greater-than&amp;nbsp;or&amp;nbsp;equal&amp;nbsp;to):&amp;nbsp;이상&amp;nbsp;(같거나&amp;nbsp;큼)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;gt&amp;nbsp;(Greater-than):&amp;nbsp;초과&amp;nbsp;(큼)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;lte&amp;nbsp;(Less-than&amp;nbsp;or&amp;nbsp;equal&amp;nbsp;to):&amp;nbsp;이하&amp;nbsp;(같거나&amp;nbsp;작음)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;lt&amp;nbsp;(Less-than):&amp;nbsp;미만&amp;nbsp;(작음)&lt;br /&gt;-&amp;nbsp;format&amp;nbsp;속성을&amp;nbsp;정의할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;y,&amp;nbsp;M,&amp;nbsp;d,&amp;nbsp;H,&amp;nbsp;m,&amp;nbsp;s&amp;nbsp;로&amp;nbsp;입력&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;&quot;format&quot;:&amp;nbsp;&quot;yyyy-MM-dd&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;&quot;format&quot;:&amp;nbsp;&quot;yyyy-MM-dd||yyyy&quot;&amp;nbsp;(||&amp;nbsp;로&amp;nbsp;OR&amp;nbsp;패턴을&amp;nbsp;넣을&amp;nbsp;수&amp;nbsp;있음)&lt;br /&gt;-&amp;nbsp;날짜인&amp;nbsp;경우&amp;nbsp;키워드로&amp;nbsp;조회할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;&quot;gt&quot;:&amp;nbsp;&quot;now-2y&quot;:&amp;nbsp;오늘부터&amp;nbsp;2년&amp;nbsp;전&amp;nbsp;이후보다&amp;nbsp;큰&amp;nbsp;값&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;&quot;gt&quot;:&amp;nbsp;&quot;2020-01-01||+6M&quot;&amp;nbsp;-&amp;gt;&amp;nbsp;2020년&amp;nbsp;1월&amp;nbsp;1일에&amp;nbsp;6개월을&amp;nbsp;더한&amp;nbsp;값보다&amp;nbsp;큰&amp;nbsp;것&lt;br /&gt;-&amp;nbsp;range&amp;nbsp;또한&amp;nbsp;스코어가&amp;nbsp;없어서&amp;nbsp;filter&amp;nbsp;조건으로&amp;nbsp;넣는&amp;nbsp;것이&amp;nbsp;좋음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;range&amp;nbsp;에&amp;nbsp;따른&amp;nbsp;스코어를&amp;nbsp;주고&amp;nbsp;싶을&amp;nbsp;땐&amp;nbsp;function_score&amp;nbsp;쿼리를&amp;nbsp;사용해서&amp;nbsp;조정이&amp;nbsp;가능함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;텍스트&amp;nbsp;분석&lt;br /&gt;-&amp;nbsp;엘라스틱서치에&amp;nbsp;색인될&amp;nbsp;때&amp;nbsp;검색에&amp;nbsp;필요한&amp;nbsp;형태로&amp;nbsp;분리됨&lt;br /&gt;-&amp;nbsp;API&amp;nbsp;_analyze&amp;nbsp;로&amp;nbsp;애널라이저(analizer)&amp;nbsp;정보를&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;애널라이즈&amp;nbsp;만들기&amp;nbsp;전에&amp;nbsp;미리&amp;nbsp;분석해보면&amp;nbsp;좋을&amp;nbsp;듯&lt;br /&gt;-&amp;nbsp;애널라이저(analizer)는&amp;nbsp;토크나이저(tokenizer)와&amp;nbsp;여러&amp;nbsp;개의&amp;nbsp;필터(filter)로&amp;nbsp;구성됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Tokenizer&amp;nbsp;을&amp;nbsp;통해&amp;nbsp;문장을&amp;nbsp;검색어&amp;nbsp;텀(term)으로&amp;nbsp;쪼갬&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Filter(토큰필터)&amp;nbsp;를&amp;nbsp;통해&amp;nbsp;쪼개진&amp;nbsp;텀들을&amp;nbsp;가공&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;필터&amp;nbsp;예:&amp;nbsp;lowercase,&amp;nbsp;unqiue,&amp;nbsp;stop(불용어&amp;nbsp;제거)&lt;br /&gt;-&amp;nbsp;T:&amp;nbsp;standard,&amp;nbsp;F:&amp;nbsp;lowercase&amp;nbsp;-&amp;gt;&amp;nbsp;스탠다드&amp;nbsp;토크나이저&amp;nbsp;+&amp;nbsp;lowercase&amp;nbsp;필터&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;역인덱스&amp;nbsp;(Invereted&amp;nbsp;Index)&lt;br /&gt;- &lt;a href=&quot;https://esbook.kimjmin.net/06-text-analysis&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://esbook.kimjmin.net/06-text-analysis&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;데이터를&amp;nbsp;저장할&amp;nbsp;때&amp;nbsp;검색&amp;nbsp;가능한&amp;nbsp;term&amp;nbsp;(검색어&amp;nbsp;토큰)&amp;nbsp;으로&amp;nbsp;색인을&amp;nbsp;생성함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 예: doc1 = 'lazy dog' -&amp;gt; lazy: doc1, dog: dog1 과 같이&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;537&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chsuEO/btqVAIEg0Uf/onNaZLKKQ1kNcOX7CximXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chsuEO/btqVAIEg0Uf/onNaZLKKQ1kNcOX7CximXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chsuEO/btqVAIEg0Uf/onNaZLKKQ1kNcOX7CximXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchsuEO%2FbtqVAIEg0Uf%2FonNaZLKKQ1kNcOX7CximXK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;537&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;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;br /&gt;-&amp;nbsp;엘라스틱서치에&amp;nbsp;저장되는&amp;nbsp;도큐먼트의&amp;nbsp;모든&amp;nbsp;문자열(text)&amp;nbsp;필드의&amp;nbsp;역&amp;nbsp;인덱스를&amp;nbsp;생성함&lt;br /&gt;-&amp;nbsp;애널라이저(Analyzer)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;텍스트&amp;nbsp;분석&amp;nbsp;(Text&amp;nbsp;Analysis)&amp;nbsp;과정:&amp;nbsp;문자열&amp;nbsp;필드가&amp;nbsp;저장될&amp;nbsp;때,&amp;nbsp;데이터에서&amp;nbsp;검색어&amp;nbsp;토큰을&amp;nbsp;추출하는&amp;nbsp;여러&amp;nbsp;과정&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;텍스트&amp;nbsp;분석을&amp;nbsp;처리하는&amp;nbsp;기능&amp;nbsp;=&amp;nbsp;애널라이저&lt;br /&gt;-&amp;nbsp;애널라이저&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 0~3개의 캐릭터 필터, 1개의 토크나이저, 0~n개의 토큰 필터로 이루어짐&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;526&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oJXqz/btqVvOL22qy/FtkOay91z46JuISpEyl041/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oJXqz/btqVvOL22qy/FtkOay91z46JuISpEyl041/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oJXqz/btqVvOL22qy/FtkOay91z46JuISpEyl041/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoJXqz%2FbtqVvOL22qy%2FFtkOay91z46JuISpEyl041%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;526&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;캐릭터&amp;nbsp;필터(Character&amp;nbsp;Filter)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;전체&amp;nbsp;문장에서&amp;nbsp;특정&amp;nbsp;문자를&amp;nbsp;대치하거나&amp;nbsp;제거&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;특수문자&amp;nbsp;제거,&amp;nbsp;특정&amp;nbsp;키워드&amp;nbsp;치환&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 7.0 기준으로 strip, mapping, pattern replace 3개가 있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;char_filter&amp;nbsp;항목에&amp;nbsp;배열로&amp;nbsp;입력&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;토크나이저(Tokenizer)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;문장에&amp;nbsp;속한&amp;nbsp;단어들을&amp;nbsp;텀&amp;nbsp;단위로&amp;nbsp;하나씩&amp;nbsp;분리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;토크나이저는&amp;nbsp;반드시&amp;nbsp;1개만&amp;nbsp;적용이&amp;nbsp;가능함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;whitespace&amp;nbsp;토크나이저:&amp;nbsp;공백을&amp;nbsp;기준으로&amp;nbsp;텀&amp;nbsp;들을&amp;nbsp;분리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;토큰&amp;nbsp;필터(Token&amp;nbsp;Filter)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;분리된&amp;nbsp;텀을&amp;nbsp;가공&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;lowercase&amp;nbsp;토큰&amp;nbsp;필터:&amp;nbsp;Lazy,&amp;nbsp;lazy&amp;nbsp;를&amp;nbsp;같은&amp;nbsp;텀으로&amp;nbsp;병합&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;stop&amp;nbsp;토큰&amp;nbsp;필터:&amp;nbsp;검색&amp;nbsp;가치가&amp;nbsp;없는&amp;nbsp;단어를&amp;nbsp;불용어(stopword)로&amp;nbsp;간주해&amp;nbsp;토큰에서&amp;nbsp;제외&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;snowball&amp;nbsp;토큰&amp;nbsp;필터:&amp;nbsp;문법상&amp;nbsp;변형된&amp;nbsp;단어를&amp;nbsp;기본&amp;nbsp;형태로&amp;nbsp;변환,&amp;nbsp;jumps&amp;nbsp;-&amp;gt;&amp;nbsp;jump,&amp;nbsp;jumping&amp;nbsp;-&amp;gt;&amp;nbsp;jump&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 예: synonym 토큰 필터: 동의어를 텀으로 추가. quick -&amp;gt; fast 를, aws -&amp;gt; amazon 을 추가&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;애널라이저를&amp;nbsp;정의해서&amp;nbsp;바로&amp;nbsp;할당할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;검색&amp;nbsp;시&amp;nbsp;검색어도&amp;nbsp;애널라이저를&amp;nbsp;거쳐&amp;nbsp;term&amp;nbsp;으로&amp;nbsp;변형돼,&amp;nbsp;색인된&amp;nbsp;값을&amp;nbsp;검색함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;한국어&amp;nbsp;형태소&amp;nbsp;분석기를&amp;nbsp;쓴다면,&amp;nbsp;'수영장'&amp;nbsp;-&amp;gt;&amp;nbsp;'수영'&amp;nbsp;+&amp;nbsp;'장'으로&amp;nbsp;분리되어&amp;nbsp;검색되는&amp;nbsp;것임&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;검색할&amp;nbsp;때&amp;nbsp;애널라이저를&amp;nbsp;적용하지&amp;nbsp;않으려면&amp;nbsp;&quot;term&quot;&amp;nbsp;쿼리를&amp;nbsp;사용하면&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;&quot;query&quot;:&amp;nbsp;{&amp;nbsp;&quot;term&quot;:&amp;nbsp;{&amp;nbsp;&quot;message&quot;:&amp;nbsp;&quot;jump&quot;&amp;nbsp;}&amp;nbsp;}&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;사용자&amp;nbsp;정의&amp;nbsp;애널라이저&lt;br /&gt;- &lt;a href=&quot;https://esbook.kimjmin.net/06-text-analysis/6.3-analyzer-1/6.4-custom-analyzer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://esbook.kimjmin.net/06-text-analysis/6.3-analyzer-1/6.4-custom-analyzer&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;인덱스에&amp;nbsp;적용할&amp;nbsp;땐&amp;nbsp;사용자&amp;nbsp;정의&amp;nbsp;애널라이저를&amp;nbsp;만들어&amp;nbsp;적용하는&amp;nbsp;것이&amp;nbsp;편함&lt;br /&gt;-&amp;nbsp;PUT&amp;nbsp;인덱스명/_analyze&amp;nbsp;로&amp;nbsp;추가&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;settings&amp;nbsp;프로퍼티로&amp;nbsp;커스텀&amp;nbsp;애널라이저와&amp;nbsp;커스텀&amp;nbsp;필터를&amp;nbsp;먼저&amp;nbsp;설정하고,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이&amp;nbsp;값을&amp;nbsp;데이터에&amp;nbsp;적용할&amp;nbsp;땐,&amp;nbsp;mappings&amp;nbsp;프로퍼티로&amp;nbsp;대상&amp;nbsp;필드에&amp;nbsp;넣어주면&amp;nbsp;됨&lt;br /&gt;-&amp;nbsp;도큐먼트&amp;nbsp;토큰은&amp;nbsp;_termvectors&amp;nbsp;API로&amp;nbsp;확인&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;GET&amp;nbsp;인덱스/_termvectors/[id]?fields=foo,bar&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;한글&amp;nbsp;형태소&amp;nbsp;분석기&lt;br /&gt;-&amp;nbsp;별도&amp;nbsp;플러그인을&amp;nbsp;설치해야&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;bin/elasticsearch-plugin&amp;nbsp;install&amp;nbsp;analysis-nori&lt;br /&gt;-&amp;nbsp;T:&amp;nbsp;nori_tokenizer&amp;nbsp;로&amp;nbsp;분석해보면&amp;nbsp;다른&amp;nbsp;결과를&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;한국어&amp;nbsp;사전&amp;nbsp;기반으로&amp;nbsp;분석됨&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;사용자&amp;nbsp;정의&amp;nbsp;애널라이저&lt;br /&gt;-&amp;nbsp;인덱스는&amp;nbsp;도큐먼트의&amp;nbsp;모음&lt;br /&gt;-&amp;nbsp;settings&amp;nbsp;와&amp;nbsp;mapping&amp;nbsp;로&amp;nbsp;구분됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;settings:&amp;nbsp;애널라이저,&amp;nbsp;샤드개수,&amp;nbsp;리프레시&amp;nbsp;기준&amp;nbsp;등&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;mapping:&amp;nbsp;필드의&amp;nbsp;명세,&amp;nbsp;텍스트라면&amp;nbsp;어떤&amp;nbsp;애널라이저를&amp;nbsp;쓸&amp;nbsp;것인지&amp;nbsp;등&lt;br /&gt;-&amp;nbsp;한&amp;nbsp;번&amp;nbsp;만든&amp;nbsp;인덱스의&amp;nbsp;설정&amp;nbsp;대부분은&amp;nbsp;변경&amp;nbsp;불가능함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;리프레시&amp;nbsp;타임이나&amp;nbsp;리플리카&amp;nbsp;샤드&amp;nbsp;개수&amp;nbsp;등은&amp;nbsp;수정할&amp;nbsp;수&amp;nbsp;있으나,&amp;nbsp;대부분&amp;nbsp;변경&amp;nbsp;불가&lt;br /&gt;- 토큰 필터: &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenfilters.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenfilters.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;집계&lt;br /&gt;-&amp;nbsp;검색이&amp;nbsp;아니라&amp;nbsp;집계&lt;br /&gt;-&amp;nbsp;metrics&amp;nbsp;와&amp;nbsp;bucket이&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;metrics:&amp;nbsp;min,&amp;nbsp;max,&amp;nbsp;sum,&amp;nbsp;avg&amp;nbsp;등의&amp;nbsp;계산&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;bucket:&amp;nbsp;특정&amp;nbsp;기준으로&amp;nbsp;도큐먼트들을&amp;nbsp;그룹화&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;메트릭스&amp;nbsp;집계&lt;br /&gt;-&amp;nbsp;검색할&amp;nbsp;땐&amp;nbsp;query&amp;nbsp;대신&amp;nbsp;&quot;aggs&quot;&amp;nbsp;속성을&amp;nbsp;줌&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;&quot;size&quot;:&amp;nbsp;0&amp;nbsp;을&amp;nbsp;주지&amp;nbsp;않으면,&amp;nbsp;hits&amp;nbsp;결과가&amp;nbsp;함께&amp;nbsp;나오니&amp;nbsp;집계만&amp;nbsp;할&amp;nbsp;거면&amp;nbsp;적용하는&amp;nbsp;게&amp;nbsp;좋음&lt;br /&gt;-&amp;nbsp;집계를&amp;nbsp;query&amp;nbsp;와&amp;nbsp;같이&amp;nbsp;하면,&amp;nbsp;결과의&amp;nbsp;합계만&amp;nbsp;집계함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;버킷&amp;nbsp;집계&lt;br /&gt;-&amp;nbsp;날짜별&amp;nbsp;히스토그램&amp;nbsp;(date_histogram)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;date_histogram으로&amp;nbsp;date&amp;nbsp;필드를&amp;nbsp;1개월&amp;nbsp;간격으로&amp;nbsp;구분하는&amp;nbsp;bucket&amp;nbsp;aggregation&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;기본적으로&amp;nbsp;문서&amp;nbsp;개수를&amp;nbsp;리턴(doc_count)&lt;br /&gt;-&amp;nbsp;keyword&amp;nbsp;타입&amp;nbsp;=&amp;nbsp;분석하지&amp;nbsp;않고&amp;nbsp;통으로&amp;nbsp;저장함&lt;br /&gt;-&amp;nbsp;메트릭스&amp;nbsp;집계를&amp;nbsp;같이&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;위치&amp;nbsp;정보&lt;br /&gt;- geo_point : { &quot;lat&quot;: 41.12, &quot;lon&quot;: -71.34 } 같은 형식으로 입력&lt;br /&gt;-&amp;nbsp;geo_bounding_box&amp;nbsp;:&amp;nbsp;두&amp;nbsp;점을&amp;nbsp;기준으로&amp;nbsp;하는&amp;nbsp;네모&amp;nbsp;안에&amp;nbsp;있는&amp;nbsp;도큐먼트들을&amp;nbsp;가져옴&lt;br /&gt;-&amp;nbsp;geo_distance&amp;nbsp;:&amp;nbsp;한&amp;nbsp;점을&amp;nbsp;기준으로&amp;nbsp;반경&amp;nbsp;안에&amp;nbsp;있는&amp;nbsp;도큐먼트들을&amp;nbsp;가져옴&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;노드:&amp;nbsp;엘라스틱서치&amp;nbsp;인스턴스&lt;br /&gt;- &lt;a href=&quot;https://esbook.kimjmin.net/03-cluster/3.3-master-and-data-nodes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://esbook.kimjmin.net/03-cluster/3.3-master-and-data-nodes&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;노드에&amp;nbsp;여러&amp;nbsp;인덱스를&amp;nbsp;생성할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;한&amp;nbsp;노드는&amp;nbsp;9200번&amp;nbsp;포트로&amp;nbsp;클라이언트와&amp;nbsp;통신,&amp;nbsp;9300번&amp;nbsp;포트로&amp;nbsp;다른&amp;nbsp;노드와&amp;nbsp;통신&lt;br /&gt;-&amp;nbsp;여러&amp;nbsp;노드를&amp;nbsp;하나의&amp;nbsp;클러스터로&amp;nbsp;묶을&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;클러스터&amp;nbsp;&amp;gt;&amp;nbsp;노드&amp;nbsp;&amp;gt;&amp;nbsp;샤드&lt;br /&gt;-&amp;nbsp;노드&amp;nbsp;하나는&amp;nbsp;동시에&amp;nbsp;여러&amp;nbsp;역할을&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;master:&amp;nbsp;마스터&amp;nbsp;후보&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;data:&amp;nbsp;데이터&amp;nbsp;저장&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;ingest:&amp;nbsp;데이터&amp;nbsp;색인&amp;nbsp;전처리&amp;nbsp;작업&amp;nbsp;수행&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;ml:&amp;nbsp;머신러닝&amp;nbsp;작업&amp;nbsp;수행&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;아무&amp;nbsp;것도&amp;nbsp;설정&amp;nbsp;안하면&amp;nbsp;클라이언트와&amp;nbsp;통신만&amp;nbsp;하는&amp;nbsp;역할&lt;br /&gt;-&amp;nbsp;마스터&amp;nbsp;노드와&amp;nbsp;데이터&amp;nbsp;노드를&amp;nbsp;분리하면,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;마스터&amp;nbsp;노드는&amp;nbsp;클러스터&amp;nbsp;관리만&amp;nbsp;하면&amp;nbsp;되고,&amp;nbsp;데이터노드는&amp;nbsp;데이터&amp;nbsp;처리에&amp;nbsp;집중할&amp;nbsp;수&amp;nbsp;있어&amp;nbsp;효율적임&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;마스터&amp;nbsp;전용&amp;nbsp;노드를&amp;nbsp;Dedicated&amp;nbsp;master&amp;nbsp;node&amp;nbsp;라고&amp;nbsp;함&lt;br /&gt;-&amp;nbsp;Split&amp;nbsp;Brain&amp;nbsp;이슈&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;마스터&amp;nbsp;노드가&amp;nbsp;1개면&amp;nbsp;마스터&amp;nbsp;노드가&amp;nbsp;유실되었을&amp;nbsp;때&amp;nbsp;클러스터&amp;nbsp;전체가&amp;nbsp;작동하지&amp;nbsp;않을&amp;nbsp;위험이&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;짝수&amp;nbsp;개라면&amp;nbsp;네트워크가&amp;nbsp;끊겼을&amp;nbsp;때&amp;nbsp;정합성&amp;nbsp;이슈가&amp;nbsp;생길&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;마스터&amp;nbsp;노드는&amp;nbsp;3개&amp;nbsp;이상의&amp;nbsp;홀수&amp;nbsp;개를&amp;nbsp;놓은&amp;nbsp;것을&amp;nbsp;권장,&amp;nbsp;마스터&amp;nbsp;후보&amp;nbsp;노드가&amp;nbsp;최소&amp;nbsp;2개&amp;nbsp;이상&amp;nbsp;존재하도록&amp;nbsp;하는&amp;nbsp;것이&amp;nbsp;좋음&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;샤드&lt;br /&gt;-&amp;nbsp;샤드:&amp;nbsp;인덱스는&amp;nbsp;기본적으로&amp;nbsp;샤드(shard)라는&amp;nbsp;단위로&amp;nbsp;분리되고,&amp;nbsp;각&amp;nbsp;노드에&amp;nbsp;분산되어&amp;nbsp;저장됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;클러스터에&amp;nbsp;노드를&amp;nbsp;추가하면,&amp;nbsp;샤드들이&amp;nbsp;각&amp;nbsp;노드들로&amp;nbsp;분산됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;기본적으로&amp;nbsp;1개의&amp;nbsp;복제본을&amp;nbsp;생성함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;처음&amp;nbsp;생성된&amp;nbsp;샤드를&amp;nbsp;프라이머리&amp;nbsp;샤드(Primary&amp;nbsp;Shard),&amp;nbsp;복제본은&amp;nbsp;리플리카(Replica)라고&amp;nbsp;함&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;600&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcHfYD/btqVFBxQWp5/SEggURtapSvnOF5aSq5zW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcHfYD/btqVFBxQWp5/SEggURtapSvnOF5aSq5zW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcHfYD/btqVFBxQWp5/SEggURtapSvnOF5aSq5zW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcHfYD%2FbtqVFBxQWp5%2FSEggURtapSvnOF5aSq5zW0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;600&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;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;최소&amp;nbsp;3개의&amp;nbsp;노드로&amp;nbsp;구성할&amp;nbsp;것을&amp;nbsp;권장함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;샤드는&amp;nbsp;반드시&amp;nbsp;서로&amp;nbsp;다른&amp;nbsp;노드에&amp;nbsp;저장되며,&amp;nbsp;노드가&amp;nbsp;유실되면&amp;nbsp;남아있던&amp;nbsp;샤드가&amp;nbsp;프라이머리로&amp;nbsp;승격되고&amp;nbsp;다른&amp;nbsp;노드로&amp;nbsp;복제본을&amp;nbsp;생성함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;a href=&quot;https://esbook.kimjmin.net/03-cluster/3.2-index-and-shards&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://esbook.kimjmin.net/03-cluster/3.2-index-and-shards&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;샤드는&amp;nbsp;인덱스&amp;nbsp;단위로&amp;nbsp;생성할&amp;nbsp;수&amp;nbsp;있고,&amp;nbsp;샤드의&amp;nbsp;개수는&amp;nbsp;인덱스를&amp;nbsp;처음&amp;nbsp;생성할&amp;nbsp;때&amp;nbsp;지정할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;프라이머리&amp;nbsp;샤드의&amp;nbsp;개수는&amp;nbsp;인덱스&amp;nbsp;재색인&amp;nbsp;전에는&amp;nbsp;변경할&amp;nbsp;수&amp;nbsp;없음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;number_of_shards:&amp;nbsp;프라이머리&amp;nbsp;샤드&amp;nbsp;개수&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;number_of_replicas:&amp;nbsp;리플리카&amp;nbsp;개수&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;프라이머리&amp;nbsp;5개,&amp;nbsp;리플리카&amp;nbsp;1개&amp;nbsp;=&amp;nbsp;5&amp;nbsp;*&amp;nbsp;(1&amp;nbsp;+&amp;nbsp;1)&amp;nbsp;=&amp;nbsp;총&amp;nbsp;10개의&amp;nbsp;샤드&lt;br /&gt;-&amp;nbsp;유실되지&amp;nbsp;않으려면,&amp;nbsp;노드&amp;nbsp;개수&amp;nbsp;*&amp;nbsp;2&amp;nbsp;*&amp;nbsp;2&amp;nbsp;정도의&amp;nbsp;샤드는&amp;nbsp;있어야&amp;nbsp;할&amp;nbsp;듯?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;인덱스&amp;nbsp;설정&lt;br /&gt;- 인덱스는 두 개의 정보를 가지고 있음. settings &amp;amp; mappings&lt;br /&gt;- &lt;a href=&quot;https://esbook.kimjmin.net/07-settings-and-mappings/7.1-settings&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://esbook.kimjmin.net/07-settings-and-mappings/7.1-settings&lt;/a&gt;&lt;br /&gt;-&amp;nbsp;settings&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;프라이머리&amp;nbsp;샤드&amp;nbsp;수는&amp;nbsp;인덱스를&amp;nbsp;생성할&amp;nbsp;때&amp;nbsp;한&amp;nbsp;번&amp;nbsp;설정하면&amp;nbsp;바꾸기&amp;nbsp;어려움&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 7.x부터 기본 샤드 수는 1개, 6버전 이하는 5개였다고 함&lt;br /&gt;-&amp;nbsp;mappings&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;properties&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;생략하면&amp;nbsp;값을&amp;nbsp;보고&amp;nbsp;직접&amp;nbsp;설정함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;매핑&amp;nbsp;필드를&amp;nbsp;한&amp;nbsp;번&amp;nbsp;설정하면&amp;nbsp;변경할&amp;nbsp;수&amp;nbsp;없음&amp;nbsp;(인덱스를&amp;nbsp;새로&amp;nbsp;만들어야&amp;nbsp;함)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;type&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;타입&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;문자열&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;text&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;a href=&quot;https://esbook.kimjmin.net/07-settings-and-mappings/7.2-mappings/7.2.1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://esbook.kimjmin.net/07-settings-and-mappings/7.2-mappings/7.2.1&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 문자열 역인덱스를 생성함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;analizer,&amp;nbsp;search_analizer를&amp;nbsp;설정할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;keyword:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;입력된&amp;nbsp;문자열을&amp;nbsp;하나의&amp;nbsp;토큰으로&amp;nbsp;저장&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;집계나&amp;nbsp;정렬할&amp;nbsp;때&amp;nbsp;사용하는&amp;nbsp;경우&amp;nbsp;자주&amp;nbsp;사용함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;normalizer&amp;nbsp;적용이&amp;nbsp;가능&amp;nbsp;(노멀라이저는&amp;nbsp;캐릭터&amp;nbsp;필터와&amp;nbsp;토큰&amp;nbsp;필터만&amp;nbsp;적용&amp;nbsp;가능)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;숫자&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;long,&amp;nbsp;integer,&amp;nbsp;short,&amp;nbsp;byte,&amp;nbsp;double,&amp;nbsp;float,&amp;nbsp;hal_float,&amp;nbsp;scaled_float&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- coerce 옵션이 있음. 자동으로 형변환 됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- ignore_malformed 를 true 로 설정하면, 값이 오류가 있어도 저장함. 소스에는 저장이 되나 검색 대상에서는 제외됨 (기본값 false)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;date&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;ISO8601&amp;nbsp;포맷면&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;format&amp;nbsp;으로&amp;nbsp;별도&amp;nbsp;포맷을&amp;nbsp;설정할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;epoch_mills,&amp;nbsp;epoch_second&amp;nbsp;도&amp;nbsp;사용&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;&quot;format&quot;:&amp;nbsp;&quot;yyyy-MM-dd&amp;nbsp;HH:mm:ss||yyyy/MM/dd||epoch_millis&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;boolean&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;true&amp;nbsp;또는&amp;nbsp;문자열&amp;nbsp;&quot;true&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;object&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;객체&amp;nbsp;형태로&amp;nbsp;넣을&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;다만,&amp;nbsp;검색할&amp;nbsp;때&amp;nbsp;query&amp;nbsp;내에&amp;nbsp;&quot;nested&quot;&amp;nbsp;쿼리를&amp;nbsp;사용해야&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;geo&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;위도(latitude)와&amp;nbsp;경도(logitude)를&amp;nbsp;넣음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- &lt;a href=&quot;https://esbook.kimjmin.net/07-settings-and-mappings/7.2-mappings/7.2.6-geo&quot;&gt;https://esbook.kimjmin.net/07-settings-and-mappings/7.2-mappings/7.2.6-geo&lt;/a&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;geo_point와&amp;nbsp;geo_shape&amp;nbsp;형식으로&amp;nbsp;저장&amp;nbsp;및&amp;nbsp;검색&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;geo_shape:&amp;nbsp;point,&amp;nbsp;multipoint,&amp;nbsp;linestring,&amp;nbsp;mutilinearstring,&amp;nbsp;polygon,&amp;nbsp;mutlipolygon,&amp;nbsp;envelope&amp;nbsp;(직사각형&amp;nbsp;영역)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;폴리곤과&amp;nbsp;멀티폴리곤의&amp;nbsp;한&amp;nbsp;폴리곤의&amp;nbsp;시작점과&amp;nbsp;끝점은&amp;nbsp;동일해야&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;포인트&amp;nbsp;쿼리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;geo_bounding_box:&amp;nbsp;박스&amp;nbsp;내&amp;nbsp;좌표&amp;nbsp;검색&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;geo_distance:&amp;nbsp;특정&amp;nbsp;포인트부터&amp;nbsp;원형으로&amp;nbsp;영역&amp;nbsp;내&amp;nbsp;좌표&amp;nbsp;검색&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;geo_shape&amp;nbsp;&amp;nbsp;쿼리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;relation&amp;nbsp;으로&amp;nbsp;검색&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;intersects:&amp;nbsp;기본값^&amp;nbsp;일부만&amp;nbsp;겹치면&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;disjoint:&amp;nbsp;겹치지&amp;nbsp;않는&amp;nbsp;도큐먼트&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;within:&amp;nbsp;완전이&amp;nbsp;포함되어&amp;nbsp;있는&amp;nbsp;도큐먼트&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이&amp;nbsp;외에&amp;nbsp;geo_polygon으로&amp;nbsp;검색할&amp;nbsp;수도&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;참고로,&amp;nbsp;전처리된&amp;nbsp;데이터가&amp;nbsp;아니면&amp;nbsp;항상&amp;nbsp;_source&amp;nbsp;의&amp;nbsp;값은&amp;nbsp;변경되지&amp;nbsp;않음&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;br /&gt;- 시작하기 영상: &lt;a href=&quot;https://www.elastic.co/kr/webinars/getting-started-elasticsearch&quot;&gt;https://www.elastic.co/kr/webinars/getting-started-elasticsearch&lt;/a&gt;&lt;br /&gt;- 가이드북: &lt;a href=&quot;https://esbook.kimjmin.net/01-overview/1.1-elastic-stack&quot;&gt;https://esbook.kimjmin.net/01-overview/1.1-elastic-stack&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/Etc</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/794</guid>
      <comments>https://ohgyun.com/794#entry794comment</comments>
      <pubDate>Wed, 3 Feb 2021 23:50:10 +0900</pubDate>
    </item>
    <item>
      <title>파일을 바이너리로 보기</title>
      <link>https://ohgyun.com/793</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2020.12.23&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; &lt;span style=&quot;color: #333333;&quot;&gt;byte, binary, 바이너리, 바이트, hex, hexdump, xdd, od&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;머신러닝 예제를 돌려보고 있는데, 1바이트에 1개의 레이블이 있고 10개씩 묶으로 가져와 처리하는 예제였다.&lt;/p&gt;
&lt;p&gt;파일을 특정 바이트 단위로 묶어서 바이너리 형태로 보고 싶은데 어떻게 하면 될까?&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;xxd 툴로 바이너리로 읽어볼 수 있다.&lt;/p&gt;
&lt;p&gt;기억을 더듬느라 좀 헷갈렸는데,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1byte&lt;/p&gt;
&lt;p&gt;= 8bit&lt;/p&gt;
&lt;p&gt;= 0~255 까지 256을 표현&lt;/p&gt;
&lt;p&gt;= 2진수(binary)로 8자리 (00000000 ~ 11111111)&lt;/p&gt;
&lt;p&gt;= 2진수는 4자리 씩 끊어 보여주면 16진수 (0000 0000 ~ 1111 1111)&lt;/p&gt;
&lt;p&gt;= 16진수(hexadecimal)로 2자리 (00 ~ ff)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;xxd 나 hexdump 명령으로 바이너리 파일을 hex 로 볼 수 있음&lt;/p&gt;
&lt;p&gt;xxd 는 출력 그룹을 지정할 수 있고, 컬럼 개수도 보여줄 수 있어 편하다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 명령은 1byte 씩 구분해서 보여주고(-g 1), 한 컬러에 10개(-c 10) 즉 10 바이트씩 보여주겠단 얘기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;484&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clCNZu/btqVCcE4gJW/68li7kdy0riJJwgEan8MPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clCNZu/btqVCcE4gJW/68li7kdy0riJJwgEan8MPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clCNZu/btqVCcE4gJW/68li7kdy0riJJwgEan8MPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclCNZu%2FbtqVCcE4gJW%2F68li7kdy0riJJwgEan8MPK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;484&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/Unix</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/793</guid>
      <comments>https://ohgyun.com/793#entry793comment</comments>
      <pubDate>Wed, 3 Feb 2021 23:10:44 +0900</pubDate>
    </item>
    <item>
      <title>AI For Everyone 노트</title>
      <link>https://ohgyun.com/792</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2020.12.07&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt; Coursera, Andrew Ng, Deep Learning, AI&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;코세라의 AI For Everyone 강의를 보고 정리한 노트 (&lt;a href=&quot;https://www.coursera.org/learn/ai-for-everyone&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;강의 링크&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;AI&amp;nbsp;&amp;gt;&amp;nbsp;머신러닝&amp;nbsp;&amp;gt;&amp;nbsp;딥러닝&amp;nbsp;=&amp;nbsp;Neural&amp;nbsp;Network&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;딥러닝은&amp;nbsp;뉴런에서&amp;nbsp;영감을&amp;nbsp;받았지만,&amp;nbsp;사람의&amp;nbsp;뇌의&amp;nbsp;동작&amp;nbsp;방식은&amp;nbsp;알&amp;nbsp;수&amp;nbsp;없음&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;머신러닝의&amp;nbsp;대부분은&amp;nbsp;지도&amp;nbsp;학습(Supervised&amp;nbsp;Learning)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Input&amp;nbsp;A&amp;nbsp;로&amp;nbsp;B를&amp;nbsp;매핑하는&amp;nbsp;것&amp;nbsp;(A&amp;nbsp;to&amp;nbsp;B&amp;nbsp;mapping)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;사람이&amp;nbsp;1초&amp;nbsp;안에&amp;nbsp;판단할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;것이라면&amp;nbsp;지도&amp;nbsp;학습으로&amp;nbsp;구현&amp;nbsp;가능&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-&amp;nbsp;AI&amp;nbsp;팀과&amp;nbsp;DS&amp;nbsp;팀은&amp;nbsp;다름&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI&amp;nbsp;팀은&amp;nbsp;머신러닝을&amp;nbsp;활용한&amp;nbsp;자동화에&amp;nbsp;중점&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;DS&amp;nbsp;팀은&amp;nbsp;데이터를&amp;nbsp;분석해서&amp;nbsp;인사이트를&amp;nbsp;얻는&amp;nbsp;것에&amp;nbsp;중점&amp;nbsp;(의사&amp;nbsp;결정,&amp;nbsp;프리젠테이션&amp;nbsp;등)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;머그컵&amp;nbsp;판매&amp;nbsp;사업이라면,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;DS팀:&amp;nbsp;데이터를&amp;nbsp;분석해서&amp;nbsp;어떤&amp;nbsp;지역에서&amp;nbsp;판매가&amp;nbsp;잘&amp;nbsp;되는지&amp;nbsp;분석하고&amp;nbsp;전략에&amp;nbsp;도움&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI팀:&amp;nbsp;머그컵&amp;nbsp;사진을&amp;nbsp;보고&amp;nbsp;불량품&amp;nbsp;파악&amp;nbsp;자동화&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;직업을&amp;nbsp;대체하는&amp;nbsp;것보다는&amp;nbsp;작업을&amp;nbsp;대체하는&amp;nbsp;것이&amp;nbsp;효율적임&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;비지니스&amp;nbsp;가치를&amp;nbsp;올릴&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;곳에&amp;nbsp;AI를&amp;nbsp;적용하면&amp;nbsp;더&amp;nbsp;가치&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;즉,&amp;nbsp;먼저&amp;nbsp;비즈니스에서&amp;nbsp;중요한&amp;nbsp;걸&amp;nbsp;파악하란&amp;nbsp;뜻&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;수용가능한&amp;nbsp;기준을&amp;nbsp;세워야&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;성능&amp;nbsp;기준&amp;nbsp;95%&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;기술을&amp;nbsp;사는&amp;nbsp;게&amp;nbsp;나을까?&amp;nbsp;팀을&amp;nbsp;꾸리는&amp;nbsp;게&amp;nbsp;나을까?&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;일반적인&amp;nbsp;산업에서&amp;nbsp;필요한&amp;nbsp;기술(산업&amp;nbsp;표준)이라면&amp;nbsp;사는&amp;nbsp;게&amp;nbsp;나음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;기차&amp;nbsp;앞에서&amp;nbsp;뛰지&amp;nbsp;마라:&amp;nbsp;산업&amp;nbsp;표준의&amp;nbsp;발전&amp;nbsp;속도가&amp;nbsp;더&amp;nbsp;빠르므로&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;우리&amp;nbsp;사업에&amp;nbsp;특화된&amp;nbsp;것이라면&amp;nbsp;팀을&amp;nbsp;꾸리는&amp;nbsp;게&amp;nbsp;나음&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;AI는&amp;nbsp;구조적으로&amp;nbsp;100%&amp;nbsp;정확도를&amp;nbsp;달성하기&amp;nbsp;어렵다&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;비즈니스에서&amp;nbsp;의미있는&amp;nbsp;정확도를&amp;nbsp;설정해도&amp;nbsp;큰&amp;nbsp;도움이&amp;nbsp;됨&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;스마트&amp;nbsp;스피커&amp;nbsp;구현&amp;nbsp;예&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;&quot;헤이&amp;nbsp;구글,&amp;nbsp;10시에&amp;nbsp;알람&amp;nbsp;맞춰줘&quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;호출&amp;nbsp;단어&amp;nbsp;인식:&amp;nbsp;오디오에서&amp;nbsp;'헤이&amp;nbsp;구글'이&amp;nbsp;있는지&amp;nbsp;없는지&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;음성&amp;nbsp;인식:&amp;nbsp;'10시에&amp;nbsp;알람&amp;nbsp;맞춰줘'를&amp;nbsp;텍스트로&amp;nbsp;변환&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3.&amp;nbsp;의도&amp;nbsp;인식:&amp;nbsp;알람,&amp;nbsp;10시&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4.&amp;nbsp;명령&amp;nbsp;실행&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;명령&amp;nbsp;만큼의&amp;nbsp;의도&amp;nbsp;인식&amp;nbsp;분류가&amp;nbsp;필요함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;대부분&amp;nbsp;supervised&amp;nbsp;learning&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;자율&amp;nbsp;주행&amp;nbsp;구현&amp;nbsp;예&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;역시&amp;nbsp;supervised&amp;nbsp;learning&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;이미지/라이다/레이더&amp;nbsp;+&amp;nbsp;GPS/MAP&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;자동차&amp;nbsp;인식,&amp;nbsp;보행자&amp;nbsp;인식&amp;nbsp;(이후&amp;nbsp;방향&amp;nbsp;예측),&amp;nbsp;경로&amp;nbsp;인식,&amp;nbsp;신호등&amp;nbsp;인식,&amp;nbsp;장애물&amp;nbsp;인식&amp;nbsp;등...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3.&amp;nbsp;모션&amp;nbsp;플래닝&amp;nbsp;(가속,&amp;nbsp;핸들&amp;nbsp;등)&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;Edge&amp;nbsp;Deployment&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;유저&amp;nbsp;디바이스에&amp;nbsp;직접&amp;nbsp;배포하는&amp;nbsp;것&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;네트워크&amp;nbsp;비용&amp;nbsp;없이&amp;nbsp;빨리&amp;nbsp;응답&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;자율주행&amp;nbsp;차량에&amp;nbsp;탑재,&amp;nbsp;스마트&amp;nbsp;스피커에&amp;nbsp;탑재&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;AI&amp;nbsp;논문&amp;nbsp;사이트:&amp;nbsp;Arxiv&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;규모가&amp;nbsp;있는&amp;nbsp;AI&amp;nbsp;팀의&amp;nbsp;구성&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Software&amp;nbsp;Engineer&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Machine&amp;nbsp;Learning&amp;nbsp;Engineer&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Machine&amp;nbsp;Learning&amp;nbsp;Resercher&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Data&amp;nbsp;Scientist:&amp;nbsp;데이터로&amp;nbsp;의사&amp;nbsp;결정에&amp;nbsp;도움&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Data&amp;nbsp;Engineer:&amp;nbsp;데이터&amp;nbsp;구성,&amp;nbsp;접근&amp;nbsp;관리&amp;nbsp;등&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI&amp;nbsp;Product&amp;nbsp;Manager:&amp;nbsp;어떻게&amp;nbsp;빌드할&amp;nbsp;건지,&amp;nbsp;구현&amp;nbsp;가능한지,&amp;nbsp;가치가&amp;nbsp;있는지&amp;nbsp;결정&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;AI&amp;nbsp;Transformation&amp;nbsp;Playbook:&amp;nbsp;훌륭한&amp;nbsp;AI&amp;nbsp;회사를&amp;nbsp;만드는&amp;nbsp;법&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;추친력을&amp;nbsp;얻기&amp;nbsp;위해&amp;nbsp;파일럿&amp;nbsp;프로젝트&amp;nbsp;진행&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;가치보단&amp;nbsp;성공&amp;nbsp;가능성에&amp;nbsp;집중해서&amp;nbsp;시작&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;일단&amp;nbsp;성공을&amp;nbsp;해서&amp;nbsp;한&amp;nbsp;팀에서&amp;nbsp;지지를&amp;nbsp;얻는&amp;nbsp;것이&amp;nbsp;중요&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;구글&amp;nbsp;브레인에서는&amp;nbsp;음성&amp;nbsp;인식&amp;nbsp;팀에&amp;nbsp;처음으로&amp;nbsp;딥러닝을&amp;nbsp;적용함(음성인식은&amp;nbsp;광고나&amp;nbsp;검색만큼&amp;nbsp;가치가&amp;nbsp;있진&amp;nbsp;않음)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;여기서&amp;nbsp;성공한&amp;nbsp;후,&amp;nbsp;구글&amp;nbsp;맵&amp;nbsp;팀&amp;nbsp;등으로&amp;nbsp;연결됨.&amp;nbsp;이후에&amp;nbsp;온라인&amp;nbsp;광고까지&amp;nbsp;적용&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;파일럿&amp;nbsp;프로젝트는&amp;nbsp;단기(6-12개월)에&amp;nbsp;결과를&amp;nbsp;보여줘야&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;자체&amp;nbsp;팀도&amp;nbsp;좋지만,&amp;nbsp;처음엔&amp;nbsp;결과를&amp;nbsp;빨리&amp;nbsp;얻고&amp;nbsp;전문가의&amp;nbsp;경험을&amp;nbsp;얻을&amp;nbsp;수&amp;nbsp;있으니&amp;nbsp;외주여도&amp;nbsp;괜찮음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;자체&amp;nbsp;AI&amp;nbsp;팀&amp;nbsp;구성&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI를&amp;nbsp;company-wide&amp;nbsp;platform&amp;nbsp;으로&amp;nbsp;구축할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;CEO&amp;nbsp;직속(내지는&amp;nbsp;비즈니스&amp;nbsp;유닛과&amp;nbsp;평행하게)&amp;nbsp;구성하는&amp;nbsp;것이&amp;nbsp;좋음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;CEO,&amp;nbsp;CTO,&amp;nbsp;CIO&amp;nbsp;직속일&amp;nbsp;수도&amp;nbsp;있고,&amp;nbsp;CAIO&amp;nbsp;직속일&amp;nbsp;수도&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;비즈니스&amp;nbsp;펀딩을&amp;nbsp;위해&amp;nbsp;AI팀을&amp;nbsp;조직하는&amp;nbsp;것&amp;nbsp;아니라,&amp;nbsp;AI&amp;nbsp;조직을&amp;nbsp;위해&amp;nbsp;펀딩을&amp;nbsp;받은&amp;nbsp;경우&amp;nbsp;더&amp;nbsp;성과가&amp;nbsp;좋았음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3.&amp;nbsp;AI&amp;nbsp;이해를&amp;nbsp;위한&amp;nbsp;학습&amp;nbsp;전파&amp;nbsp;(Provide&amp;nbsp;broad&amp;nbsp;AI&amp;nbsp;training)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;임원&amp;nbsp;레벨은&amp;nbsp;4시간&amp;nbsp;이상,&amp;nbsp;리더는&amp;nbsp;12시간&amp;nbsp;이상&amp;nbsp;AI에&amp;nbsp;대해&amp;nbsp;학습하도록&amp;nbsp;하는&amp;nbsp;게&amp;nbsp;좋음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI&amp;nbsp;개발자를&amp;nbsp;채용하는&amp;nbsp;대신&amp;nbsp;내부에서&amp;nbsp;기르는&amp;nbsp;것도&amp;nbsp;좋은&amp;nbsp;방법&amp;nbsp;(100시간&amp;nbsp;학습)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;The&amp;nbsp;smart&amp;nbsp;CLO&amp;nbsp;knows&amp;nbsp;they&amp;nbsp;should&amp;nbsp;curate&amp;nbsp;rather&amp;nbsp;than&amp;nbsp;create&amp;nbsp;(CLO&amp;nbsp;=&amp;nbsp;learning)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;4.&amp;nbsp;AI&amp;nbsp;전략&amp;nbsp;수립&amp;nbsp;(Develop&amp;nbsp;an&amp;nbsp;AI&amp;nbsp;strategy)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;전략을&amp;nbsp;세우는&amp;nbsp;것이&amp;nbsp;첫&amp;nbsp;번째가&amp;nbsp;아닌&amp;nbsp;이유는,&amp;nbsp;먼저&amp;nbsp;AI를&amp;nbsp;우리&amp;nbsp;회사의&amp;nbsp;비즈니스에&amp;nbsp;적합하게&amp;nbsp;적용하는&amp;nbsp;것을&amp;nbsp;이해해야&amp;nbsp;하기&amp;nbsp;때문&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI&amp;nbsp;선순환&amp;nbsp;구조를&amp;nbsp;만드는&amp;nbsp;것이&amp;nbsp;중요&amp;nbsp;(좋은&amp;nbsp;제품&amp;nbsp;&amp;gt;&amp;nbsp;많은&amp;nbsp;사용자&amp;nbsp;&amp;gt;&amp;nbsp;많은&amp;nbsp;데이터&amp;nbsp;&amp;gt;&amp;nbsp;좋은&amp;nbsp;제품)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;검색&amp;nbsp;같은&amp;nbsp;것은&amp;nbsp;기존&amp;nbsp;업체와&amp;nbsp;경쟁하기&amp;nbsp;어렵지만,&amp;nbsp;스타트업이&amp;nbsp;새로운&amp;nbsp;분야에&amp;nbsp;진입할&amp;nbsp;때&amp;nbsp;큰&amp;nbsp;장점이&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;양배추와&amp;nbsp;잡초를&amp;nbsp;구분하는&amp;nbsp;트랙터&amp;nbsp;사업&amp;nbsp;예&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이렇게&amp;nbsp;하면&amp;nbsp;defensible&amp;nbsp;company&amp;nbsp;를&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있음&amp;nbsp;(신규&amp;nbsp;사업자가&amp;nbsp;진입하기&amp;nbsp;어려운&amp;nbsp;구글&amp;nbsp;같은&amp;nbsp;회사)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI는&amp;nbsp;대부분&amp;nbsp;해당&amp;nbsp;사업군에&amp;nbsp;특화된&amp;nbsp;경우가&amp;nbsp;많기&amp;nbsp;때문에,&amp;nbsp;대형&amp;nbsp;기술&amp;nbsp;회사의&amp;nbsp;AI&amp;nbsp;기술과&amp;nbsp;견주어서&amp;nbsp;지레&amp;nbsp;겁먹을&amp;nbsp;필요가&amp;nbsp;없음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;데이터&amp;nbsp;수집&amp;nbsp;전략를&amp;nbsp;세우는&amp;nbsp;것을&amp;nbsp;고려&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;데이터&amp;nbsp;수집&amp;nbsp;전략:&amp;nbsp;무료&amp;nbsp;에메일,&amp;nbsp;사진&amp;nbsp;서비스가&amp;nbsp;무료로&amp;nbsp;(광고도&amp;nbsp;없이)&amp;nbsp;서비스를&amp;nbsp;제공하는&amp;nbsp;이유&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;통합된&amp;nbsp;데이터&amp;nbsp;웨어하우스&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;네트워크&amp;nbsp;효과와&amp;nbsp;플랫폼의&amp;nbsp;장점을&amp;nbsp;고려&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;승자독식&amp;nbsp;구조의&amp;nbsp;산업에서는&amp;nbsp;AI가&amp;nbsp;큰&amp;nbsp;도움이&amp;nbsp;될&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;5.&amp;nbsp;커뮤니케이션&amp;nbsp;(Develop&amp;nbsp;an&amp;nbsp;internal&amp;nbsp;and&amp;nbsp;external&amp;nbsp;communications)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI는&amp;nbsp;회사와&amp;nbsp;제품에&amp;nbsp;변화를&amp;nbsp;줄&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;투자자:&amp;nbsp;AI&amp;nbsp;회사로서의&amp;nbsp;가치를&amp;nbsp;적합하게&amp;nbsp;평가할&amp;nbsp;수&amp;nbsp;있게&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;정부:&amp;nbsp;공익에&amp;nbsp;도움이&amp;nbsp;됨&amp;nbsp;(규제가&amp;nbsp;많았던&amp;nbsp;헬스케어,&amp;nbsp;자율주행&amp;nbsp;분야...)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;소비자/사용자에게&amp;nbsp;교육해서&amp;nbsp;인식을&amp;nbsp;바꿔주는&amp;nbsp;것도&amp;nbsp;중요&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI&amp;nbsp;성공&amp;nbsp;사례는&amp;nbsp;드물어서,&amp;nbsp;커뮤니케이션하면&amp;nbsp;채용에&amp;nbsp;도움이&amp;nbsp;됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;내부&amp;nbsp;커뮤니케이션도&amp;nbsp;중요:&amp;nbsp;직원들이&amp;nbsp;걱정하는&amp;nbsp;경우도&amp;nbsp;있어서&amp;nbsp;안심을&amp;nbsp;주는&amp;nbsp;것이&amp;nbsp;도움이&amp;nbsp;됨&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;AI&amp;nbsp;팀을&amp;nbsp;만들려고&amp;nbsp;할&amp;nbsp;때&amp;nbsp;일반적인&amp;nbsp;위험&amp;nbsp;(Don't&amp;nbsp;/&amp;nbsp;Do)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Don't:&amp;nbsp;AI로&amp;nbsp;모든&amp;nbsp;걸&amp;nbsp;해결하려는&amp;nbsp;것&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI의&amp;nbsp;한계를&amp;nbsp;인정하고~&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Don't:&amp;nbsp;머신러닝&amp;nbsp;개발자를&amp;nbsp;단독으로&amp;nbsp;2-3명&amp;nbsp;고용하는&amp;nbsp;것&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;비즈니스&amp;nbsp;전문가와&amp;nbsp;개발자&amp;nbsp;한&amp;nbsp;명의&amp;nbsp;쌍이&amp;nbsp;더&amp;nbsp;나음&amp;nbsp;(구현&amp;nbsp;가능&amp;nbsp;여부와&amp;nbsp;비즈니스&amp;nbsp;가치를&amp;nbsp;모두&amp;nbsp;고려할&amp;nbsp;수&amp;nbsp;있음)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Don't:&amp;nbsp;첫&amp;nbsp;번째&amp;nbsp;시도로&amp;nbsp;AI&amp;nbsp;프로젝트가&amp;nbsp;동작할&amp;nbsp;거라고&amp;nbsp;기대&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;되는&amp;nbsp;경우는&amp;nbsp;잘&amp;nbsp;없음.&amp;nbsp;반복된&amp;nbsp;시도로&amp;nbsp;성공할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Don't:&amp;nbsp;전통적인&amp;nbsp;방법으로&amp;nbsp;계획&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;타임라인과&amp;nbsp;마일스톤,&amp;nbsp;KPI를&amp;nbsp;세울&amp;nbsp;때&amp;nbsp;AI&amp;nbsp;팀과&amp;nbsp;함께&amp;nbsp;논의&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Don't:&amp;nbsp;시작&amp;nbsp;전&amp;nbsp;AI&amp;nbsp;슈퍼스타가&amp;nbsp;필요할&amp;nbsp;지&amp;nbsp;고려&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;팀을&amp;nbsp;만들어서&amp;nbsp;시도해보는&amp;nbsp;게&amp;nbsp;좋음&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;AI&amp;nbsp;팀&amp;nbsp;시작하기&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;스터디할&amp;nbsp;팀을&amp;nbsp;구성하고&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;브레인스토밍&amp;nbsp;프로젝트를&amp;nbsp;시작&amp;nbsp;(No&amp;nbsp;project&amp;nbsp;is&amp;nbsp;too&amp;nbsp;small)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;도움이&amp;nbsp;필요하다면&amp;nbsp;ML/DS&amp;nbsp;엔지니어를&amp;nbsp;고용&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI&amp;nbsp;Leader&amp;nbsp;(VP&amp;nbsp;AI,&amp;nbsp;CAIO,&amp;nbsp;...)를&amp;nbsp;고용하거나&amp;nbsp;임명.&amp;nbsp;최근엔&amp;nbsp;CAIO&amp;nbsp;라는&amp;nbsp;직급이&amp;nbsp;많이&amp;nbsp;생겼다고&amp;nbsp;함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;CEO와&amp;nbsp;AI&amp;nbsp;전환에&amp;nbsp;대해&amp;nbsp;논의&amp;nbsp;(Key&amp;nbsp;Question:&amp;nbsp;AI를&amp;nbsp;적용하면&amp;nbsp;회사의&amp;nbsp;가치가&amp;nbsp;높아지거나&amp;nbsp;효율적이&amp;nbsp;될까?)&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;AI의&amp;nbsp;한계&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;성능&amp;nbsp;이슈&amp;nbsp;(많은&amp;nbsp;데이터가&amp;nbsp;필요함)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;과정을&amp;nbsp;설명하기&amp;nbsp;어려움&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;가능한&amp;nbsp;경우도&amp;nbsp;있으나&amp;nbsp;대체로&amp;nbsp;판단한&amp;nbsp;과정을&amp;nbsp;설명하기&amp;nbsp;어려움&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;판단의&amp;nbsp;과정을&amp;nbsp;표현하는&amp;nbsp;것도&amp;nbsp;주요&amp;nbsp;연구&amp;nbsp;대상&amp;nbsp;중의&amp;nbsp;하나로&amp;nbsp;진행&amp;nbsp;중임&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;사람도&amp;nbsp;잘&amp;nbsp;설명하지&amp;nbsp;못함.&amp;nbsp;(머그컵이&amp;nbsp;왜&amp;nbsp;머그컵인지&amp;nbsp;설명하는&amp;nbsp;것이&amp;nbsp;쉽지&amp;nbsp;않은&amp;nbsp;것처럼)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;편향:&amp;nbsp;편향된&amp;nbsp;정보(인종&amp;nbsp;등)로&amp;nbsp;학습된&amp;nbsp;AI&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AI&amp;nbsp;시스템에&amp;nbsp;대한&amp;nbsp;공격:&amp;nbsp;일부러&amp;nbsp;공격하는&amp;nbsp;경우가&amp;nbsp;있음&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;편향이&amp;nbsp;중요한&amp;nbsp;이유&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;고용&amp;nbsp;도구에서&amp;nbsp;여성이&amp;nbsp;차별됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;얼굴&amp;nbsp;인식&amp;nbsp;도구는&amp;nbsp;흑인보다&amp;nbsp;백인을&amp;nbsp;더&amp;nbsp;잘&amp;nbsp;구별함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;은행&amp;nbsp;대출&amp;nbsp;승인&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;고정관념에&amp;nbsp;의한&amp;nbsp;중독&amp;nbsp;효과가&amp;nbsp;발생함&amp;nbsp;(예:&amp;nbsp;검색&amp;nbsp;결과에서&amp;nbsp;CEO&amp;nbsp;결과는&amp;nbsp;항상&amp;nbsp;백인&amp;nbsp;남성)&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;편향을&amp;nbsp;피하는&amp;nbsp;방법&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;기술적인&amp;nbsp;방법&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;잘&amp;nbsp;사용되지&amp;nbsp;않는&amp;nbsp;단어는&amp;nbsp;0으로&amp;nbsp;처리&amp;nbsp;(이런&amp;nbsp;단어에서&amp;nbsp;주로&amp;nbsp;편향이&amp;nbsp;발생했음)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;데이터가&amp;nbsp;편향되지&amp;nbsp;않게&amp;nbsp;(예:&amp;nbsp;성별/인종별로&amp;nbsp;비슷한&amp;nbsp;비중의&amp;nbsp;데이터를&amp;nbsp;사용)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;검증&amp;nbsp;과정을&amp;nbsp;투명하게.&amp;nbsp;편향을&amp;nbsp;이&amp;nbsp;단계에서&amp;nbsp;제거할&amp;nbsp;수&amp;nbsp;있게&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;다양한&amp;nbsp;인종과&amp;nbsp;성별을&amp;nbsp;가진&amp;nbsp;직원으로&amp;nbsp;팀을&amp;nbsp;구성&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;-&amp;nbsp;AI&amp;nbsp;시스템에&amp;nbsp;대한&amp;nbsp;공격&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이미지&amp;nbsp;인식을&amp;nbsp;방해하는&amp;nbsp;방법&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;토끼&amp;nbsp;이미지&amp;nbsp;픽셀을&amp;nbsp;몇&amp;nbsp;개&amp;nbsp;변경하면,&amp;nbsp;책상으로&amp;nbsp;분류됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;바나나&amp;nbsp;이미지에&amp;nbsp;특정&amp;nbsp;스티커를&amp;nbsp;보여주면&amp;nbsp;토스터로&amp;nbsp;변경됨&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;사기&amp;nbsp;감지,&amp;nbsp;스팸&amp;nbsp;필터&amp;nbsp;같은&amp;nbsp;시스템에서는&amp;nbsp;특히&amp;nbsp;중요함&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;----&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Supervised Learning 구현 기술 예&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Computer&amp;nbsp;Vision&lt;br /&gt;-&amp;nbsp;Image&amp;nbsp;Classification/Object&amp;nbsp;Recognition&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이미지가&amp;nbsp;고양이&amp;nbsp;사진인지&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Face&amp;nbsp;recognition,&amp;nbsp;사람&amp;nbsp;사진인지,&amp;nbsp;같은&amp;nbsp;사람의&amp;nbsp;사진인지&amp;nbsp;등&lt;br /&gt;-&amp;nbsp;Object&amp;nbsp;Detection&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;사진에서&amp;nbsp;차량이나&amp;nbsp;보행자가&amp;nbsp;어디에&amp;nbsp;있는지(영역)&lt;br /&gt;-&amp;nbsp;Image&amp;nbsp;Segmentation&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;차량과&amp;nbsp;보행자의&amp;nbsp;픽셀&amp;nbsp;단위까지&amp;nbsp;파악&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;X-ray&amp;nbsp;사진에서&amp;nbsp;각&amp;nbsp;부위&amp;nbsp;파악&lt;br /&gt;-&amp;nbsp;Tracking&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;마라톤&amp;nbsp;비디오에서&amp;nbsp;사용자의&amp;nbsp;이동&amp;nbsp;위치&amp;nbsp;파악&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;동영상에서&amp;nbsp;새의&amp;nbsp;위치를&amp;nbsp;파악&lt;br /&gt;&lt;br /&gt;Natural&amp;nbsp;Language&amp;nbsp;Processing&lt;br /&gt;-&amp;nbsp;Text&amp;nbsp;classification&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이메일&amp;nbsp;카테고리&amp;nbsp;분류&amp;nbsp;(스팸&amp;nbsp;or&amp;nbsp;정상)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;제품&amp;nbsp;설명으로&amp;nbsp;카테고리&amp;nbsp;분류&amp;nbsp;(당근마켓&amp;nbsp;글&amp;nbsp;등)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;감정(Sentiment)&amp;nbsp;분류&amp;nbsp;(레스토랑&amp;nbsp;리뷰를&amp;nbsp;별점으로&amp;nbsp;환산)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Food&amp;nbsp;was&amp;nbsp;good&amp;nbsp;-&amp;gt;&amp;nbsp;별점&amp;nbsp;4점&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Service&amp;nbsp;was&amp;nbsp;horrible&amp;nbsp;-&amp;gt;&amp;nbsp;별점&amp;nbsp;1점&lt;br /&gt;-&amp;nbsp;Information&amp;nbsp;retrieval&amp;nbsp;(정보&amp;nbsp;반환)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;검색&amp;nbsp;결과에&amp;nbsp;연관&amp;nbsp;문서&amp;nbsp;반환&lt;br /&gt;-&amp;nbsp;Name&amp;nbsp;entity&amp;nbsp;recognition&amp;nbsp;(이름&amp;nbsp;인식)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;문장에서&amp;nbsp;인물의&amp;nbsp;이름을&amp;nbsp;파악&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;회사&amp;nbsp;이름,&amp;nbsp;전화&amp;nbsp;번호,&amp;nbsp;지명&amp;nbsp;등등&lt;br /&gt;-&amp;nbsp;Machine&amp;nbsp;translation&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;번역&lt;br /&gt;-&amp;nbsp;자연어&amp;nbsp;처리의&amp;nbsp;다른&amp;nbsp;부분&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;part-of-speech&amp;nbsp;tagging&amp;nbsp;(품사&amp;nbsp;태깅)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;parsing&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;파싱&amp;nbsp;예:&amp;nbsp;구문을&amp;nbsp;의미&amp;nbsp;단위로&amp;nbsp;연결&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;[the&amp;nbsp;cat]&amp;nbsp;[on&amp;nbsp;the&amp;nbsp;mat]&lt;br /&gt;&lt;br /&gt;Speech&lt;br /&gt;-&amp;nbsp;Speech&amp;nbsp;recognition&amp;nbsp;(speech-to-text)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;시간에&amp;nbsp;따른&amp;nbsp;음파&amp;nbsp;분석&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;최근의&amp;nbsp;음성&amp;nbsp;인식&amp;nbsp;발전은&amp;nbsp;대부분&amp;nbsp;딥러닝&amp;nbsp;덕분&lt;br /&gt;-&amp;nbsp;Trigger&amp;nbsp;word/wakeword&amp;nbsp;detection&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;헤이&amp;nbsp;구글&lt;br /&gt;-&amp;nbsp;Speaker&amp;nbsp;ID&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;화자가&amp;nbsp;누군지&amp;nbsp;분석&lt;br /&gt;-&amp;nbsp;Speech&amp;nbsp;synthesis&amp;nbsp;(text-to-speech,&amp;nbsp;TTS)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;The&amp;nbsp;quick&amp;nbsp;brown&amp;nbsp;fox&amp;nbsp;jumps&amp;nbsp;over&amp;nbsp;the&amp;nbsp;lazy&amp;nbsp;dogs.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;알파벳&amp;nbsp;26자가&amp;nbsp;다&amp;nbsp;있는&amp;nbsp;문장ㅎㅎ&lt;br /&gt;&lt;br /&gt;Robotics&lt;br /&gt;-&amp;nbsp;Perception&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;주변&amp;nbsp;물체를&amp;nbsp;인지&amp;nbsp;(자율&amp;nbsp;주행차)&lt;br /&gt;-&amp;nbsp;Motion&amp;nbsp;planning&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;이동할&amp;nbsp;경로를&amp;nbsp;찾음&lt;br /&gt;-&amp;nbsp;Control&lt;br /&gt;&lt;br /&gt;General&amp;nbsp;machine&amp;nbsp;learning&lt;br /&gt;-&amp;nbsp;Unstructured&amp;nbsp;data&amp;nbsp;(Image,&amp;nbsp;Video,&amp;nbsp;Audio,&amp;nbsp;...)&lt;br /&gt;-&amp;nbsp;Structured&amp;nbsp;data&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;---&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Supervised Learning 외 구현 기술 예&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;Unsupervised&amp;nbsp;learning&lt;br /&gt;-&amp;nbsp;Clustering&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;식료품점에서&amp;nbsp;감자를&amp;nbsp;사는&amp;nbsp;사용자군을&amp;nbsp;클러스터링&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예:&amp;nbsp;낮은&amp;nbsp;가격의&amp;nbsp;칩을&amp;nbsp;많이&amp;nbsp;사는&amp;nbsp;사람들,&amp;nbsp;비싼&amp;nbsp;가격의&amp;nbsp;칩을&amp;nbsp;적게&amp;nbsp;사는&amp;nbsp;사람들&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;구글&amp;nbsp;팀에서는&amp;nbsp;유튜브&amp;nbsp;데이터로&amp;nbsp;비지도&amp;nbsp;학습을&amp;nbsp;시켰더니,&amp;nbsp;고양이&amp;nbsp;동영상이&amp;nbsp;많다는&amp;nbsp;결과를&amp;nbsp;냈다고&amp;nbsp;함&lt;br /&gt;-&amp;nbsp;지도&amp;nbsp;학습의&amp;nbsp;단점은&amp;nbsp;데이터&amp;nbsp;라벨링&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;머그컵을&amp;nbsp;학습시키려고&amp;nbsp;아이들한테&amp;nbsp;머그컵&amp;nbsp;1,000개를&amp;nbsp;학습시키지&amp;nbsp;않으니까...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;사람의&amp;nbsp;뇌가&amp;nbsp;동작하는&amp;nbsp;방식은&amp;nbsp;아직&amp;nbsp;알지&amp;nbsp;못하지만,&amp;nbsp;비지도&amp;nbsp;학습이&amp;nbsp;도움이&amp;nbsp;될&amp;nbsp;수도&amp;nbsp;있을&amp;nbsp;듯&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;현재는&amp;nbsp;지도&amp;nbsp;학습에&amp;nbsp;비해&amp;nbsp;경제적인&amp;nbsp;가치가&amp;nbsp;낮은&amp;nbsp;편&lt;br /&gt;&lt;br /&gt;Transfer&amp;nbsp;learning&lt;br /&gt;-&amp;nbsp;이미&amp;nbsp;학습한&amp;nbsp;모델로&amp;nbsp;다른&amp;nbsp;분야를&amp;nbsp;학습&lt;br /&gt;-&amp;nbsp;대규모&amp;nbsp;데이터로&amp;nbsp;학습한&amp;nbsp;자동차&amp;nbsp;인식&amp;nbsp;모델로&amp;nbsp;소규모&amp;nbsp;데이터가&amp;nbsp;있는&amp;nbsp;골프카트&amp;nbsp;인식&amp;nbsp;모델&amp;nbsp;학습&lt;br /&gt;-&amp;nbsp;컴퓨터&amp;nbsp;비전&amp;nbsp;쪽에서&amp;nbsp;특히&amp;nbsp;많이&amp;nbsp;사용함&lt;br /&gt;&lt;br /&gt;Reinforcement&amp;nbsp;learning&lt;br /&gt;-&amp;nbsp;행동에&amp;nbsp;보상을&amp;nbsp;주는&amp;nbsp;방식으로&amp;nbsp;학습&lt;br /&gt;-&amp;nbsp;아주&amp;nbsp;많은&amp;nbsp;데이터가&amp;nbsp;필요함&lt;br /&gt;-&amp;nbsp;미디어의&amp;nbsp;주목을&amp;nbsp;많이&amp;nbsp;받긴&amp;nbsp;했지만,&amp;nbsp;경제적&amp;nbsp;가치는&amp;nbsp;지도학습에&amp;nbsp;비해&amp;nbsp;미미함&lt;br /&gt;&lt;br /&gt;GANs&amp;nbsp;(Generative&amp;nbsp;Adversarial&amp;nbsp;Network)&lt;br /&gt;-&amp;nbsp;이미지를&amp;nbsp;생성하는데&amp;nbsp;효과적임&lt;br /&gt;-&amp;nbsp;예:&amp;nbsp;연예인&amp;nbsp;이미지를&amp;nbsp;학습해,&amp;nbsp;연예인처럼&amp;nbsp;생긴&amp;nbsp;이미지를&amp;nbsp;생성&lt;br /&gt;&lt;br /&gt;Knowledge&amp;nbsp;Graph&lt;br /&gt;-&amp;nbsp;구글&amp;nbsp;검색&amp;nbsp;우측에&amp;nbsp;나오는&amp;nbsp;인물&amp;nbsp;정보&amp;nbsp;(자동으로&amp;nbsp;생성되는&amp;nbsp;거였다니..)&lt;br /&gt;-&amp;nbsp;데이터로&amp;nbsp;인물&amp;nbsp;정보를&amp;nbsp;생성함&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;출생,&amp;nbsp;사망,&amp;nbsp;이력&amp;nbsp;등&lt;br /&gt;-&amp;nbsp;영화,&amp;nbsp;연예인,&amp;nbsp;호텔,&amp;nbsp;공항&amp;nbsp;등의&amp;nbsp;정보&amp;nbsp;생성에&amp;nbsp;사용되고&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;경제적&amp;nbsp;가치가&amp;nbsp;높은데&amp;nbsp;의외로&amp;nbsp;많이&amp;nbsp;연구되지&amp;nbsp;않고&amp;nbsp;있음&lt;/p&gt;</description>
      <category>Daylogs/AI</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/792</guid>
      <comments>https://ohgyun.com/792#entry792comment</comments>
      <pubDate>Wed, 3 Feb 2021 22:39:04 +0900</pubDate>
    </item>
    <item>
      <title>스케치의 컬러와 크롬의 컬러가 다른 이슈</title>
      <link>https://ohgyun.com/791</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt;&amp;nbsp;2020.04.09&lt;br /&gt;&lt;br /&gt;&lt;b&gt;키워드:&lt;/b&gt;&amp;nbsp;스케치,&amp;nbsp;sketch,&amp;nbsp;컬러&amp;nbsp;스페이스&lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;디자이너가&amp;nbsp;스케치에서&amp;nbsp;작업해준&amp;nbsp;컬러를&amp;nbsp;크롬에&amp;nbsp;적용했는데,&amp;nbsp;컬러&amp;nbsp;코드가&amp;nbsp;동일한데도&amp;nbsp;색상이&amp;nbsp;눈에&amp;nbsp;띄게&amp;nbsp;다르다.&lt;br /&gt;&lt;br /&gt;왜&amp;nbsp;그런&amp;nbsp;걸까?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;프로그램마다&amp;nbsp;사용하는&amp;nbsp;컬러&amp;nbsp;스페이스가&amp;nbsp;다르기&amp;nbsp;때문이었다.&lt;br /&gt;(좀&amp;nbsp;찾아보니&amp;nbsp;컬러&amp;nbsp;스페이스는&amp;nbsp;색&amp;nbsp;공간,&amp;nbsp;색&amp;nbsp;영역,&amp;nbsp;컬러&amp;nbsp;프로필,&amp;nbsp;컬러&amp;nbsp;프로파일,&amp;nbsp;디스플레이&amp;nbsp;프로필&amp;nbsp;같은&amp;nbsp;용어로&amp;nbsp;쓰인다.&amp;nbsp;여기선&amp;nbsp;통일해서&amp;nbsp;컬러&amp;nbsp;스페이스라고&amp;nbsp;표기했다)&lt;br /&gt;&lt;br /&gt;크롬을&amp;nbsp;포한한&amp;nbsp;브라우저의&amp;nbsp;컬러&amp;nbsp;스페이스는&amp;nbsp;일반적으로&amp;nbsp;sRGB&amp;nbsp;이고,&lt;br /&gt;스케치는&amp;nbsp;기본적으로&amp;nbsp;모니터의&amp;nbsp;컬러&amp;nbsp;스페이스를&amp;nbsp;사용한다.&lt;br /&gt;&lt;br /&gt;우린&amp;nbsp;iMac을&amp;nbsp;사용하고&amp;nbsp;있는데,&amp;nbsp;디스플레이&amp;nbsp;속성&amp;nbsp;메뉴에&amp;nbsp;가보면&amp;nbsp;어떤&amp;nbsp;컬러&amp;nbsp;스페이스가&amp;nbsp;적용되어&amp;nbsp;있는지&amp;nbsp;알&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p1ESf/btqDsbksABl/JtUYSlj3VuWS4Jcnxy9sv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p1ESf/btqDsbksABl/JtUYSlj3VuWS4Jcnxy9sv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p1ESf/btqDsbksABl/JtUYSlj3VuWS4Jcnxy9sv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp1ESf%2FbtqDsbksABl%2FJtUYSlj3VuWS4Jcnxy9sv1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;557&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;디자이는 기본적으로 iMac 컬러 스페이스를 사용하고 있었고,&lt;br /&gt;스케치에서&amp;nbsp;컬러를&amp;nbsp;헥스&amp;nbsp;코드로&amp;nbsp;추출할&amp;nbsp;때에도&amp;nbsp;해당&amp;nbsp;컬러&amp;nbsp;스페이스를&amp;nbsp;따르기&amp;nbsp;때문에&amp;nbsp;크롬에서&amp;nbsp;다르게&amp;nbsp;보였던&amp;nbsp;것이었다.&lt;br /&gt;&lt;br /&gt;스케치도&amp;nbsp;포토샵처럼&amp;nbsp;개별&amp;nbsp;컬러&amp;nbsp;스페이스를&amp;nbsp;적용할&amp;nbsp;수&amp;nbsp;있고,&lt;br /&gt;File&amp;nbsp;&amp;gt;&amp;nbsp;Change&amp;nbsp;Color&amp;nbsp;Profile&amp;nbsp;메뉴에서&amp;nbsp;컬러&amp;nbsp;스페이스를&amp;nbsp;변경할&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;참고로,&amp;nbsp;맥에&amp;nbsp;Digital&amp;nbsp;Color&amp;nbsp;Meter&amp;nbsp;라는&amp;nbsp;기본&amp;nbsp;앱을&amp;nbsp;사용하면,&lt;br /&gt;모니터에&amp;nbsp;표시된&amp;nbsp;컬러를&amp;nbsp;여러&amp;nbsp;컬러&amp;nbsp;스페이스의&amp;nbsp;코드로&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;528&quot; width=&quot;408&quot; height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh2nka/btqDsaeJzcM/7qdvapHxTDluDSLGK98klK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh2nka/btqDsaeJzcM/7qdvapHxTDluDSLGK98klK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh2nka/btqDsaeJzcM/7qdvapHxTDluDSLGK98klK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh2nka%2FbtqDsaeJzcM%2F7qdvapHxTDluDSLGK98klK%2Fimg.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;528&quot; width=&quot;408&quot; height=&quot;278&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;402&quot; height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y0rPt/btqDtBiBrf3/cvY5lcmsIBfpBsrAH2qKH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y0rPt/btqDtBiBrf3/cvY5lcmsIBfpBsrAH2qKH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y0rPt/btqDtBiBrf3/cvY5lcmsIBfpBsrAH2qKH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY0rPt%2FbtqDtBiBrf3%2FcvY5lcmsIBfpBsrAH2qKH0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;402&quot; height=&quot;248&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;br /&gt;&lt;br /&gt;&lt;br /&gt;우리는&amp;nbsp;스케치의&amp;nbsp;컬러&amp;nbsp;스페이스를&amp;nbsp;크롬과&amp;nbsp;맞춰서&amp;nbsp;sRGB로&amp;nbsp;변경하는&amp;nbsp;걸로&amp;nbsp;해결했다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;br /&gt;*&amp;nbsp;&lt;a href=&quot;https://www.reddit.com/r/sketchapp/comments/71ntgh/sketch_giving_me_the_wrong_color_hex_codes_looks/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.reddit.com/r/sketchapp/comments/71ntgh/sketch_giving_me_the_wrong_color_hex_codes_looks/&lt;/a&gt;&lt;br /&gt;*&amp;nbsp;&lt;a href=&quot;https://carrotdesign.tistory.com/35&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://carrotdesign.tistory.com/35&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/Etc</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/791</guid>
      <comments>https://ohgyun.com/791#entry791comment</comments>
      <pubDate>Fri, 17 Apr 2020 11:45:58 +0900</pubDate>
    </item>
    <item>
      <title>Array.sort() 정렬 기준은 문자열의 유니코드 포인트</title>
      <link>https://ohgyun.com/790</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2019.05.31&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;키워드:&lt;/b&gt; Array.sort, sort, 정렬&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래와 같이 sort() 함수로 정렬했는데,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; [1, 5, 10, 20].sort();&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;결과가 아래와 같이 기대했던 것처럼 나오지 않는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #b00800;&quot;&gt;&amp;nbsp; &amp;nbsp; [1, 10, 20, 5]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;으잉?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;나만 몰랐나...&lt;/p&gt;
&lt;p&gt;Array.sort() 의 기본 정렬 기준은 문자열의 유니코드 포인트였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;숫자로 정렬하려면 아래와 같이 compareFunction 을 넘겨주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; [1, 5, 10, 20].sort(&lt;span style=&quot;color: #b00800;&quot;&gt;(a, b) =&amp;gt; a - b&lt;/span&gt;);&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기존에 비슷한 코드도 확인해봐야겠다...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우린 유틸리티 모듈로&amp;nbsp;&lt;a href=&quot;https://underscorejs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;언더스코어(underscore.js)&lt;/a&gt;를 쓰고 있는데, 언더스코어의 _.sortBy 는 의도대로 숫자로 정렬된다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jashkenas/underscore/blob/d5fe0fd4060f13b40608cb9d92eda6d857e8752c/underscore.js#L416&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;코드를 확인&lt;/a&gt;해보니, 기본 compareFunction 에서 각 값을 비교하는 과정에서 암묵적으로 형변환되었기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;_.sortBy&amp;nbsp;=&amp;nbsp;function(obj,&amp;nbsp;iteratee,&amp;nbsp;context)&amp;nbsp;{&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;index&amp;nbsp;=&amp;nbsp;0;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;iteratee&amp;nbsp;=&amp;nbsp;cb(iteratee,&amp;nbsp;context);&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;_.pluck(_.map(obj,&amp;nbsp;function(value,&amp;nbsp;key,&amp;nbsp;list)&amp;nbsp;{&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;{&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;value:&amp;nbsp;value,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;index:&amp;nbsp;index++,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;criteria:&amp;nbsp;iteratee(value,&amp;nbsp;key,&amp;nbsp;list)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}).sort(function(left,&amp;nbsp;right)&amp;nbsp;{&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;a&amp;nbsp;=&amp;nbsp;left.criteria;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;b&amp;nbsp;=&amp;nbsp;right.criteria;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(a&amp;nbsp;!==&amp;nbsp;b)&amp;nbsp;{&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(&lt;span style=&quot;color: #b00800;&quot;&gt;a&amp;nbsp;&amp;gt;&amp;nbsp;b&lt;/span&gt;&amp;nbsp;||&amp;nbsp;a&amp;nbsp;===&amp;nbsp;void&amp;nbsp;0)&amp;nbsp;return&amp;nbsp;1;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(&lt;span style=&quot;color: #b00800;&quot;&gt;a&amp;nbsp;&amp;lt;&amp;nbsp;b&lt;/span&gt;&amp;nbsp;||&amp;nbsp;b&amp;nbsp;===&amp;nbsp;void&amp;nbsp;0)&amp;nbsp;return&amp;nbsp;-1;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;left.index&amp;nbsp;-&amp;nbsp;right.index;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}),&amp;nbsp;'value');&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;};&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리 코드는 _.sortBy()를 쓰는 것으로 처리했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/Javascript</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/790</guid>
      <comments>https://ohgyun.com/790#entry790comment</comments>
      <pubDate>Fri, 31 May 2019 16:52:20 +0900</pubDate>
    </item>
    <item>
      <title>MySQL: 테이블 이름 변경에 view 활용하기</title>
      <link>https://ohgyun.com/789</link>
      <description>&lt;p&gt;&lt;b&gt;발생일:&lt;/b&gt; 2019.04.22&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;키워드:&lt;/b&gt; mysql, view, 뷰&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기존 변수를 모두 변경하면서 관련된 테이블의 이름도 함께 변경하려고 한다.&lt;/p&gt;
&lt;p&gt;테이블 앨리어스가 있다면 가장 쉽게 운영 중인 서비스에도 문제 없이 적용할 수 있을 것 같다.&lt;/p&gt;
&lt;p&gt;가만, view 가 업데이트도 가능했던가?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;단일 테이블로 생성한 뷰는 업데이트나 인서트도 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - view 조건에 해당되지 않는 경우에도 업데이트나 인서트가 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; CREATE VIEW emp_view AS&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SELECT * FROM emp WHERE age &amp;gt; 10&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; INSERT INTO emp_view (iage) VALUES (5);&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 뷰 생성 시 컬럼 앨리어스를 사용한 경우에도 인서트나 업데이트 된다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; CREATE VIEW emp_view AS&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SELECT id, age AS age2 FROM emp&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; - 1:1 매핑이 되지 않는 경우엔 업데이트 가능하지 않다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; CREATE VIEW emp_view AS&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SELECT id, age, age AS age2 FROM emp&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; CREATE VIEW emp_view AS&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SELECT id, age, 10 AS age2 FROM emp&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참고로,&lt;/p&gt;
&lt;p&gt;- 뷰의 정의는 생성 시점에 정해진다. 뷰가 생성된 이후에 테이블의 컬럼이 추가된 경우, 뷰에 적용되진 않는다.&lt;/p&gt;
&lt;p&gt;- 뷰를 생성한 후에 테이블을 삭제해도, 뷰는 삭제되지 않는다. 참조만 없음&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;# 운영 중인 foo 테이블의 이름을 bar 로 변경&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;스킴을 변경해도 운영 중인 테이블이나 변경 타이밍이 다른 코드 등에서 동일하게 사용할 수 있어야 하니,&lt;/p&gt;
&lt;p&gt;기존 테이블의 이름을 변경하고 바로 기존 이름의 뷰를 생성해두는 게 안전하다.&lt;/p&gt;
&lt;p&gt;(rename 하는 동안, 테이블을 찾지 못해 오류가 발생할 수 있음)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;RENAME TABLE foo TO bar;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;CREATE VIEW foo AS SELECT * FROM bar;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;# 변경할 테이블의 컬럼명을 바꾸려고 하는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;foo_id 컬럼이 있는 foo_sub 테이블을,&lt;/p&gt;
&lt;p&gt;bar_id 컬럼을 갖는 bar_sub 테이블로 변경하려는 경우임.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;RENAME TABLE foo_sub TO bar_sub;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;ALTER TABLE bar_sub CHANGE foo_id bar_id INT(10) unsigned;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;CREATE VIEW foo AS&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SELECT id, bar_id AS foo_id, ...&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FROM bar;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Daylogs/DB</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/789</guid>
      <comments>https://ohgyun.com/789#entry789comment</comments>
      <pubDate>Mon, 20 May 2019 17:33:27 +0900</pubDate>
    </item>
    <item>
      <title>LetsEncrypt 로 개발용 인증서 만들기</title>
      <link>https://ohgyun.com/788</link>
      <description>&lt;p&gt;&lt;b&gt;&lt;span&gt;발생일:&lt;/span&gt;&lt;/b&gt; 2019.05.13&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;키워드:&lt;/span&gt;&lt;/b&gt; 인증서, https, ssl, letsencrypt, cordova, phonegap, 코르도바, 폰갭, certbot, 자가 서명 인증서, self-signed certificate&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;문제:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;로컬 환경에서 https 를 구성하기 위해 자가 서명 인증서를 만들어 적용했다. (자가 서명 인증서는&amp;nbsp;&lt;a href=&quot;https://ohgyun.com/429&quot;&gt;https://ohgyun.com/429&lt;/a&gt;&amp;nbsp;참고)&lt;/p&gt;
&lt;p&gt;브라우저에서는 잘 테스트했는데, 시뮬레이터에서는 동작하지 않는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;왜 그럴까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;해결책:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리 서비스는 코르도바 기반의 하이브리드 앱으로 구현되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cordova.apache.org/docs/ko/latest/guide/appdev/security/&quot;&gt;코르도바 보안 가이드&lt;/a&gt;에 따르면, 자체 서명된 인증서는 보안에 취약하기 때문에 기본적으로는 허용하지 않는다고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;디버깅 옵션을 주면 된다고 하는데, 실제 환경과 비슷하게 테스트할 수 있게 신뢰할 수 있는 인증 기간의 인증서를 사용하는 쪽으로 해결했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://letsencrypt.org/&quot;&gt;LetsEncrypt&lt;/a&gt; 에서 무료로 인증서를 제공하며, OS에 따라 certbot 모듈을 이용해 생성하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;certbot으로 각 서버에 자동으로 인증서 설정을 할 수 있지만, 우린 기존 환경을 미리 구성해둬서 인증서만 생성하면 되는 상황이었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 명령으로 수동으로 인증서를 생성했다. -d 옵션에 할당한&amp;nbsp;dev.example.com을&amp;nbsp;본인의 도메인에 맞게 넣어주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; $ sudo certbot -d dev.example.com&amp;nbsp;--server https://acme-v02.api.letsencrypt.org/directory --manual --preferred-challenges dns certonly&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 명령어를 실행하면, 아래와 같이 DNS TXT를 설정하라고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; Please deploy a DNS TXT record under the name&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; _acme-challenge.dev.example.com with the following value:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; m61ycxBWXXKIt8ws2LJEelAYSt5wITUCyHxzHw9Slys&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해당 도메인의 소유 여부를 확인하려는 목적이다.&lt;/p&gt;
&lt;p&gt;도메인 제공 서비스에서&amp;nbsp;_acme-challenge.dev.example.com&amp;nbsp;의 레코드 셋을 만들고, 타입을 TXT로 Value 에 출력된 값을 넣어주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리는 AWS의 Route53을 사용하고 있어서, 아래와 같이 추가해줬다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sxD5V/btqvsxOGcUc/1BwAfT59JnCAyTnbpj4szk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sxD5V/btqvsxOGcUc/1BwAfT59JnCAyTnbpj4szk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sxD5V/btqvsxOGcUc/1BwAfT59JnCAyTnbpj4szk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsxD5V%2FbtqvsxOGcUc%2F1BwAfT59JnCAyTnbpj4szk%2Fimg.png&quot; width=&quot;341&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음 단계로 진행하면, OS 환경에 따라 특정 경로에 인증서가 생성된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;인증서는 3개월마다 갱신해줘야 한다.&lt;/p&gt;
&lt;p&gt;`sudo certbot renew` 커맨드로 실행하면 되고, 젠킨스에 배치로 걸어둘 예정이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;논의:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;# API 호출 제한&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;테스트로 계속 호출했더니, API 호출 제한에 걸렸다.&lt;/p&gt;
&lt;p&gt;테스트 목적으로는 --dry-run 플래그로 API 스테이징 서버를 호출하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; $ sudo certbot -d dev.example.com&amp;nbsp;--server https://acme-v02.api.letsencrypt.org/directory --manual --preferred-challenges dns certonly --dry-run&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;# AWS Linux에서의 설치&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;AWS Linux 에서 certbot이 yum 으로 설치되지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/SSL-on-an-instance.html#letsencrypt&quot;&gt;가이드&lt;/a&gt;가 있어서 따라해봤는데, 여전히 안된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;certbot-auto 파일을 별도로 받아 설정하는 방식으로 해결했다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://certbot.eff.org/lets-encrypt/pip-other&quot;&gt;https://certbot.eff.org/lets-encrypt/pip-other&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; $ sudo /usr/local/bin/certbot-auto -d dev.example.com&amp;nbsp;--server https://acme-v02.api.letsencrypt.org/directory --manual --preferred-challenges dns certonly&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;# 관련 모듈을 찾을 수 없다는 오류&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래와 같이 관련 파이썬 모듈을 찾을 수 없다고 나오는 경우:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; [ec2-user@admin&amp;nbsp;~]$&amp;nbsp;/usr/local/bin/certbot-auto&amp;nbsp;renew&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; Requesting&amp;nbsp;to&amp;nbsp;rerun&amp;nbsp;/usr/local/bin/certbot-auto&amp;nbsp;with&amp;nbsp;root&amp;nbsp;privileges...&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; Error:&amp;nbsp;couldn't&amp;nbsp;get&amp;nbsp;currently&amp;nbsp;installed&amp;nbsp;version&amp;nbsp;for&amp;nbsp;/opt/eff.org/certbot/venv/bin/letsencrypt:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Traceback&amp;nbsp;(most&amp;nbsp;recent&amp;nbsp;call&amp;nbsp;last):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;File&amp;nbsp;&quot;/opt/eff.org/certbot/venv/bin/letsencrypt&quot;,&amp;nbsp;line&amp;nbsp;7,&amp;nbsp;in&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;from&amp;nbsp;certbot.main&amp;nbsp;import&amp;nbsp;main&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;... 중략&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;File&amp;nbsp;&quot;/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/OpenSSL/__init__.py&quot;,&amp;nbsp;line&amp;nbsp;8,&amp;nbsp;in&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;from&amp;nbsp;OpenSSL&amp;nbsp;import&amp;nbsp;crypto,&amp;nbsp;SSL&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;... 중략&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;ImportError:&amp;nbsp;No&amp;nbsp;module&amp;nbsp;named&amp;nbsp;cryptography&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;sitecustomize.py 파일을 생성한 후에, 아래와 같이 패스를 추가해주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; $ sudo&amp;nbsp;vi&amp;nbsp;/opt/eff.org/certbot/venv/lib64/python2.7/site-packages/sitecustomize.py&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; import&amp;nbsp;site&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; &amp;nbsp; site.addsitedir('/opt/eff.org/certbot/venv/lib64/python2.7/dist-packages')&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;# TXT record 가 제대로 업데이트 되었는지 확인하고 싶을 땐, txt type으로 nslookup 하면 된다,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #0051a1;&quot;&gt;&amp;nbsp; $ nslookup &lt;span style=&quot;color: #f41a18;&quot;&gt;-type=txt&lt;/span&gt;&amp;nbsp;_acme-challenge.dev.example.com&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;참고:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Certbot:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://certbot.eff.org/&quot;&gt;https://certbot.eff.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Guide: Using Let's Encrypt SSL Certificates for a local or network server&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://r.je/guide-lets-encrypt-certificate-for-local-development#vjecerts&quot;&gt;https://r.je/ adguide-lets-encrypt-certificate-for-local-development#vjecerts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;LetsEncrypt Staging Environment&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://letsencrypt.org/docs/staging-environment/&quot;&gt;https://letsencrypt.org/docs/staging-environment/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Amazon&amp;nbsp;Linux&amp;nbsp;AMI&amp;nbsp;에서&amp;nbsp;letencrypt&amp;nbsp;certbot&amp;nbsp;으로&amp;nbsp;SSL&amp;nbsp;인증서&amp;nbsp;발급&amp;nbsp;에러&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.lesstif.com/pages/viewpage.action?pageId=54952117&quot;&gt;https://www.lesstif.com/pages/viewpage.action?pageId=54952117&lt;/a&gt;&lt;/p&gt;</description>
      <category>Daylogs/Etc</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/788</guid>
      <comments>https://ohgyun.com/788#entry788comment</comments>
      <pubDate>Mon, 20 May 2019 17:23:15 +0900</pubDate>
    </item>
    <item>
      <title>Error: pg_config executable not found</title>
      <link>https://ohgyun.com/787</link>
      <description>&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;발생일:&lt;/span&gt; 2019.04.05&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;키워드:&lt;/span&gt; psycopg2, pg_config&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;문제:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;얼마 전, 파이썬 배치 작업들이 모두 실패했다.&lt;/div&gt;
&lt;div&gt;오류 문구는 아래와 같다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #005493;&quot;&gt;Collecting&amp;nbsp;psycopg2&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;Using&amp;nbsp;cached&amp;nbsp;https://files.pythonhosted.org/packages/c7/ca/75236b17f1b951950ffc55d657c5aa408d3d0327a1b6c4c0f7cb16ef7e7b/psycopg2-2.8.tar.gz&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Complete&amp;nbsp;output&amp;nbsp;from&amp;nbsp;command&amp;nbsp;python&amp;nbsp;setup.py&amp;nbsp;egg_info:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;running&amp;nbsp;egg_info&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;creating&amp;nbsp;pip-egg-info/psycopg2.egg-info&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writing&amp;nbsp;pip-egg-info/psycopg2.egg-info/PKG-INFO&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writing&amp;nbsp;dependency_links&amp;nbsp;to&amp;nbsp;pip-egg-info/psycopg2.egg-info/dependency_links.txt&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writing&amp;nbsp;top-level&amp;nbsp;names&amp;nbsp;to&amp;nbsp;pip-egg-info/psycopg2.egg-info/top_level.txt&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writing&amp;nbsp;manifest&amp;nbsp;file&amp;nbsp;'pip-egg-info/psycopg2.egg-info/SOURCES.txt'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Error:&amp;nbsp;pg_config&amp;nbsp;executable&amp;nbsp;not&amp;nbsp;found.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pg_config&amp;nbsp;is&amp;nbsp;required&amp;nbsp;to&amp;nbsp;build&amp;nbsp;psycopg2&amp;nbsp;from&amp;nbsp;source.&amp;nbsp;&amp;nbsp;Please&amp;nbsp;add&amp;nbsp;the&amp;nbsp;directory&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;containing&amp;nbsp;pg_config&amp;nbsp;to&amp;nbsp;the&amp;nbsp;$PATH&amp;nbsp;or&amp;nbsp;specify&amp;nbsp;the&amp;nbsp;full&amp;nbsp;executable&amp;nbsp;path&amp;nbsp;with&amp;nbsp;the&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;option:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;python&amp;nbsp;setup.py&amp;nbsp;build_ext&amp;nbsp;--pg-config&amp;nbsp;/path/to/pg_config&amp;nbsp;build&amp;nbsp;...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;or&amp;nbsp;with&amp;nbsp;the&amp;nbsp;pg_config&amp;nbsp;option&amp;nbsp;in&amp;nbsp;'setup.cfg'.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;If&amp;nbsp;you&amp;nbsp;prefer&amp;nbsp;to&amp;nbsp;avoid&amp;nbsp;building&amp;nbsp;psycopg2&amp;nbsp;from&amp;nbsp;source,&amp;nbsp;please&amp;nbsp;install&amp;nbsp;the&amp;nbsp;PyPI&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'psycopg2-binary'&amp;nbsp;package&amp;nbsp;instead.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;For&amp;nbsp;further&amp;nbsp;information&amp;nbsp;please&amp;nbsp;check&amp;nbsp;the&amp;nbsp;'doc/src/install.rst'&amp;nbsp;file&amp;nbsp;(also&amp;nbsp;at&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;http://initd.org/psycopg/docs/install.html&amp;gt;).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;----------------------------------------&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #005493;&quot;&gt;Command&amp;nbsp;&quot;python&amp;nbsp;setup.py&amp;nbsp;egg_info&quot;&amp;nbsp;failed&amp;nbsp;with&amp;nbsp;error&amp;nbsp;code&amp;nbsp;1&amp;nbsp;in&amp;nbsp;/private/var/folders/7m/gd92t9wx0nl_t8yw70ymhl0c0000gn/T/pip-install-t4927zna/psycopg2/&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;왜 그럴까?&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;해결책:&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;psycopg2 관련 모듈이 업데이트 되면서 postgresql 바이너리를 찾지 못하는 문제인 것 같다.&lt;/div&gt;
&lt;div&gt;postgresql 관련 모듈을 다시 설치하는 것으로 해결했다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;맥에서는,&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #005493;&quot;&gt;$ brew install postgresql&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;CentOS에서는,&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #005493;&quot;&gt;$&amp;nbsp;sudo yum install postgresql postgresql-devel python-devel&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;참고:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://stackoverflow.com/questions/11618898/pg-config-executable-not-found&quot;&gt;https://stackoverflow.com/questions/11618898/pg-config-executable-not-found&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Daylogs/Python</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/787</guid>
      <comments>https://ohgyun.com/787#entry787comment</comments>
      <pubDate>Sat, 13 Apr 2019 21:55:35 +0900</pubDate>
    </item>
    <item>
      <title>nginx: 허용하지 않은 도메인에서 접속이 되는 경우</title>
      <link>https://ohgyun.com/786</link>
      <description>&lt;div&gt;&lt;b&gt;발생일:&lt;/b&gt; 2017.12.06&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;키워드:&lt;/b&gt; nginx, default_server, 기본 server&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;엥?? 도메인이 다른 전혀 관련 없는 웹페이지가 우리 서비스를 프록시로 걸어 제공하고 있었다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;어처구니가 없는 행동이지만, 그 전에 왜 nginx는 도메인이 다른 접근에 응답하고 있었던 걸까?&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;nginx 설정의 서버 블럭에는 아래와 같이 server_name 이 명시되어 있었다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;server {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; server_name ohgyun.com;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; ...&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;nginx의 &quot;첫 번째&quot; 서버 블럭은 server_name 이 매칭되지 않았을 때 동작하는 기본 서버 블럭이다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;서버 블럭을 하나만 정의했었기 때문에, 도메인이 매칭되지 않았을 때에도 동일하게 첫 번째 블럭이 실행된 것이 문제였다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;아래와 같이 기본 서버 블럭을 정의하고, 404를 리턴하게 한 것으로 해결했다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;# Default server&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;server {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 404;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;server {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;server_name ohgyun.com;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; ...&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;응답으로 &lt;a href=&quot;https://httpstatuses.com/444&quot;&gt;444&lt;/a&gt;를 리턴하면, 404보다는 네트워크 데이터를 좀 더 절약할 수 있다고 한다.&lt;/div&gt;
&lt;div&gt;우린 AWS ELB를 쓰고 있었는데, 444로 리턴했더니 502 Bad Gateway로 응답한다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;확인해보니, 444는 nginx 확장 코드라 아직 ELB에서 지원하지 않는다고 한다.&lt;/div&gt;
&lt;div&gt;404로 리턴하도록 했다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://stackoverflow.com/a/43128186/954292%0A&quot;&gt;https://stackoverflow.com/a/43128186/954292&lt;/a&gt;&lt;/div&gt;</description>
      <category>Daylogs/Nginx</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/786</guid>
      <comments>https://ohgyun.com/786#entry786comment</comments>
      <pubDate>Sat, 13 Apr 2019 21:45:35 +0900</pubDate>
    </item>
    <item>
      <title>nginx: 로그 때문에 서버 용량이 찼을 때 처리 방법</title>
      <link>https://ohgyun.com/785</link>
      <description>&lt;div&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;발생일:&lt;/span&gt; 2017.07.31&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;키워드:&lt;/span&gt; nginx, 용량, 로그 용량, copytruncate, logrotate, 로그 로테이트&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;문제:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;서버 용량이 가득 차서 리스타트에 실패하는 문제가 발생했다.&lt;/div&gt;
&lt;div&gt;nginx 로그 때문으로 추측되는데, 실제로 로그 디렉토리 내의 로그 파일의 용량은 문제가 될 만큼 크지 않다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;뭐가 문제일까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;해결책:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;nginx 로그는 로테이트해 S3로 업로드한 후에 삭제한다.&lt;/div&gt;
&lt;div&gt;확인해보니, 원인은 로그가 제대로 로테이트되지 않고 프로세스가 삭제된 기존 파일을 계속 물고 있었기 때문이다.&lt;/div&gt;
&lt;div&gt;로그 로테이트 옵션에 copytruncate 을 추가하는 것으로 해결했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래는 작업과 관련된 스크립트:&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# 현재 머신의 용량을 확인해본다&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;df -h&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# 어떤 디렉토리에서 가장 많이 차지하는지 확인한다&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;sudo du -hs * | sort -hr | head -n 10&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# nginx 프로세스가 삭제된 파일을 물고 있는지 확인한다 (이게 원인이었음)&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;lsof / | grep deleted&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# nginx 리로드해서 삭제된 물고 있는 파일을 정리한다&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;sudo /etc/init.d/nginx reload&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# nginx 로그로테이트 설정 파일 보기&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;sudo cat /etc/logrotate.d/nginx&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# nginx 로그로테이트 설정 파일에 copytruncate을 추가했다&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;sudo vi /etc/logrotate.d/nginx&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# 디버깅 모드로 로그로테이트 실행하기&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;sudo logrotate -d /etc/logrotate.d/nginx&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# 강제로 로그로테이트 실행하기&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;sudo logrotate -f /etc/logrotate.d/nginx&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;du + sort 로 읽기 편한 사이즈로 표시하면서 정렬하는 방법&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://serverfault.com/questions/62411/how-can-i-sort-du-h-output-by-size&quot;&gt;https://serverfault.com/questions/62411/how-can-i-sort-du-h-output-by-size&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;로그 용량이 기가 단위로 큰 경우엔 copytruncate 동작 중 I/O 블로킹으로 인한 타임아웃이 발생할 수 있다고 한다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://brunch.co.kr/@alden/27&quot;&gt;https://brunch.co.kr/@alden/27&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/Nginx</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/785</guid>
      <comments>https://ohgyun.com/785#entry785comment</comments>
      <pubDate>Tue, 26 Mar 2019 11:48:52 +0900</pubDate>
    </item>
    <item>
      <title>clasp: 구글 앱스 스크립트 로컬에서 개발하기</title>
      <link>https://ohgyun.com/784</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2019.03.02&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; Google Apps Script, 구글 앱스 스크립트, clasp&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;구글 앱스 스크립트를 로컬에서 개발하고 싶다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;기존엔 파일이 크지 않아 온라인에서 수정하거나, 수정 사항이 좀 있을 땐 에디터에 복사해서 수정하고 붙여넣곤 했다.&lt;/div&gt;
&lt;div&gt;규모가 조금 커지다보니 더 이상 귀찮아서 못하겠다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;뭔가 도구가 있을 테다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;유후. 로컬에서 개발하고 배포할 수 있는 clasp 라는 도구가 있다. 진작에 찾아볼 걸.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://github.com/google/clasp&quot;&gt;https://github.com/google/clasp&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;npm 으로 바로 설치할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; $&amp;nbsp;sudo npm i @google/clasp -g&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;구글 계정에서 Apps API 권한을 허용해준 다음,&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://script.google.com/home/usersettings&quot;&gt;https://script.google.com/home/usersettings&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;터미널에서 바로 로그인하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ clasp login&lt;/font&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;난 이미 프로젝트가 있어서, 로컬에 디렉토리를 만들어 클론했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ clasp clone [스크립트 ID]&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;한 번 클론한 이후에 원격의 수정 사항을 가져오려면 pull만 하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ clasp pull&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;로컬에서 파일을 수정한 후에, push 하면 된다.&lt;/div&gt;
&lt;div&gt;push 후 온라인 스크립트 에디터에서 확인해보려면 새로고침 한 번 해야한다.ㅎㅎ&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ clasp push&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;수정 사항으로 새 버전을 따거나 바로 배포할 수도 있다.&lt;/div&gt;
&lt;div&gt;간단하게 deploy만 하면, 신규 버전이 따지면서 배포도 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ clasp deploy&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;와. 정말 편하다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;온라인 스크립트 에디터를 바로 열 수 있기도 하고!&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ clasp open&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;버전 관리는 별도로 git 으로 하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;clone 한 스크립트 아이디는&lt;font color=&quot;#005493&quot;&gt; .clasp.json&lt;/font&gt; 파일에 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;로컬 파일이 변경될 때마다 자동으로 push 하고 싶다면, -w (--watch) 옵션을 쓰면 된다. 편하다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ clasp -w&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;로컬에서 함수를 실행할 땐, run 명령을 쓰면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ clasp run [함수명]&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;git 과 비슷하게 status 명령이 있지만 의미는 다르다.&lt;/div&gt;
&lt;div&gt;이 명령은 서버에 푸시할 때 포함되는 파일의 목록을 출력해준다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ clasp status&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;deploy 한다고 변경 내용이 서버에 push 되는 건 아니니 주의한다.&lt;/div&gt;
&lt;div&gt;수정 전에 push -w 을 걸어두는 게 편하겠다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;또한, deploy 한다고 웹앱이 신규로 퍼블리싱 되는 것도 아니다.&lt;/div&gt;
&lt;div&gt;기대한 대로라면 잘 되어야 할 것 같고, &lt;a href=&quot;https://github.com/google/clasp/issues/63&quot;&gt;깃헙 이슈에서 논의도 되었지만&lt;/a&gt; 아직 해결되지 않은 듯 싶다.&lt;/div&gt;
&lt;div&gt;좀 번거롭긴 한데, deploy 후에 퍼블리싱 과정을 챙겨줘야겠다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/Etc</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/784</guid>
      <comments>https://ohgyun.com/784#entry784comment</comments>
      <pubDate>Sat, 2 Mar 2019 03:53:48 +0900</pubDate>
    </item>
    <item>
      <title>npm 설치 오류: code EBADKEY</title>
      <link>https://ohgyun.com/783</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2019.03.01&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; node, npm&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;code EBADKEY&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;invalid config key requested: config&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;npm install 할 때, 위 문구가 나오면서 설치되지 않는다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;검색해서 몇 가지 방법을 시도해봤는데, 잘 되지 않았다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;노드 관련 파일을 모두 제거 후에 다시 설치했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- 루트 node_modules 제거&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; npm 루트 경로는 아래 명령으로 찾으면 된다&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ npm root -g &amp;nbsp;# npm&amp;nbsp;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- node 제거&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; 노드 파일은 아래 명령으로 찾으면 된다&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ type node&amp;nbsp;&lt;/font&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- n 으로 노드 신규 설치&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; $&amp;nbsp;curl -L &lt;a href=&quot;https://git.io/n-install&quot;&gt;https://git.io/n-install&lt;/a&gt; | bash&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;오랜만에 깨끗해졌다~ ㅎㅎ&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/Javascript</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/783</guid>
      <comments>https://ohgyun.com/783#entry783comment</comments>
      <pubDate>Sat, 2 Mar 2019 03:53:16 +0900</pubDate>
    </item>
    <item>
      <title>Node: String 크기 제한에 의한 RangeError</title>
      <link>https://ohgyun.com/782</link>
      <description>&lt;en-note style=&quot;zoom: 1;&quot;&gt;&lt;div&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;발생일:&lt;/span&gt; 2018.10.22&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;키워드:&lt;/span&gt; JSON.stringify, RangeError, Invalid string length,&amp;nbsp;max-old-space-size, 메모리 부족, RangeError&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;문제:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;배치 작업 과정 중에 아래와 같은 오류가 발생하면서 프로세스가 종료됐다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;RangeError: Invalid string length&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;at JSON.stringify (&amp;lt;anonymous&amp;gt;)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;메모리 이슈인 것 같아 노드의 max-old-space-size 파라미터로 힙 사이즈를 필요한 만큼 지정해 실행했는데도 동일하게 발생한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;왜 그런 걸까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;해결책:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;확인해보니, V8은 구조적 문제로 객체의 크기를 1.9기가로 제한하고 있다고 한다.&lt;/div&gt;
&lt;div&gt;사이즈가 큰 객체를 stringify 하는 과정에서 문자열의 크기가 지나치게 커진 것이 문제였다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래 코드를 실행하면, 위 오류를 재현할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let str = 'a';&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (let i = 0; i &amp;lt; 30; i++) {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;str += str;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;위 예제에서는 단순히 문자열을 더한 것이지만,&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; JSON.stringify(bigObject);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;와 같이 사이즈가 큰 객체를 변경 시 JSON Syntax를 포함한 변환된 문자열의 크기가 허용치를 넘어서면서 발생할 수도 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;해결하려면,&lt;/div&gt;
&lt;div&gt;- 객체를 작은 사이즈로 나누거나&lt;/div&gt;
&lt;div&gt;- 스트림으로 직렬화하는 방법으로 우회할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;# JSON을 스트림으로 처리&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;JSON을 스트림으로 처리하는 용도로 JSONStream 모듈이 있다.&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://www.npmjs.com/package/JSONStream&quot;&gt;https://www.npmjs.com/package/JSONStream&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래와 같은 식으로 사용하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const parser = JSONStream.stringifyObject();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parser.pipe(fs.createWriteStream('output'));&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; parser.write(['key1', 'value1']);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parser.write(['key2', 'value2']);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parser.end();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;스트림의 종료 이벤트는 좀 헷갈리는데,&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Readable 은 end 이벤트를, Writable 은 finish 이벤트를 사용하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래는 읽고 쓰는 샘플 예제이다.&lt;/div&gt;
&lt;div&gt;(유틸리티 모듈로&amp;nbsp;undescore 를, 프라미스 모듈로 Q를 썼다.)&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const fs = require('fs');&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const JSONStream = require('JSONStream');&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const dumpJson = function (obj, outputFile) {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const deferred = Q.defer();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const parser = JSONStream.stringifyObject();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const output = fs.createWriteStream(outputFile);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parser.pipe(output);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_.each(obj, (value, key) =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parser.write([key, value]);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parser.end();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;output.on('finish', () =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;deferred.resolve(output);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return deferred.promise;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const restoreJson = function (inputFile) {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const deferred = Q.defer();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const input = fs.createReadStream(inputFile);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const parser = JSONStream.parse('$*');&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const obj = {};&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;input.pipe(parser);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parser.on('data', (data) =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;obj[data.key] = data.value;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parser.on('end', () =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;deferred.resolve(obj);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return deferred.promise;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const obj = {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foo: '1',&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bar: '2',&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;baz: [1, 2, 3]&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return dumpJson(obj, 'output.json').then(() =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return restoreJson('output.json').then((restored) =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log(_.isEqual(obj, restored));&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;위 문제에서처럼 사이즈가 큰 객체를 메모리를 늘리지 않고 처리하거나,&lt;/div&gt;
&lt;div&gt;event loop을 방해하지 않으면서 처리하고 싶을 때 사용하면 좋다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;다만, 기본 파싱보다 느린 것이 단점이다. (대략 10배 정도 느림)&lt;/div&gt;
&lt;div&gt;우린 서버에서도 사용하고 있는데, 큰 사이즈의 데이터를 캐시에 넣고 뺄 때 다른 요청에 방해되지 않게 하는 용도로 사용하고 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;JSON은 사이즈가 커질수록 파싱하는데 시간이 크게 늘어나니, 이런 종류의 데이터를 만들지 않는 것이 가장 좋은 방법일 것 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;# 메모리가 부족한 경우&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;이 외에 단순히 메모리가 부족한 경우엔 아래와 같은 오류가 출력된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;Allocation failed — process out of memory&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;또는,&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;FATAL ERROR: JS Allocation failed - process out of memory&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;또는,&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;--- Last few GCs ---&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[54774:0x103000000]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;95 ms: Mark-sweep 6.2 (15.7) -&amp;gt; 5.6 (15.7) MB, 4.9 / 0.0 ms&amp;nbsp;&amp;nbsp;(average mu = 0.205, current mu = 0.152) allocation failure GC in old space requested&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[54774:0x103000000]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;100 ms: Mark-sweep 5.6 (15.7) -&amp;gt; 5.6 (16.2) MB, 4.8 / 0.0 ms&amp;nbsp;&amp;nbsp;(average mu = 0.110, current mu = 0.004) allocation failure GC in old space requested&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;--- JS stacktrace ---&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;==== JS stack trace =========================================&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0: ExitFrame [pc: 0x16c79d5dc01d]&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Security context: 0x1bc5a131e681 &amp;lt;JSObject&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이런 경우엔 노드 스크립트를 실행할 때, V8의 옵션인 max-old-space-size 파라미터로 힙 사이즈를 필요한만큼 지정해주면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; $ node&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;--max-old-space-size=10240&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt; task.js&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;단위는 MB이다. 즉, 위 코드의 10240은 10GB를 의미한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://nodejs.org/api/v8.html#v8_v8_getheapspacestatistics&quot;&gt;https://nodejs.org/api/v8.html#v8_v8_getheapspacestatistics&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;# 메모리가 부족한 경우 (Grunt)&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;우린 태스크 러너로 그런트를 사용하고 있는데, 이럴 땐 노드 옵션을 주고 그런트를 이어서 실행하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;$ &lt;/span&gt;&lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;node --max-old-space-size=10240&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt; /usr/bin/local/grunt task.js&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;# 힙 사이즈 확인&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;힙 사이즈를 확인하려면 v8 모듈을 사용하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;const v8 = require('v8');&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;v8.getHeapStatistics();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;[&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; ...,&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;{&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;space_name&quot;: &quot;old_space&quot;,&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;space_size&quot;: 3090560,&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;space_used_size&quot;: 2493792,&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;space_available_size&quot;: 0,&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;physical_space_size&quot;: 3090560&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;},&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; ...&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;]&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;참고:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;- 스택오버플로우:&amp;nbsp;&lt;a href=&quot;https://stackoverflow.com/questions/24153996/is-there-a-limit-on-the-size-of-a-string-in-json-with-node-js&quot;&gt;https://stackoverflow.com/questions/24153996/is-there-a-limit-on-the-size-of-a-string-in-json-with-node-js&lt;/a&gt;
&lt;/div&gt;
&lt;div&gt;- 크로미엄 이슈:&amp;nbsp;&lt;a href=&quot;https://bugs.chromium.org/p/v8/issues/detail?id=847&quot;&gt;https://bugs.chromium.org/p/v8/issues/detail?id=847&lt;/a&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;V8의 헤더 파일&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://v8docs.nodesource.com/node-10.6/d8/dcd/classv8_1_1_resource_constraints.html#a62577457df1cfedc707856d865529b53&quot;&gt;https://v8docs.nodesource.com/node-10.6/d8/dcd/classv8_1_1_resource_constraints.html#a62577457df1cfedc707856d865529b53&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;쉬운 예:&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://medium.com/@vuongtran/how-to-solve-process-out-of-memory-in-node-js-5f0de8f8464c&quot;&gt;https://medium.com/@vuongtran/how-to-solve-process-out-of-memory-in-node-js-5f0de8f8464c&lt;/a&gt;&lt;/div&gt;&lt;/en-note&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/Javascript</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/782</guid>
      <comments>https://ohgyun.com/782#entry782comment</comments>
      <pubDate>Tue, 30 Oct 2018 14:44:10 +0900</pubDate>
    </item>
    <item>
      <title>Python: 정규식으로 문자열을 자를 때, 그룹이 포함되어 있는 경우</title>
      <link>https://ohgyun.com/781</link>
      <description>&lt;div&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;발생일:&lt;/span&gt; 2017.08.18&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;키워드:&lt;/span&gt; re, 정규식, lookbehind, 그룹, group, regular expression, regex, split&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;문제:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;문자열을 정규식으로 split 할 때, 정규식 구문에 그룹이 포함되어 있었더니 원하는 결과가 나오지 않는다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;왜 그런 걸까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;split()으로 전달하는 패턴에 그룹이 있으면, 분할한 결과 뒤에 그룹 매칭값이 붙는다.&lt;/div&gt;
&lt;div&gt;아래 예제를 보면 쉽게 이해할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; re.split('-', 'aaa-bbb')&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; -&amp;gt; ['aaa', 'bbb']&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; re.split('&lt;/span&gt;&lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;(-)&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;', 'aaa-bbb') &amp;nbsp;&lt;/span&gt;&lt;font color=&quot;#008f00&quot;&gt;# 그룹으로 묶음&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; -&amp;gt; ['aaa', &lt;/span&gt;&lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;'-'&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;, 'bbb'] &amp;nbsp;&lt;/span&gt;&lt;font color=&quot;#008f00&quot;&gt;# split 결과에 그룹이 포함된다&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; re.split('&lt;/span&gt;&lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;(-)&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;', 'aaa-bbb-ccc') &lt;/span&gt;&lt;font color=&quot;#008f00&quot;&gt;&amp;nbsp;# 여러 번 잘라질 때도 동일&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; -&amp;gt; ['aaa', &lt;/span&gt;&lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;'-'&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;, 'bbb', &lt;/span&gt;&lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;'-'&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;, 'ccc']&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; re.split('&lt;/span&gt;&lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;(-)(x)&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;', 'aaa-xbbb-xccc') &lt;/span&gt;&lt;span style=&quot;color: rgb(0, 143, 0);&quot;&gt;&amp;nbsp;# 그룹이 여러 개인 경우&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; -&amp;gt; ['aaa', &lt;/span&gt;&lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;'-', 'x',&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;'bbb', &lt;/span&gt;&lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;'-', 'x'&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;, 'ccc']&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;그룹에 포함되지 않게 하려면 ?: 를 넣어주면 된다.&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; re.split('(&lt;/span&gt;&lt;font color=&quot;#941751&quot;&gt;?:&lt;/font&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;-)', 'aaa-bbb')&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;# 자바스크립트에서도 동일&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;자바스크립트에서도 동일하다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; 'aaa-bbb'.split(&lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;/-/&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt;); //-&amp;gt; ['aaa', 'bbb']&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; 'aaa-bbb'.split(&lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;/(-)/&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt;); //-&amp;gt; ['aaa', &lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;'-'&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt;, 'bbb']&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;그룹이 포함된 정규식 구문으로 split 하는 경우에 원하는 결과가 나오지 않는다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;# 글을 문장 단위로 자르기&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;사실 이 문제는 글을 문장 단위로 자르는 코드를 작성하면서 발생했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;예를 들어,&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &quot;오늘 아마존의 주가는 4.2% 증가했다. 구글은 3.4% 증가했다.&quot;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;와 같은 문장을 첫 문장과 두 번째 문장으로 구분하려고 했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래와 같이 단순히 마침표로만 자를 수도 있는데,&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; re.split('\.', text)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이렇게 하면, 자르는 기준에 4.2%, 3.4%와 같은 소수점도 포함되기 때문에 &lt;a href=&quot;https://medium.com/@iamreadytocommit/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%EC%A0%84%EB%B0%A9%ED%83%90%EC%83%89-%ED%9B%84%EB%B0%A9%ED%83%90%EC%83%89-8c62588c8b8f&quot;&gt;lookbehind&lt;/a&gt; 옵션으로 마치표 앞의 문자는 숫자가 아닌 것만으로 제한했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; re.split('(?&amp;lt;=[^0-9])\.', text)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이때까지 잘 동작했는데, 개행 문자를 정규식 구문에 추가하기 위해 그룹으로 묶었다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; re.split('&lt;/span&gt;&lt;font color=&quot;#941751&quot;&gt;(&lt;/font&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;(?&amp;lt;=[^0-9])&lt;/span&gt;&lt;font color=&quot;#941751&quot;&gt;\.|\n)&lt;/font&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;', text)&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이랬더니, 결과에 마침표가 포함된 거였다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래처럼 그룹에 포함되지 않게 변경하는 것으로 수정했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; re.split('(&lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;?:&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt;(?&amp;lt;=[^0-9])\.|\n)', text)&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;그치만 정리하다 보니, 그룹 없이 그냥 캐릭터를 쓰는 게 훨씬 간결하겠네...&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; re.split('&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;(?&amp;lt;=[^0-9])&lt;/span&gt;&lt;font color=&quot;#941751&quot;&gt;[\.|\n]&lt;/font&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;', text)&lt;/span&gt;
&lt;/div&gt;</description>
      <category>Daylogs/Python</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/781</guid>
      <comments>https://ohgyun.com/781#entry781comment</comments>
      <pubDate>Tue, 30 Oct 2018 14:20:24 +0900</pubDate>
    </item>
    <item>
      <title>CLI로 젠킨스 설정 업데이트하기</title>
      <link>https://ohgyun.com/780</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.10.29&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; jenkins, 젠킨스, manage jenkins, reload configuration in command line, 설정 업데이트&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;젠킨스의 설정 파일인 config.xml 을 직접 변경하고 나면,&lt;/div&gt;
&lt;div&gt;Manager Jenkin (Jenkins 관리) 메뉴에 들어가서 Reload Configuration from Disk &amp;nbsp;버튼을 눌러야 실행 중인 젠킨스에 반영된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;CLI로 바로 업데이트 할 수 없을까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;터미널에서 아래와 같이 호출하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $&amp;nbsp;java -jar jenkins-cli.jar -s [JENKINS_URL]&amp;nbsp;-auth [ID:PW] reload-configuration&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;난 젠킨스가 실행되고 있는 환경에서 바로 실행해서 아래와 같이 localhost 로 호출했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ java -jar jenkins-cli.jar &lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;-s http://localhost:8080 -auth ${id}:${password}&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt; reload-configuration&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;jenkins-cli.jar 파일은 젠킨스 URL에서 다음 주소로 다운로드 받을 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;/jnlpJars/jenkins-cli.jar&amp;nbsp;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;젠킨스가 실행되어 있는 서버의 터미널이라면, 아래처럼 호출하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ curl -O http://localhost:8080//jnlpJars/jenkins-cli.jar &lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;# REST API에는 없는 듯&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;우린 &lt;a href=&quot;https://github.com/silas/node-jenkins&quot;&gt;node-jenkins&lt;/a&gt; 클라이언트 모듈을 사용하고 있었는데, 이 라이브러리는 REST API를 호출하는 것 같더라.&lt;/div&gt;
&lt;div&gt;내가 못 찾은 걸 수도 있는데, REST API에 설정 업데이트는 지원하지 않는 것 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;잡(job)이나 빌드 설정은 클라이언트 라이브러리를 활용하는 것이 편하다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;# 권한 필요&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;설정 업데이트는 젠킨스의 어드민 권한이 있어야 가능하다.&lt;/div&gt;
&lt;div&gt;혹시 권한 문제로 실행되지 않는다면, &lt;font color=&quot;#005493&quot;&gt;Jenkins 관리 &amp;gt; Manage and Assign Role &amp;gt; Assign Roles&lt;/font&gt; 에서 설정해주면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://jenkins.io/doc/book/managing/cli/#downloading-the-client&quot;&gt;https://jenkins.io/doc/book/managing/cli/#downloading-the-client&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://stackoverflow.com/questions/34791525/how-to-reload-the-configuration-jenkins-from-the-command-line&quot;&gt;https://stackoverflow.com/questions/34791525/how-to-reload-the-configuration-jenkins-from-the-command-line&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/Etc</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/780</guid>
      <comments>https://ohgyun.com/780#entry780comment</comments>
      <pubDate>Mon, 29 Oct 2018 21:18:20 +0900</pubDate>
    </item>
    <item>
      <title>AWS Security Group 중첩 이슈 및 정리 팁</title>
      <link>https://ohgyun.com/779</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일: &lt;/b&gt;2018.04.08&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; AWS Security Group, 보안 그룹, 시큐리티 그룹, nested security group&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;AWS 보안 그룹에 다른 보안 그룹을 추가했는데, 하위 보안 그룹의 룰셋이 상위 보안 그룹에 추가되는 것 같지 않다.&lt;/div&gt;
&lt;div&gt;왜 그런 걸까?&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;보안 그룹 내에 다른 보안 그룹을 추가하는 경우,&lt;/div&gt;
&lt;div&gt;- 하위 보안 그룹의 룰셋이 상위 보안 그룹의 룰셋에 추가되는 것이 아니라,&lt;/div&gt;
&lt;div&gt;- &lt;font color=&quot;#941751&quot;&gt;하위 보안 그룹을 가진 &lt;b&gt;인스턴스들이&lt;/b&gt; 상위 보안 그룹에 액세스&lt;/font&gt;할 수 있게 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;오해하기 쉬워 보인다. @_@&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://stackoverflow.com/questions/42871841/can-you-configure-aws-security-groups-to-have-sub-groups-or-nested-groups&quot;&gt;https://stackoverflow.com/questions/42871841/can-you-configure-aws-security-groups-to-have-sub-groups-or-nested-groups&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonVPC/latest/UserGuide/VPC_SecurityGroups.html&quot;&gt;https://docs.aws.amazon.com/ko_kr/AmazonVPC/latest/UserGuide/VPC_SecurityGroups.html&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;AWS: 10 Things You're Probably Doing Wrong as ans Architect&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://labs.eleks.com/2013/11/aws-10-things-youre-probably-doing.html&quot;&gt;https://labs.eleks.com/2013/11/aws-10-things-youre-probably-doing.html&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;종종 보안 설정이 헷갈렸던 이유는, 보안 그룹을 룰셋으로 설정할 때도 있고, 대상 인스턴스의 IP 목록으로 설정하는 경우도 있었기 때문이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래처럼 정리하면 편리할 것 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- VPC를 대표하는 default 그룹을 생성. default 그룹은 모든 인스턴스가 갖도록 한다.&lt;/div&gt;
&lt;div&gt;- 각 인스턴스의 그룹은 그룹을 대표할 수 있는 이름(예: app, rds, redshift, redis, jenkins-slave 등)을 보안 그룹으로 갖게 한다.&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; - 즉, 이 보안 그룹은 룰셋인 동시에 해당 인스턴스의 목록이 된다.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span&gt;&amp;nbsp; &amp;nbsp; - 해당 보안 그룹의 Inbound 룰에 AWS 인스턴스로부터의 접근 권한을 지정한다.&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; - 예) 특정 인스턴스의 접근만 허용할 거라면, 해당 보안 그룹을 추가하는 방식으로 넣어준다.&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span&gt;&amp;nbsp; &amp;nbsp; - 예) 모든 인스턴스의 접근을 허용할 거라면 default 그룹을 추가한다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;- VPC 외부의 IP를 명시하는 보안 그룹은 별도로 만들고, (보안 그룹이 아니라) 인스턴스에 지정한다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; - 이 보안 그룹은 외부 IP에 대한 룰셋이므로 별도의 접두사를 둔다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 예) ext_office: 사무실 IP 룰 목록&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; - 예를 들어, app 인스턴스엔 default, app, ext_office 보안 그룹이 할당되어 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/AWS</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/779</guid>
      <comments>https://ohgyun.com/779#entry779comment</comments>
      <pubDate>Tue, 23 Oct 2018 18:32:45 +0900</pubDate>
    </item>
    <item>
      <title>Python으로 Alfred Workflow 만들기</title>
      <link>https://ohgyun.com/778</link>
      <description>&lt;div&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;발생일:&lt;/span&gt; 2018.10.20&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; alfred workflow, 알프레드 워크플로우&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;문제:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;파이썬으로 알프레드 워크플로우 만들기 :)&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;해결책:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;파이썬으로 워크플로우를 쉽게 만들 수 있게 부트스트랩 라이브러리를 만들어둔 저장소가 있다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://github.com/deanishe/alfred-workflow&quot;&gt;https://github.com/deanishe/alfred-workflow&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래는 간단 튜토리얼.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;http://www.deanishe.net/alfred-workflow/tutorial_1.html&quot;&gt;http://www.deanishe.net/alfred-workflow/tutorial_1.html&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;# 부트스트랩 라이브러리 설치&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;알프레드 워크플로우를 생성한 후에, 해당 디렉토리에서 아래처럼 설치할 수 있다. (&lt;a href=&quot;http://www.deanishe.net/alfred-workflow/installation.html&quot;&gt;설치 가이드 참고&lt;/a&gt;)&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ pip install --target ./workflow Alfred-Workflow&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;&lt;b&gt;# 한글 이슈&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;알프레드는 맥의 기본 버전인 파이썬 2.7 버전을 사용하기 때문에, 고질적인 유니코드 문제가 있다.&lt;/div&gt;
&lt;div&gt;unicodedata 모듈로 아래와 같이 노멀라이징하면 스크립트 필터에서 전달하는 한글 이슈를 해결할 수 있다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; import sys&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; import unicodedata&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; reload(sys)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; sys.setdefaultencoding(&quot;utf-8&quot;)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; query = unicodedata.normalize('NFC', query)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;위에서 언급한 workflow 모듈의 인스턴스의 args 변수에 실행 스크립트로 전달한 인자가 저장되며,&lt;/div&gt;
&lt;div&gt;이미 유니코드로 노멀라이즈 되어 있어서 바로 사용해도 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; query = wf.args[0]&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;다만, 디버깅 용도로 print()로 한글을 출력하려면 기본 인코딩을 지정해줘야 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; import sys&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; reload(sys)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; sys.setdefaultencoding(&quot;utf-8&quot;)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; query = wf.args[0]&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;# 서드파티 라이브러리 추가 (third-party library)&lt;/b&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;먼저, pip로 별도 라이브러리에 서드파티 라이브러리를 설치한다.&lt;/div&gt;
&lt;div&gt;예를 들어, lib 디렉토리에 click 이란 패키지를 설치하려면 아래와 같이 실행하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ pip install --target=&lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;./lib&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt; click&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;스크립트에서 직접 가져오려면 __init__.py 를 만들어줘야 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; $ touch ./lib/__init__.py&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; from lib import click&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;wf 인스턴스에서 바로 가져오는 방법도 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; def main(wf):&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;import requests&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ...&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; if __name__ == '__main__':&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; wf = Workflow3(&lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;libraries=['./lib']&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt;)&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sys.exit(wf.run(main))&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;&lt;b&gt;# 꿀벌개발일지 검색용 워크플로우&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래와 같이 검색하는 간단한 워크플로우이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 683px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99EF334E5BCA081211&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99EF334E5BCA081211&quot; width=&quot;683&quot; height=&quot;580&quot; filename=&quot;evernote_image_1.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;코드는 아래 gist에서 확인하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://gist.github.com/ohgyun/0f728f80ff14fd64e5bea476be2cf695&quot;&gt;https://gist.github.com/ohgyun/0f728f80ff14fd64e5bea476be2cf695&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;다운로드해 참고해보면 쉽게&amp;nbsp;다른 워크플로우를 만들 수 있을 것이다. :D&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block;   height: auto; max-width: 100%;&quot;&gt;&lt;a href=&quot;https://t1.daumcdn.net/cfile/tistory/998686455BCA08380E&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;https://i1.daumcdn.net/cfs.tistory/v/0/blog/image/extension/unknown.gif&quot; style=&quot;vertical-align: middle;&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;꿀벌개발일지_1.0.alfredworkflow&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/DevTip</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/778</guid>
      <comments>https://ohgyun.com/778#entry778comment</comments>
      <pubDate>Sat, 20 Oct 2018 01:37:35 +0900</pubDate>
    </item>
    <item>
      <title>MySql: LOAD DATA INFILE 로 대용량 데이터 인서트하기</title>
      <link>https://ohgyun.com/777</link>
      <description>&lt;div&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;발생일:&lt;/span&gt; 2017.11.17&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;키워드:&lt;/span&gt; MySQL, LOAD DATA INFILE, insert large amount of dataset into mysql database, 대용량 데이터 추가&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;문제:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;대용량 데이터를 MySQL 디비에 인서트하려고 한다. 가장 효율적인 방법이 뭘까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;해결책:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;텍스트 파일을 읽어 테이블에 인서트하는 &lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/load-data.html&quot;&gt;LOAD DATA INFILE&lt;/a&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt; &lt;/span&gt;구문이 있다.&lt;/div&gt;
&lt;div&gt;기본 INSERT 구문을 쓰는 것보다 20배 정도 빠르다고 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;대략 아래와 같은 포맷으로 실행할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; LOAD DATA LOCAL INFILE '{file_name}'&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; INTO TABLE {table_name}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; CHARACTER SET utf8&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; FIELDS&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; TERMINATED BY '{field_terminator}' &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 143, 0);&quot;&gt;# 각 필드 구분 문자 (예: CSV라면 컴마)&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; OPTIONALLY ENCLOSED BY '&quot;' &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 143, 0);&quot;&gt;# 필요할 경우, 따옴표(&quot;)로 구분&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; LINE TERMINATED BY '\n'&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; IGNORE 1 LINES &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 143, 0);&quot;&gt;# 제목이 포함된 첫 번째 줄은 생략&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; (col1, col2, ... ) &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: rgb(0, 143, 0);&quot;&gt;# 컬럼명&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;논의:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;# 권한 이슈&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;LOAD DATA를 사용하려면 별도의 권한이 있어야 한다. DB에 접속할 때, &lt;span style=&quot;color: rgb(148, 23, 81);&quot;&gt;load_infile=1&lt;/span&gt; 파라미터를 넘기면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;# 인덱스와 인서트 성능&lt;/b&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;MyISAM 테이블에서는 인덱스를 끄면 성능이 향상된다고 한다.&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ALTER TABLE ... DISABLE KEYS;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;InnoDB 테이블에서는 인덱스를 끄지 않아도 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;2018.03.09 추가&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;b&gt;# 유니코드 캐릭터 인코딩 이슈&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;유니코드 형식의 데이터의 백슬래시가 사라지고 코드 자체가 인서트되는 경우가 있다.&lt;/div&gt;
&lt;div&gt;예를 들어,&amp;nbsp;데이터에 '경기도'라는 문자가 있다면, 유니코드로 변환돼&amp;nbsp;&quot;\uacbd\uae30\ub3c4&quot;가 인서트되어야 하는데,&lt;/div&gt;
&lt;div&gt;백슬래시가 생략되어&amp;nbsp;&quot;uacbduae30ub3c4&quot;와 같이 인서트된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;CSV로 저장할 때 이스케이프 캐릭터였던 백슬래시를 문자열로 변경했는데,&lt;/div&gt;
&lt;div&gt;MySQL에서 파일을 로드할 때 백슬래시를 다시 이스케이프 캐릭터로 인식한 것이 문제였다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;CSV로 저장할 때 이미 문자열된 상태이기 때문에 MySQL에서 로드할 땐 이스케이프 캐릭터를 무시하는 것으로 해결했다.&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; LOAD DATA LOCAL INFILE '{file_name}'&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; INTO TABLE {table_name}&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; CHARACTER SET utf8&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; FIELDS&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; TERMINATED BY '{field_terminator}'&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; OPTIONALLY ENCLOSED BY '&quot;'&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;ESCAPED BY '' &lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#008f00&quot;&gt;# 문자열 그대로 인서트될 수 있게 이스케이프 캐릭터를 빈 값으로 처리&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; LINES TERMINATED BY '\n'&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; IGNORE 1 LINES&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;({column_str})&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;2018.10.19 추가&lt;/div&gt;
&lt;div&gt;&lt;b&gt;# 빈 값 이슈&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;CSV 파일 내 빈 데이터가 NULL로 인서트 되지 않고, 컬럼 타입에 따라 empty string ('') 또는 0으로 들어간다.&lt;/div&gt;
&lt;div&gt;기본값인 NULL로 넣고 싶다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; 예) 1,2,3,,5&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/problems-with-null.html&quot;&gt;MySQL 문서&lt;/a&gt;에 따르면, LOAD DATA의 타겟 파일의 빈 필드에&amp;nbsp;&lt;font color=&quot;#941751&quot;&gt;\N &lt;/font&gt;을 넣으면 NULL로 인서트된다고 한다.&lt;/div&gt;
&lt;div&gt;NULL를 넣어도 될 때가 있는데, 환경에 따라 다르다고 하니 \N을 넣는 게 좋겠다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp; &amp;nbsp; 예) 1,2,3,&lt;/span&gt;&lt;font color=&quot;#941751&quot;&gt;\N&lt;/font&gt;&lt;span style=&quot;color: #005493;&quot;&gt;,5&lt;/span&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;우린 pandas의 데이터프레임을 CSV로 만들어 인서트하고 있는데, pandas에 빈 값을 할당하는 인자가 있어 아래와 같이 처리했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; df.to_csv(filename, &lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;na_rep='\\N'&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt;)&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;참고:&amp;nbsp;&lt;a href=&quot;https://stackoverflow.com/questions/2675323/mysql-load-null-values-from-csv-data&quot;&gt;https://stackoverflow.com/questions/2675323/mysql-load-null-values-from-csv-data&lt;/a&gt;
&lt;/div&gt;
&lt;div&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/DB</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/777</guid>
      <comments>https://ohgyun.com/777#entry777comment</comments>
      <pubDate>Fri, 19 Oct 2018 19:14:08 +0900</pubDate>
    </item>
    <item>
      <title>Pandas: 그룹 내에서 상위 n개 가져오기</title>
      <link>https://ohgyun.com/776</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.10.19&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; pands, 판다스, groupby, nlargest, nsmallest, sort_values, get n largest value in group&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;그룹 내에서 값이 큰 순으로 상위 n개만 가져오려고 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이 용도의 &lt;a href=&quot;http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.nlargest.html?highlight=nlargest#pandas.DataFrame.nlargest&quot;&gt;nlargest&lt;/a&gt; 와 &lt;a href=&quot;http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.nsmallest.html#pandas.DataFrame.nsmallest&quot;&gt;nsmallest&lt;/a&gt; API를 제공하고 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;하지만, 가장 빠른 방법은 데이터프레임을 원하는 값으로 정렬한 후에 groupby 하고, head 로 가져오는 것이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;id, name, total 로 구성된 데이터프레임이 있고, 각 name의 값을 total 로 정렬해 상위 3개만 가져오고 싶다면 아래와 같이 처리하는 것이 빠르다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; df.sort_values(by='total', ascending=False).groupby('name').head(3)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;DataFrame.groupby 문서에 의하면,&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#008f00&quot;&gt;sort : boolean, default True&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#008f00&quot;&gt;&amp;nbsp; &amp;nbsp; Sort group keys. Get better performance by turning this off.&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#008f00&quot;&gt;&amp;nbsp; &amp;nbsp; Note this does not influence the order of observations within each group.&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#008f00&quot;&gt;&amp;nbsp; &amp;nbsp; groupby preserves the order of rows within each group.&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;그룹 내 행의 순서가 유지되기 때문에 안전하다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;그룹 자체의 정렬이 필요하지 않다면, sort 옵션을 끄면 좀 더 빠르다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp; &amp;nbsp; df.sort_values(by='total', ascending=False).groupby('name', &lt;/span&gt;&lt;font color=&quot;#941751&quot;&gt;sort=False&lt;/font&gt;&lt;span style=&quot;color: #005493;&quot;&gt;).head(3)&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;nlargest 를 사용해 동일하게 아래와 같이 처리해도 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; df.groupby('name').apply(lambda x: x.nlargest(3, 'total'))&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;하지만, 약 150만개 행으로 테스트했을 때, 첫 번째 방법은 200ms 근처로 실행됐지만, 두 번째 방법은 40초 이상 걸렸다.&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://stackoverflow.com/a/36074520/954292&quot;&gt;https://stackoverflow.com/a/36074520/954292&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/Python</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/776</guid>
      <comments>https://ohgyun.com/776#entry776comment</comments>
      <pubDate>Fri, 19 Oct 2018 16:57:19 +0900</pubDate>
    </item>
    <item>
      <title>ML: Matchbox Recommender 추천 모델 적용 실패 사례</title>
      <link>https://ohgyun.com/775</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt;&amp;nbsp;2017.01.14&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt;&amp;nbsp;Matchbox recommender&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;지금도 잘 모르지만, 지금보다 더 잘 몰랐을 때의 메모 ㅎㅎ.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/machine-learning/studio-module-reference/train-matchbox-recommender&quot;&gt;MS 의 Mathbox recommender 모델&lt;/a&gt;을 사용해 추천을 적용하려고 했다가 실패했다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;점수를 내는 기준을 제대로 잡지 못했기 때문이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;지금 보면 워낙 기초가 없던 때라 그런 거였는데, 나중에 같은 실수를 하지 않게 기록해둔다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;매치박스 레코멘더 모듈은 각 아이템 간에 점수(rating)를 부여하고 이를 조합해 추천하는 방식이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;우린 아파트 정보 서비스여서,&lt;/div&gt;
&lt;div&gt;- 사용자에게 아파트를 추천&lt;/div&gt;
&lt;div&gt;- 아파트와 비슷한 아파트 추천&lt;/div&gt;
&lt;div&gt;하는 것을 목표로 했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;점수는 조회수와 알림 구독 여부, 우리집 선택 여부로 아래와 같이 조합했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- rating이 0부터 시작해서,&lt;/div&gt;
&lt;div&gt;- 조회수 4건 이상이면 +1&lt;/div&gt;
&lt;div&gt;- 조회수 7건 이상이면 +2&lt;/div&gt;
&lt;div&gt;- 구독하면 +2&lt;/div&gt;
&lt;div&gt;- 우리집으로 선택하면 +2&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;rating의 최대값은 10으로 조정했다.&lt;/div&gt;
&lt;div&gt;평가값이 0~최대값 사이이기 때문에, 이상치에 의해 최대값이 영향을 받을 것 같았기 때문이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;여러 조건으로 실험해본 결과는 정확도는 0.75~0.8사이에 있었고, 평가값이 좋아지는 쪽으로 수치를 조절했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;사용자에게 아파트를 추천하는 작업은 학습 시간이 오래 소요돼서, 아파트와 비슷한 아파트 추천 결과를 실제로 돌려봤다.&lt;/div&gt;
&lt;div&gt;헐... 근데 결과가 정말 엉망이었다. 이렇게 안 맞을 수 있다니.... -0-&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;혹시나 해서 조회수만 기준으로 해서, rating 값이 0, 1, 2만 나오도록 실험했더니 0.87이 나오더라.&lt;/div&gt;
&lt;div&gt;나아가, 구독 여부로 rating이 0, 1만 나오도록 했더니, 정확도가 0.96까지 올라갔다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;비슷한 아파트 추천 결과도 만족스러웠다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;왜 그랬던 걸까?&lt;/div&gt;
&lt;div&gt;논문을 살펴봤다.&lt;/div&gt;
&lt;div&gt;Matchbox: Large Scale Online Bayesian&amp;nbsp;Recommendations 논문:&amp;nbsp;&lt;a href=&quot;http://www.herbrich.me/papers/www09.pdf&quot;&gt;http://www.herbrich.me/papers/www09.pdf&lt;/a&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;In addition it is desirable that a recommender system can&lt;/div&gt;
&lt;div&gt;be applied flexibly in a wide variety of scenarios. Frequently,&lt;/div&gt;
&lt;div&gt;data on user preferences is in the form of ratings on an ordinal&lt;/div&gt;
&lt;div&gt;scale (for example Netflix [1]) where each user’s interpretation&lt;/div&gt;
&lt;div&gt;of this scale may be different. In other cases we&lt;/div&gt;
&lt;div&gt;may only have data showing which items were clicked on&lt;/div&gt;
&lt;div&gt;by a user, where we assume that a user clicking on an item&lt;/div&gt;
&lt;div&gt;provides implicit evidence that it is interesting to them&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;사용자는 흔히 넷플릭스의 영화 평점처럼 1~5점의 순서를 가진 값으로 선호도를 평가할 수 있다.&lt;/div&gt;
&lt;div&gt;이렇게 평가할 경우, 같은 점수를 주더라도 사용자마다 평가하는 기준은 다를 수 있다.&lt;/div&gt;
&lt;div&gt;(예: A가 준 4점과, B가 준 4점은, 점수를 주는 그의 성향에 따라 의미가 다를 수 있다)&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;반면, 좋다/싫다와 같이 평가할 수도 있다.&lt;/div&gt;
&lt;div&gt;구글의 뉴스 추천 시스템은 단순히 기사 클릭 여부를 가지고 추천한다고 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;논문에서 제시하는 추천 시스템 아래 3가지 대안을 가지고 rating을 평가한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;The model for user feedback is flexible. We present&lt;/div&gt;
&lt;div&gt;three alternatives: direct observation of an absolute&lt;/div&gt;
&lt;div&gt;rating each user gives to some items, observation of a&lt;/div&gt;
&lt;div&gt;binary preference (like/ don’t like) and observation of&lt;/div&gt;
&lt;div&gt;a set of ordinal ratings on a user-specific scale&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- rating의 절대값&lt;/div&gt;
&lt;div&gt;- rating의 바이너리값&lt;/div&gt;
&lt;div&gt;- 사용자가 평가한 값의 범위 내에서의 순위&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;생각해보니, 우리가 잘못 적용한 점이 있었다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;먼저, rating의 후보 값은 같은 맥락에 있어야 한다는 것이다.&lt;/div&gt;
&lt;div&gt;영화 평점이 1~5점이었던 것에 반해, 우리가 처음에 적용했던 척도와는 좀 달랐다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- A가 평점을 3, 4, 5 범위 내에서만 했다면, A에게 4점은 보통일 것이다.&lt;/div&gt;
&lt;div&gt;- B가 평점을 1, 3, 5 범위 내에서만 했다면, B에게 4점은 좋은 축에 속할 것이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;같은 맥락이 아니라면 사용자 범위 내로 순위를 매길 때 의미가 없어질 것 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;완전히 조회수 기준이라면,&amp;nbsp;&lt;/div&gt;
&lt;div&gt;- A의 아파트 선호도가 1, 10, 30이라면, A에게 10번 조회는 나쁜 축에 속할 것이다.&lt;/div&gt;
&lt;div&gt;- B의 아파트 선호도가 1, 3, 10이라면, B에게 10번 조회는 좋은 축에 속할 것이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;(이런 이유 때문에 n번 이상 평가한 사용자를 변수로 설정할 수 있게 한 모양이다)&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;우리가 처음에 했던 것처럼, 조회수(+1씩)와 구독 여부(+10)의 조건을 합해 점수를 냈다면,&lt;/div&gt;
&lt;div&gt;- A의 아파트 선호도가 1, 10(a,&amp;nbsp;구독), 30(조회+)이라면, A는 구독한 아파트 a는 추천 대상이 아닐 것이다.&lt;/div&gt;
&lt;div&gt;- 반면, B의 아파트 선호도가 1, 3, 10(b,&amp;nbsp;구독)이라면, B가 구독한 아파트 b는 추천 대상이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;처음부터 rating의 변수를 설정했던 방법이 우리 의도를 벗어난 것이었다.&lt;/div&gt;
&lt;div&gt;이런 경우라면, 바이너리 결과 값으로 rating을 평가하는 것이 더 나은 결정일 것 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/AI</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/775</guid>
      <comments>https://ohgyun.com/775#entry775comment</comments>
      <pubDate>Fri, 19 Oct 2018 12:15:02 +0900</pubDate>
    </item>
    <item>
      <title>conda 환경에서 pip 패키지도 동시에 업데이트 하기</title>
      <link>https://ohgyun.com/774</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.10.18&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; conda env update, pip install --upgrade, environment.yml, 콘다 환경, 아나콘다, 미니콘다, anaconda, miniconda&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;서비스에서는 conda 패키지를 envrionment.yml 파일로 관리하고 있다.&lt;/div&gt;
&lt;div&gt;이런 식이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;[environment.yml]&lt;/div&gt;
&lt;div&gt;name: foo&lt;/div&gt;
&lt;div&gt;channels:&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp;- conda-forge&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp;- defaults&lt;/div&gt;
&lt;div&gt;dependencies:&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp;- python=3.7&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp;- ipython&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp;- jupyter&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp;- pandas&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp;- numpy&lt;/div&gt;
&lt;div&gt;&amp;nbsp; - pip:&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; - konlpy&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; - ...&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;헌데, conda env update 를 실행해도, 환경 파일에 정의해둔 pip 패키지는 업데이트되지 않는다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;어떻게 해야할까?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;---&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: rgb(9, 0, 255);&quot;&gt;conda 4.6.13 부터 해결되었다고 한다.&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/conda/conda/issues/7774&quot; target=&quot;_blank&quot; class=&quot;tx-link&quot;&gt;https://github.com/conda/conda/issues/7774&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;conda environment 내에 정의한 pip 내의 패키지는 환경을 생성(conda create)할 땐 설치되지만, conda env update 명령으로 업데이트되진 않는다.&lt;/div&gt;
&lt;div&gt;패키지가 별도로 관리되기 때문이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;conda list 의 결과 내에 pip 패키지도 포함되어 있기 때문에, 이 목록으로 별도로 업데이트하게 처리했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;conda list | grep &quot;&amp;lt;pip&amp;gt;&quot; | cut -d &quot; &quot; -f 1 | xargs pip install --upgrade&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://stackoverflow.com/questions/44265533/does-conda-update-packages-from-pypi-installed-using-pip-install&quot;&gt;https://stackoverflow.com/questions/44265533/does-conda-update-packages-from-pypi-installed-using-pip-install&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# 환경 파일에서 채널은 뭔가?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- 채널을 정의하면 conda install 로 패키지를 설치할 때, 해당 채널의 패키지를 설치한다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;- conda-forge 채널의 패키지를 설치하는 것이 안정성 측면에서 높다고 한다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;- 설치하려는 패키지가 정의한 여러 채널에 중복으로 있는 경우, 선호도가 높은 채널(정의한 순서대로)의 것을 먼저 사용한다.&lt;/div&gt;
&lt;div&gt;- 참고:&amp;nbsp;&lt;a href=&quot;https://veranostech.github.io/docs-korean-conda-docs/docs/build/html/user-guide/tasks/manage-channels_ko.html#id3&quot;&gt;https://veranostech.github.io/docs-korean-conda-docs/docs/build/html/user-guide/tasks/manage-channels_ko.html#id3&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# 환경 파일에서 pip 정의해야 하나?&lt;/div&gt;
&lt;div&gt;- pip는 기본으로 설치된다.&lt;/div&gt;
&lt;div&gt;- environment.yml 에서 pip 는 생략해도 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;# 환경 파일을 자동으로 관리할 수 없을까? lock 을 한다거나.&lt;/div&gt;
&lt;div&gt;- conda env export 명령으로 enviroment.yml 파일을 생성할 수 있지만, OS 간 호환성이 보장되지 않는다.&lt;/div&gt;
&lt;div&gt;-&amp;nbsp;패키지에 따라 OS 별로 버전이 다른 경우가 있기 때문이다.&lt;/div&gt;
&lt;div&gt;- 따라서, OS 간 호환이 필요하다면, conda 의 environment.yml 파일은 수동으로 관리해주는 게 좋다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;- 참고:&amp;nbsp;&lt;a href=&quot;https://stackoverflow.com/questions/39280638/how-to-share-conda-environments-across-platforms&quot;&gt;https://stackoverflow.com/questions/39280638/how-to-share-conda-environments-across-platforms&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;나중엔 운영과 동일하게 docker 로 환경을 구성하고, 필요한 시점에 conda env export 로 lock 파일을 만들어 관리해야겠다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/Python</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/774</guid>
      <comments>https://ohgyun.com/774#entry774comment</comments>
      <pubDate>Fri, 19 Oct 2018 11:35:34 +0900</pubDate>
    </item>
    <item>
      <title>node.js: euc-kr 페이지에 보낼 요청 파라미터 인코딩</title>
      <link>https://ohgyun.com/773</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.09.03&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; euc-kr, euckr, node.js, iconv, 인코딩, encoding, escape, encodeURIComponent&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;노드에서 euc-kr 로 인코딩 된 페이지로 GET 요청을 보내려고 한다.&lt;/div&gt;
&lt;div&gt;한글이 포함된 값을 파라미터로 전송하려고 하는데, 어떻게 인코딩하면 될까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/bnoordhuis/node-iconv&quot;&gt;iconv&lt;/a&gt; 라이브러리를 활용하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;const buffer = iconv.encode('한글이 포함된 파라미터', 'euckr'); &lt;/font&gt;&lt;font color=&quot;#008f00&quot;&gt;//&amp;nbsp;파라미터를 euc-kr 로 인코딩 해 버퍼에 담은 후,&lt;/font&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;const param = encode(buffer.toString('binary')); &lt;/font&gt;&lt;font color=&quot;#008f00&quot;&gt;// 바이너리로 변환해 이스케이프하면 된다.&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;encodeURI() 와 encodeURIComponent() 는 UTF8 인코딩된 문자열을 대상으로 한다.&lt;/div&gt;
&lt;div&gt;UTF8로 인코딩되지 않은 문자열을 URL 인코딩(퍼센트 인코딩)하고자 할 땐, 바이너리로 변환 후 escape() 함수를 사용하면 된다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;노드에서 euc-kr 로 인코딩된 문서나 페이지를 읽어올 땐 아래 포스트를 참고&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;http://ohgyun.com/665&quot;&gt;http://ohgyun.com/665&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;클라이언트에서 euc-kr로 submit을 보낼 땐 아래 포스트를 참고&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;http://ohgyun.com/314&quot;&gt;http://ohgyun.com/314&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/Javascript</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/773</guid>
      <comments>https://ohgyun.com/773#entry773comment</comments>
      <pubDate>Sat, 29 Sep 2018 22:37:57 +0900</pubDate>
    </item>
    <item>
      <title>Jupyter notebook HTML output에 템플릿 적용하기</title>
      <link>https://ohgyun.com/772</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.09.29&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; Jupyter notebook, nbconvert to HTML&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;작성한 주피터 노트북을 팀내에 공유하거나 배치로 메일 등으로 발송하게 하려고 한다.&lt;/div&gt;
&lt;div&gt;nbconvert 모듈로 HTML이나 PDF 포맷으로 export 할 수 있다는 건 알고 있다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font style=&quot;color: rgb(0, 84, 147);&quot;&gt;$ jupyter nbconvert --to html sample.ipynb --output output.html&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이렇게 처리했는데, 생성된 HTML이 정말 못생겼다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;좀 예쁘게 바꾸고 싶은데, 어떻게 하면 될까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;nbconvert 모듈로 export 할 때, --template 옵션으로 별도의 템플릿을 적용해 출력 형태를 바꿀 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #005493;&quot;&gt;$ jupyter nbconvert --to html sample.ipynb --template=basic.tpl --output output.html&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;템플릿을 적용해 변환하는 것에 대한 설명은 아래 포스트에 쉽게 잘 정리되어 있다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://www.datacamp.com/community/tutorials/jinja2-custom-export-templates-jupyter&quot;&gt;https://www.datacamp.com/community/tutorials/jinja2-custom-export-templates-jupyter&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;기본적인 방법은 위 포스트 뿐 아니라 구글링 결과도 많다.&lt;/div&gt;
&lt;div&gt;여기엔 상세하게 스타일을 설정하려고 할 때 참고할 만한 자료에 대해 정리해뒀다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;템플릿 엔진&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;먼저, nbconvert 모듈은 파이썬의 주요 템플릿 엔진인&amp;nbsp;&lt;a href=&quot;http://jinja.pocoo.org/&quot;&gt;Jinja 템플릿&lt;/a&gt;을 쓴다.&lt;/div&gt;
&lt;div&gt;스타일을 변경하거나 원하는 템플릿만 출력하는 정도만 활용하면 되며, 아래 링크의 템플릿 문법만 알아두면 된다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;http://jinja.pocoo.org/docs/2.10/templates/&quot;&gt;http://jinja.pocoo.org/docs/2.10/templates/&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;템플릿 구조&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;주피터 노트북을 컨버팅할 때, 템플릿의 구조를 알아야 상세하게 설정할 수 있겠더라.&lt;/div&gt;
&lt;div&gt;템플릿의 구조는 아래 문서를 확인하면 된다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://nbconvert.readthedocs.io/en/latest/customizing.html#Template-structure&quot;&gt;https://nbconvert.readthedocs.io/en/latest/customizing.html#Template-structure&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;부모 템플릿&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;하위 템플릿에서 상속한 상위 템플릿의 상세 구조를 알아야 하는 경우가 있었다.&lt;/div&gt;
&lt;div&gt;내가 사용하기엔 기본 제공되는 basic.tpl 은 너무 간소했고, full.tpl 은 너무 과했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이럴 땐 basic.tpl 의 일부만 따서, 원하는 템플릿을 만들면 된다.&lt;/div&gt;
&lt;div&gt;상위 템플릿 파일은 아래 링크에서 확인할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;basic.tpl 파일&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://github.com/jupyter/nbconvert/blob/4.x/nbconvert/templates/html/basic.tpl&quot;&gt;https://github.com/jupyter/nbconvert/blob/4.x/nbconvert/templates/html/basic.tpl&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;full.tpl 파일&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://github.com/jupyter/nbconvert/blob/4.x/nbconvert/templates/html/full.tpl&quot;&gt;https://github.com/jupyter/nbconvert/blob/4.x/nbconvert/templates/html/full.tpl&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;테마 모음&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;주피터 노트북 테마가 없을 리가 없다.ㅎㅎ&lt;/div&gt;
&lt;div&gt;주피터 노트북의 테마까지 변경할 수 있고, 어떤 모듈은 프로필까지 적용해준다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;그 중 괜찮아 보이는 모듈을 찾았다.&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/dunovank/jupyter-themes&quot;&gt;https://github.com/dunovank/jupyter-themes&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;난 일단 목적에 충실하기 위해 스타일만 가져다 적용해봤다. HTML 아웃풋도 예쁘게 잘 나온다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://github.com/dunovank/jupyter-themes/blob/master/jupyterthemes/styles/compiled/chesterish.css&quot;&gt;https://github.com/dunovank/jupyter-themes/blob/master/jupyterthemes/styles/compiled/chesterish.css&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;그렇지만, 난 깃헙 스타일이 제일 좋더라!&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;A Github Flavored Theme for Ipython or Jupyter Notebook&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://gist.github.com/aahoo/e8ed425759711ead1ef7e7a3332dcd2d&quot;&gt;https://gist.github.com/aahoo/e8ed425759711ead1ef7e7a3332dcd2d&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;스크립트 적용&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;full.tpl 은 기본적으로 jQuery를 임베딩하고 있다.&lt;/div&gt;
&lt;div&gt;난 스태틱한 페이지를 만들기 위해 스크립트를 제거했는데, 필요에 따라 활용하는 것도 좋다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래는 output이 토글되게 적용한 샘플이다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://gist.github.com/gabraganca/7097067&quot;&gt;https://gist.github.com/gabraganca/7097067&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;TemplateExporter&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;템플릿 파일에서 셀의 출력 방식을 변경하거나 원하지 않은 경우 빈 값으로 렌더링할 수 있다.&lt;/div&gt;
&lt;div&gt;하지만, nbconvert 명령에서 TemplateExporter 옵션으로도 제거할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;난 input 영역과 output 의 숫자 출력 부분을 제거하고 싶어 아래와 같이 실행했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;$ jupyter nbconvert --to html .ipynb --template=sample.tpl &lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;--TemplateExporter.exclude_input=True --TemplateExporter.exclude_output_prompt=True&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt; --output output.html&lt;/font&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;TemplateExporter 로 제거할 수 있는 항목은&amp;nbsp;아래 문서를 참고하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://nbconvert.readthedocs.io/en/latest/config_options.html?highlight=TemplateExporter&quot;&gt;https://nbconvert.readthedocs.io/en/latest/config_options.html?highlight=TemplateExporter&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;추가로, 용어가 좀 헷갈려서 설명 이미지를 만들었다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 683px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/990E544C5BAE5EE63C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F990E544C5BAE5EE63C&quot; width=&quot;683&quot; height=&quot;410&quot; filename=&quot;evernote_image_1.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;노트북을 새로 실행하면서 변환&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;노트북을 새로 실행하면서 생성하려면 --execute 옵션을 주면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;$ jupyter nbconvert --to html .ipynb &lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;--execute&lt;/font&gt;&lt;font style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; --output output.html&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;최종&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래 이미지는 기본 컨버팅 결과와 템플릿을 다듬어서 출력한 결과를 비교해둔 것이다.&lt;/div&gt;
&lt;div&gt;손본 게 확실히 예뿌다.ㅎㅎㅎ&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 683px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9983E73F5BAE5EE733&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9983E73F5BAE5EE733&quot; width=&quot;683&quot; height=&quot;794&quot; filename=&quot;evernote_image_2.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;수정한 템플릿 파일은&amp;nbsp;아래 gist를 참고하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://gist.github.com/ohgyun/986a5c1718e49f8d6da6d0cb5e79438e&quot;&gt;https://gist.github.com/ohgyun/986a5c1718e49f8d6da6d0cb5e79438e&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;컨버팅할 때 실행한 명령은 다음과 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;$ jupyter nbconvert --to html .ipynb --template=sample.tpl --TemplateExporter.exclude_input=True --TemplateExporter.exclude_output_prompt=True --output output.html&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/Python</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/772</guid>
      <comments>https://ohgyun.com/772#entry772comment</comments>
      <pubDate>Sat, 29 Sep 2018 02:03:48 +0900</pubDate>
    </item>
    <item>
      <title>Node.js: npm asyncWrite 오류 발생할 때</title>
      <link>https://ohgyun.com/771</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2019.09.27&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; node, npm install, n, asyncWrite&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;npm install 로 모듈을 설치할 때 아래와 같은 오류가 발생한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;npm ERR! asyncWrite is not a function&lt;/div&gt;
&lt;div&gt;npm ERR! pna.nextTick is not a function&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;npm 을 재설치하는 방법으로 해결했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;$ curl -0 -L https://npmjs.com/install.sh | sudo sh&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;npm 모듈의 캐시도 날려보고&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; $&amp;nbsp;&lt;/span&gt;sudo npm cache clean -f&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;node 관리 모듈인 n을 재설치하기도 하고, node 를 stable 버전으로 새로 갱신하기도 했는데 해결되지 않았다.&lt;/div&gt;
&lt;div&gt;다른 사람들은 이런 방법으로도 해결된 것 같으니 혹시 npm 재설치로 해결되지 않는다면 시도해보는 것도 좋겠다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://stackoverflow.com/questions/50597159/npm-err-asyncwrite-is-not-a-function/52529039#52529039&quot;&gt;https://stackoverflow.com/questions/50597159/npm-err-asyncwrite-is-not-a-function/52529039#52529039&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/Javascript</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/771</guid>
      <comments>https://ohgyun.com/771#entry771comment</comments>
      <pubDate>Thu, 27 Sep 2018 12:59:58 +0900</pubDate>
    </item>
    <item>
      <title>DataGrip 팁 모음</title>
      <link>https://ohgyun.com/770</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.09.01&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; JetBrains DataGrip, 단축키, shortcut&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;얼마 전부터 데이터베이스 IDE로 젯브레인의 데이터 그립을 사용하고 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;1년 결제 시 연간 $199, 3년 결제 시 연간 $119로 매우 비싸지만...&lt;/div&gt;
&lt;div&gt;여러 디비를 한 번에 쓸 수 있다는 장점이 있어 바꿨다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;사용해보고 몇 가지 자주 사용하는 팁을 기록해둔다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;* 팁 내에서 RMB는 오른쪽 마우스(Right Mouse Button)를 의미&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;특정 테이블 하나를 빠르게 조회하기&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- Cmd + O -&amp;gt; 테이블명 입력 -&amp;gt; &amp;lt;Filter Criteria&amp;gt; 에 where 절 검색&lt;/div&gt;
&lt;div&gt;- 기본 필터 단축키가 복잡하다. 난, Cmd + Shift + F 로 변경해두고 사용하고 있는데 편하다!&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;Row/Column 전환하기&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- 테이블에 여러 컬럼이 있을 때에 유용하다.&lt;/div&gt;
&lt;div&gt;- 테이블 조회 후, RMB -&amp;gt; Transpose 하면 된다.&lt;/div&gt;
&lt;div&gt;- 난 단축키로 Cmd + Shift + T 를 할당해놓고 사용하고 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;특정 인덱스나 컬럼을 포함한 테이블 조회하기&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- Cmd + Option + O 로 특정 컬럼이나 인덱스 명으로 테이블을 조회할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Explain 빠르게 하기&lt;/b&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- 콘솔에서 쿼리 선택 후, RMB -&amp;gt; Explain&lt;/div&gt;
&lt;div&gt;- 명령어에 Explain을 붙이지 않아도 편리하다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;템플릿으로 빠르게 쿼리 작성하기&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- 자주 사용하는 쿼리를 설정 &amp;gt; Live Templates 에 넣어두면 편하다.&lt;/div&gt;
&lt;div&gt;- 예: sel 입력하면 select 완성&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/DevTip</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/770</guid>
      <comments>https://ohgyun.com/770#entry770comment</comments>
      <pubDate>Sun, 23 Sep 2018 18:28:17 +0900</pubDate>
    </item>
    <item>
      <title>Matplotlib: 폰트 기본 경로를 찾을 수 없을 때</title>
      <link>https://ohgyun.com/769</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.09.22&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt;&amp;nbsp;Jupyter Notebook, 주피터 노트북, 한글 폰트, 한글 깨질 때, font_manager, fontManager, seaborn, custom font, 나눔고딕, Nanum Gothic, AppleGothic, Mac OS X, set matplotlib font directory&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;주피터 노트북에서 Matplotlib 이나 Seaborn 으로 차트를 그릴 때 한글이 깨지는 문제가 있다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;이상한 건, 기존엔 잘 그려지다가 갑자기 안된다는 것이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;폰트 설정도 하고, rcParam도 설정하고, 캐시도 날려보고, 무려 재부팅도 했는데 안된다...&lt;/div&gt;
&lt;div&gt;왜 그럴까?&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;Matplotlib 모듈이 내 맥의 폰트 디렉토리를 제대로 찾지 못하는 것 같다.&lt;/div&gt;
&lt;div&gt;정확한 원인은 모르겠지만, 커널이 실행된 후에 모듈을 업데이트하는 과정에서 뭔가 문제가 생겼나보다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;여튼, 아래와 같이 명시적으로 폰트 디렉토리를 설정해주는 방법으로 해결했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; from matplotlib import font_manager&lt;/span&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 폰트 경로가 인식되지 않는 문제가 있다&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font_dirs = ['/Library/Fonts']&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font_files = font_manager.findSystemFonts(fontpaths=font_dirs)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font_list = font_manager.createFontList(font_files)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;font_manager.fontManager.ttflist.extend(font_list)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;font_dirs 변수 내에 있는 /Library/Fonts 는 맥의 기본 폰트 디렉토리이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;별도로 설치한 폰트나 다른 디렉토리에 있는 폰트를 차트를 그리는데 사용하고 싶다면,&lt;/div&gt;
&lt;div&gt;동일하게 font_dirs 디렉토리에 넣어두면 된다.&lt;/div&gt;
&lt;div&gt;프로젝트 내에서 공통으로 사용하는 폰트라면, 폰트 ttf 파일을 리파지터리에 넣어두고 상대 경로로 지정해도 괜찮을 것 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;font_dirs 가 유효하지 않아도 워닝이가 경고가 발생하지 않는다.&lt;/div&gt;
&lt;div&gt;스크립트가 여러 환경에서 실행된다면, 대상 OS의 유효한 폰트 디렉토리를 넣어둬도 괜찮을 것 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;난 주로 Seaborn 을 사용하고 있어서, 폰트는 아래와 같이 적용했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;import seaborn as sns&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;...&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;sns.set(font='AppleGothic')&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;일반적으로 한글이 깨지는 건 기본으로 설정된 폰트에 한글이 없기 때문이다.&lt;/div&gt;
&lt;div&gt;대부분 차트의 폰트를 바꾸는 방법으로 해결 가능하다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;내게 발생한 문제는 아니었는데, 주피터 노트북의 캐시가 문제가 되는 경우가 있다고 한다.&lt;/div&gt;
&lt;div&gt;이 땐 주피터 노트북의 캐시를 삭제하고, 노트북을 다시 실행하면 해결된다고 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;캐시 디렉토리는 get_cachedir() 명령으로 확인할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;import matplotlib as mpl&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;print(mpl.get_cachedir())&amp;nbsp; #--&amp;gt; '/Users/ohgyun/.matplotlib'&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;디렉토리엔 font 관련 JSON 파일 등이 있는데, 그냥 다 지우면 된다.ㅎㅎ&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;!rm -r {mpl.get_cachedir()}/*&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;참고로, 캐시를 지우지 않고도 폰트 매니저의 내부 메서드로 빌드할 수도 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;mpl.font_manager._rebuild()&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;커스텀 폰트 디렉토리 설정하기&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://stackoverflow.com/questions/35668219/how-to-set-up-a-custom-font-with-custom-path-to-matplotlib-global-font&quot;&gt;https://stackoverflow.com/questions/35668219/how-to-set-up-a-custom-font-with-custom-path-to-matplotlib-global-font&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;폰트 설정에 대해 전반적으로 잘 설명해주는 강의&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/21/lessons/950&quot;&gt;https://programmers.co.kr/learn/courses/21/lessons/950&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;/div&gt;</description>
      <category>Daylogs/AI</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/769</guid>
      <comments>https://ohgyun.com/769#entry769comment</comments>
      <pubDate>Sun, 23 Sep 2018 12:27:49 +0900</pubDate>
    </item>
    <item>
      <title>Pandas: 한 셀의 데이터를 여러 행으로 나누기</title>
      <link>https://ohgyun.com/768</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.09.22&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; pandas, stack, unstack, split array to multiple rows&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;df = pd.DataFrame({'foo': ['a,b,c,d,e', 'd,e,f', 'h,i']})&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;df&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 242px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99FED14D5BA5B01119&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99FED14D5BA5B01119&quot; width=&quot;242&quot; height=&quot;260&quot; filename=&quot;evernote_image_1.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;위와 같이 한 셀에 들어있는 문자열을 컴마로 구분해서 한 글자씩 여러 행으로 나누고 싶다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;문자열을 split 해 각 행을 여러 컬럼으로 나눈 후 병합하는 방법으로 구현할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;먼저, 각 foo 컬럼의 문자열을 배열로 나눈다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;split = df.foo.str.split(',')&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;split&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 552px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99459E3A5BA5B01132&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99459E3A5BA5B01132&quot; width=&quot;552&quot; height=&quot;178&quot; filename=&quot;evernote_image_2.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;각 배열이 Series를 리턴하게 apply를 적용하면, Series -&amp;gt; DataFrame으로 변환할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;split = split.apply(lambda x: pd.Series(x))&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;split&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 476px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/994858465BA5B01234&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F994858465BA5B01234&quot; width=&quot;476&quot; height=&quot;272&quot; filename=&quot;evernote_image_3.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;stack() 으로 컬럼을 행으로 변환할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;split.stack()&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 430px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99F546445BA5B0121A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F546445BA5B0121A&quot; width=&quot;430&quot; height=&quot;706&quot; filename=&quot;evernote_image_4.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;stack()을 실행하면, 위와 같이 멀티 인덱스를 가진 Series가 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;알파벳 낱자만 가져오기 위해 인덱스를 초기화하고, 기준이 된 인덱스도 제거해보자.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;split.stack().reset_index(level=1, drop=True)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 340px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99BC903D5BA5B01329&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99BC903D5BA5B01329&quot; width=&quot;340&quot; height=&quot;472&quot; filename=&quot;evernote_image_5.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이 결과는 Series이기 때문에, DataFrame으로 변환해보자.&lt;/div&gt;
&lt;div&gt;to_frame()이 파라미터로 컬럼명을 지정할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;split = split.stack().reset_index(level=1, drop=True).to_frame('foo_single')&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;split&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 316px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99D215375BA5B01330&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99D215375BA5B01330&quot; width=&quot;316&quot; height=&quot;708&quot; filename=&quot;evernote_image_6.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;필요에 따라, 원본 프레임에 left join으로 머지하면 아래와 같이 의도했던 결과를 얻을 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;df.merge(split, left_index=True, right_index=True, how='left')&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 448px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9920184E5BA5B01421&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9920184E5BA5B01421&quot; width=&quot;448&quot; height=&quot;714&quot; filename=&quot;evernote_image_7.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/AI</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/768</guid>
      <comments>https://ohgyun.com/768#entry768comment</comments>
      <pubDate>Sat, 22 Sep 2018 12:00:36 +0900</pubDate>
    </item>
    <item>
      <title>MySQL: max_execution_time 설정하기</title>
      <link>https://ohgyun.com/767</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.08.28&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; max_execution_time, slow query, cpu 100% of database&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;실수로 실행 시간이 아주 긴 쿼리가 운영 중인 서비스에서 실행됐다.&lt;/div&gt;
&lt;div&gt;서버에서 맺은 커넥션은 타임아웃이 걸려있어서 문제 없이 끊겼는데, MySQL 서버의 CPU는 여전히 100%다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;디비 서버의 모든 커넥션을 끊은 후에도 한동안 CPU가 100%로 유지되더라.&lt;/div&gt;
&lt;div&gt;문제가 뭘까.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;문제의 쿼리는 SELECT 구문으로 무려 80초나 걸리는 것이었는데, 커넥션이 끊긴 이후에도 해당 쿼리는 계속 실행된 게 원인으로 보인다.&lt;/div&gt;
&lt;div&gt;동일 쿼리가 여러 번 실행됐을 거고, 이런 이유로 CPU가 계속 100%가 되었던 것으로 추측된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;커넥션 타임아웃(connet_timeout)을 설정해둬서 이런 경우에도 문제 없을 거라고 생각했고,&lt;/div&gt;
&lt;div&gt;언뜻 커넥션이 끊겼으니 문제의 쿼리도 종료됐을 거라 생각했는데 아니었다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;커넥션 타임아웃을 설정하는 것과 쿼리의 실행 시간을 제한하는 것은 별개의 문제다.&lt;/div&gt;
&lt;div&gt;또한, 운영 중인 디비 서버에서 실행 시간이 지나치게 긴 쿼리는 제한 시간 내에 종료되어야 하는 게 정상이다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;MySQL에선 max_execution_time 을 설정해 SELECT 구문의 실행 시간을 제한할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;서비스 뿐만 아니라, 배치 작업 등 여러 환경에서 디비 서버를 사용하고 있기 때문에 글로벌로 설정하는 대신 세션 단위로 설정하기로 했다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;커넥션 풀에서 커넥션을 맺을 때 아래 쿼리를 실행해 시간을 제한했다. 단위는 밀리세컨드이다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; SET SESSION max_execution_time = 5000 &amp;nbsp;&lt;/font&gt;&lt;font color=&quot;#008f00&quot;&gt;-- SELECT 구문을 5초로 제한&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;세션이 아니라 쿼리에서 직접 설정할 수도 있는데, 이땐 아래와 같이 /*+ 로 시작하는 힌트 구문을 적용하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; SELECT &lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;/*+ MAX_EXECUTION_TIME(1000) */&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt; * FROM employee&amp;nbsp;&lt;span style=&quot;color: #008f00;&quot;&gt;-- 실행시간을 1초로 제한&lt;/span&gt;&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;참고로, 시스템 변수를 0으로 설정하면 무제한이지만, 힌트에서 0으로 설정할 경우엔 무제한이 아니라 시스템 변수를 따른다고 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;font color=&quot;#005493&quot;&gt;SELECT /*+ MAX_EXECUTION_TIME&lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;(0)&lt;/font&gt;&lt;font color=&quot;#005493&quot;&gt; */ * FROM employee &lt;/font&gt;&lt;font style=&quot;color: rgb(0, 143, 0);&quot;&gt;-- 시스템 변수를 따름&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;꽤 오래 전부터 종종 디비 서버의 CPU가 100%가 되면서 힘들어하곤 했다.&lt;/div&gt;
&lt;div&gt;여러 원인이 있었고, 해결하기 위해 여러 방법(쿼리 수정, 실행 시간 제한, 리드 리플리케이션 등등)을 적용했고, 지금은 안정화된 것 같아 보인다.&lt;/div&gt;
&lt;div&gt;글쎄- 원인도 복합적이고 해결책도 그랬겠지만, 실행 시간을 제한하기로 한 건 좋은 선택이었던 것 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://stackoverflow.com/questions/415905/how-to-set-a-maximum-execution-time-for-a-mysql-query&quot;&gt;https://stackoverflow.com/questions/415905/how-to-set-a-maximum-execution-time-for-a-mysql-query&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/DB</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/767</guid>
      <comments>https://ohgyun.com/767#entry767comment</comments>
      <pubDate>Fri, 21 Sep 2018 01:14:16 +0900</pubDate>
    </item>
    <item>
      <title>MySQL: FULLTEXT 인덱스와 일반 인덱스 성능 차이</title>
      <link>https://ohgyun.com/766</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.07.26&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; fulltext index, use index, match against&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;FULLTEXT 인덱스를 적용해 둔 테이블에서 날짜와 주소로 조회하려고 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;테이블 크기는 약 1800만 행 정도이고, 컬럼과 인덱스 정보는 아래와 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;employee: 약 1800만 행&lt;/div&gt;
&lt;div&gt;- address: 주소&lt;/div&gt;
&lt;div&gt;- join_date: 입사일&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;인덱스:&lt;/div&gt;
&lt;div&gt;- address: FULLTEXT index&lt;/div&gt;
&lt;div&gt;- join_date: index&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;주소와 날짜로 검색하기 위해 아래와 같이 조회했는데,.. 엄~청 느리다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;SELECT *&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;FROM employee&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;WHERE&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MATCH(address) AGAINST('+서울' IN BOOLEAN MODE) &lt;/font&gt;&lt;font color=&quot;#008f00&quot;&gt;-- (A)&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AND join_date &amp;gt;= '${fromDate}'&lt;/font&gt;&lt;font color=&quot;#008f00&quot;&gt; -- (B)&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AND join_date &amp;lt;= '${now}'&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;왜 그런 걸까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;풀텍스트 인덱스(A 조건)가 날짜 인덱스(B 조건)보다 먼저 적용됐기 때문이다.&lt;/div&gt;
&lt;div&gt;FULLTEXT 인덱스는 전체 테이블을 대상으로 스캔하고, 매칭 결과도 많아 오래 걸린 것이었다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이번 건의 경우, 날짜 조건이 더 좁았기 때문에 USE INDEX로 입사일 인덱스를 사용하게 변경했다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;SELECT *&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;FROM employee &lt;/font&gt;&lt;font color=&quot;#941751&quot;&gt;USE INDEX (join_date)&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;WHERE&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;address LIKE '%서울%'&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AND join_date &amp;gt;= '${fromDate}'&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AND join_date &amp;lt;= '${now}'&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;참고로, FULLTEXT 인덱스는 다른 인덱스와 함께 사용할 수 없다.&lt;/div&gt;
&lt;div&gt;MATCH AGAINST 구문을 인덱스 설정 없이 사용하면 아래와 같은 오류가 발생한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;[HY000][1191] Can't find FULLTEXT index matching the column list&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&amp;nbsp;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;http://jojoldu.tistory.com/243&quot;&gt;http://jojoldu.tistory.com/243&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;http://blog.acronym.co.kr/270&quot;&gt;http://blog.acronym.co.kr/270&lt;/a&gt;&lt;/div&gt;</description>
      <category>Daylogs/DB</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/766</guid>
      <comments>https://ohgyun.com/766#entry766comment</comments>
      <pubDate>Fri, 21 Sep 2018 01:13:50 +0900</pubDate>
    </item>
    <item>
      <title>iOS/macOS 사파리 12에서 Array.prototype.reverse() 버그</title>
      <link>https://ohgyun.com/765</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.09.20&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; reverse, array, iOS, macOS, safari, javascript array bug&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;옆자리 C가 사파리 12버전에서 Array.prototype.reverse() 버그가 발견됐다며 클리앙의 포스트를 공유해줬다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://www.clien.net/service/board/news/12614248?po=0&amp;amp;od=T31&amp;amp;sk=&amp;amp;sv=&amp;amp;category=&amp;amp;groupCd=&amp;amp;articlePeriod=default&amp;amp;pt=0&quot;&gt;https://www.clien.net/service/board/news/12614248?po=0&amp;amp;od=T31&amp;amp;sk=&amp;amp;sv=&amp;amp;category=&amp;amp;groupCd=&amp;amp;articlePeriod=default&amp;amp;pt=0&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;배열을 선언하고 reverse()를 호출한 후에, 페이지를 새로고침 했을 때 해당 변수의 배열이 reverse() 된 체 남아있는 버그다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;에이 설마. 이런 빅버그가...&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;오잉잉. 버그가 맞다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;클리앙 글에 참조되어 있는 스택오버플로우 글에서 폴리필 코드도 제시해주고 있다.&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://stackoverflow.com/questions/52390368/array-state-will-be-cached-in-ios-12-safari-is-it-a-bug-or-feature/52392901#52392901&quot;&gt;https://stackoverflow.com/questions/52390368/array-state-will-be-cached-in-ios-12-safari-is-it-a-bug-or-feature/52392901#52392901&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;서비스에도 적용해야 할까 해서 좀 더 살펴봤다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- 아래 테스트 URL에서 '문제 있음'이 뜨면 버그가 발생하는 것이다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;- 테스트 URL (A) : &lt;a href=&quot;http://output.jsbin.com/yecixixuma/1&quot;&gt;http://output.jsbin.com/yecixixuma/1&lt;/a&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;span&gt;&amp;nbsp; &amp;nbsp; - iOS 크롬이나 인앱 브라우저에서도 모두 발생한다.&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- iOS12 사파리 버전에서 배열 reverse() 시 복사본을 생성하지 않는 게 원인인 것 같은데... 정확한 원인은 모르겠다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; C와 얘기하면서 아무래도 사파리의 최적화와 관계가 있지 않을까 생각했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- 다만, 배열을 선언한 후 동적으로 배열을 변경하는 경우엔 발생하지 않는 문제다.&lt;/div&gt;
&lt;div&gt;
&lt;span&gt;&amp;nbsp; &amp;nbsp; - 문제가 발생했던 환경에서도 아래 URL은 정상적으로 동작할 것이다.&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;- 테스트 URL (B): &lt;a href=&quot;http://output.jsbin.com/linefufuse/1&quot;&gt;http://output.jsbin.com/linefufuse/1&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; - 우리 서비스에서는 모든 케이스에서 배열을 동적으로 할당하기 때문에 적용하지 않기로 했다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;버그 여부를 확인하는 (A)와 (B)의 테스트 코드는 각각 아래와 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;(A)&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;window.addEventListener('load', function () {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function buggy() {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var a = [1, 2];&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return String(a) === String(a.reverse());&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(buggy() ? '문제 있음' : '괜찮음');&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;(B)&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;window.addEventListener('load', function () {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function buggy() {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#941751&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var a = [];&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#941751&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.push(1);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#941751&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a.push(2);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return String(a) === String(a.reverse());&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(buggy() ? '문제 있음' : '괜찮음');&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/Javascript</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/765</guid>
      <comments>https://ohgyun.com/765#entry765comment</comments>
      <pubDate>Thu, 20 Sep 2018 15:06:42 +0900</pubDate>
    </item>
    <item>
      <title>객체에 프로퍼티 설정 시 성능 이슈</title>
      <link>https://ohgyun.com/764</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.09.19&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; json, object, Map&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;배치 작업에서 약 천만 행의 데이터를 로드에 객체의 프로퍼티로 할당하는 작업을 진행하고 있었다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;대략 다음과 같이 DB에서 데이터를 읽어와 메모리에 할당하는 간단한 작업이다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;readFromDatabase().then((result) =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; const map = {};&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; const list = [];&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; result.forEach((item) =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;
&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; map[item.id] = item.value; &lt;/font&gt;&lt;font color=&quot;#008f00&quot;&gt;// (A)&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; list.push(item); &lt;/span&gt;&lt;span style=&quot;color: rgb(0, 143, 0);&quot;&gt;// (B)&lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp; &amp;nbsp; });&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;헌데, 특정 시점(문제가 발생했던 데이터의 경우 약 700만 라인 즈음)부터 (A) 부분의 작업이 1초 이상 걸린다.&lt;/div&gt;
&lt;div&gt;하지만 배열에 추가하는 (B) 코드는 아무 문제가 없었다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;단순히 맵에 할당하는 건데... 왜 이렇게 느려지는 걸까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;건너자리 J에게 이 문제에 대해 얘기했더니, 네이티브 Map 객체를 쓰면 어떠냐고 한다.&lt;/div&gt;
&lt;div&gt;V8에선 객체의 형태가 달라질 때 최적화 작업을 하는데, 이 비용이 꽤 크다고 알고 있다고 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;문제가 됐던 코드는 아래와 같이 변경했다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;readFromDatabase().then((result) =&amp;gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#941751&quot;&gt;&amp;nbsp; &amp;nbsp; const map = new Map();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #005493;&quot;&gt;&amp;nbsp; &amp;nbsp; const list = [];&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; result.forEach((item) =&amp;gt; {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;font color=&quot;#941751&quot;&gt; &amp;nbsp; map.set(item.id, item.value);&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; list.push(item);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;});&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;만세! 잘 된다! ㅎㅎㅎ&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;위 문제를 재현하고 싶다면 아래 코드를 실행하면 된다. (테스트 환경의 node는 10.10 버전)&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;const map = {};&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;let i = 0;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;for (; i &amp;lt; 10000000; i++) {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const key = 'key_' + i; &lt;/span&gt;&lt;font color=&quot;#008f00&quot;&gt;// (A)&lt;/font&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;map[key] = i;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (i &amp;gt; 8388600) {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log(key, process.memoryUsage().heapTotal);&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br style=&quot;color: rgb(0, 84, 147);&quot;&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (i === 8388610) {&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: rgb(0, 84, 147);&quot;&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;결과는 다음과 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 516px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99F93E445BA338CE0E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F93E445BA338CE0E&quot; width=&quot;516&quot; height=&quot;434&quot; filename=&quot;evernote_image_1.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;내 맥에선 8,388,605번째부터 느리게 할당됐고, 실제로 이 시점부터 프로퍼티를 한 번 설정할 때마다 메모리가 크게 (무려 64MB&amp;nbsp;씩)&amp;nbsp;늘어났다.&lt;/div&gt;
&lt;div&gt;참고로, (A) 부분의 key 값을 숫자인 i로 할당하면 전혀 느리지 않고, 메모리가 크게 증가하는 현상도 없다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;정확한 원인이 뭘까 찾아봤는데, 아래 포스팅에서 언급한 내용과 관련이 있지 않을까 싶다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://v8project.blogspot.com/2017/08/fast-properties.html&quot;&gt;https://v8project.blogspot.com/2017/08/fast-properties.html&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;살짝 정리해보면 다음과 같다. (편의 상 영어는 발음되는 대로 표기했다)&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;자바스크립트 객체는 메모리 내에서 아래 그림과 같이 표현된다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 683px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/990F5A3F5BA338CF34&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F990F5A3F5BA338CF34&quot; width=&quot;683&quot; height=&quot;209&quot; filename=&quot;evernote_image_2.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;배열이나 키가 숫자인 객체는 엘리먼트(Elements)로, 키가 문자열인 객체는 네임드 프로퍼티(Named Properties)로 저장된다.&lt;/div&gt;
&lt;div&gt;엘리먼트는 엘리먼트 스토어(elements store)에, 네임드 프로퍼티는 프로퍼티 스토어(properties store)에 저장된다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;V8의 모든 자바스크립트 객체는 객체의 형태에 대한 정보가 있는 히든 클래스(HiddenClass)를 갖고 있다.&lt;/div&gt;
&lt;div&gt;
&lt;span&gt;히든 클래스에는, &lt;/span&gt;프로퍼티의 이름과 인덱스에 대한 정보, 프로퍼티의 개수, 객체 프로토타입의 참조 등이 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;자바스크립트와 같은 프토토타입 기반의 언어는 객체의 타입을 미리 알 수 없기 때문에, V8은 객체가 업데이트 될 때마다 히든 클래스의 정보를 업데이트한다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;히든 클래스는 객체의 형태에 대한 식별자 역할을 하고, 인라인 캐시나 컴파일러 최적화에 아주 중요한 요소로 사용된다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;V8은 객체에 새 프로퍼티가 생성될 때마다 히든 클래스를 생성한다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;프로퍼티의 구조와 추가된 순서가 같다면, 객체의 히든 클래스도 동일하다. V8에서도 동일한 구조일 땐 객체간 히든 클래스를 공유한다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;동일한 구조이더라도 순서가 다를 땐, 별도의 히든 클래스가 새로 생성된다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;참고로, &amp;nbsp;{ a: 1, b: 2 } 같이 네임트 프로퍼티를 가진 객체는, 외부에서 보기엔 딕셔너리 같지만 V8에선 인라인 캐시나 최적화를 위해 딕셔너리처럼 사용하지 않는다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;네임드&amp;nbsp;프로퍼티에는 3가지 타입이 있다.&lt;/div&gt;
&lt;div&gt;In-object, Fast, Slow/dictionary&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- In-object 타입은 V8에서 사용할 수 있는 가장 빠른 프로퍼티로, 미리 지정된 개수만큼 객체에 '직접' 할당된다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; 허용된 개수보다 더 많은 프로퍼티가 저장되면, 별도의 프로퍼티 스토어에 저장된다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; 프로퍼티 스토어는 한 단계 간접 참조가 있지만, 개별적으로 확장될 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- Fast 타입은 프로퍼티 스토어에 있고, 모든 메타 정보가 히든 클래스의 디스크립터 배열(discriptor array)에 저장된다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;일반적으로 선형 프로퍼티 스토어(linear properties store)에 저장된 프로퍼티를 빠르다고 정의한다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; Fast 프로퍼티는 프로퍼티 저장소의 인덱스를 사용해 간단히 액세스할 수 있다. 프로퍼티 이름으로 프로퍼티 저장소의 실제 위치로 이동하려면 히든 클래스의 디스크립터 배열을 참조해야 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;- Slow 타입은 히든 클래스 외부의 자체(self-contained) 프로퍼티 사전을 갖는다. 메타 정보는 더이상 히든 클래스에 저장되지 않는다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; 만약 객체에 많은 프로퍼티가 추가되거나 삭제되면, 디스크립터 배열과 히든 클래스를 유지하기 위해 많은 시간과 메모리가 필요하다.&amp;nbsp;V8이 slow 프로퍼티를 지원하는 이유이다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; Slow 프로퍼티는 개별적으로 메타 정보를 저장하는 사전을 갖지 때문에, 객체를 추가하거나 삭제할 때 히든 클래스가 업데이트 되지 않으며 인라인 캐시에서도 일반적으로 빠른 프로퍼티보다 느리다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; 즉, slow 프로퍼티는 객체의 추가/삭제는 빠르지만 다른 프로퍼티에 비해 접근이 느리다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://stackoverflow.com/questions/43594092/slow-delete-of-object-properties-in-js-in-v8&quot;&gt;https://stackoverflow.com/questions/43594092/slow-delete-of-object-properties-in-js-in-v8&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;위 문제랑 좀 다르긴 한데, 비슷하게 객체 삭제가 특정 시점부터 느려진다는 글도 있다.&lt;/div&gt;
&lt;div&gt;V8 개발자의 답변으로는 flat array 형태로 되어 있던 메타 정보를 사전 형태로 바꿀지 여부를 확인하는 과정에서 발생하는 known issue란다.&lt;/div&gt;
&lt;div&gt;위 문제랑은 조금 다른 문제이긴 하다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;----&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;글쎄... 정확한 원인이 뭔진 잘 모르겠다.&lt;/div&gt;
&lt;div&gt;어쨌든, 느려지는 이유는 메모리의 급격한 상승 때문인 것 같다.&lt;/div&gt;
&lt;div&gt;첫 번째 블로그 글을 읽고, Fast 프로퍼티가 Slow 프로퍼티로 전환되는 과정에서 그런 걸까하고 생각했는데, 느려도 너무 느려서..ㅠㅠ&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;여튼, 이런 케이스엔 Map()을 사용하자.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/Javascript</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/764</guid>
      <comments>https://ohgyun.com/764#entry764comment</comments>
      <pubDate>Thu, 20 Sep 2018 15:06:21 +0900</pubDate>
    </item>
    <item>
      <title>Charlse Proxy iOS의 프록시 자동 옵션으로 연결하기</title>
      <link>https://ohgyun.com/763</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2018.07.27&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; Chalse Proxy, iOS, proxy, 찰스 프록시, iOS proxy auto configuration&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;맥에서 HTTP 프록싱 도구로 &lt;a href=&quot;https://www.charlesproxy.com/&quot;&gt;찰스 프록시&lt;/a&gt;를 사용하고 있다.&lt;/div&gt;
&lt;div&gt;패킷 디버깅이 필요할 때마다 와이파이 설정에서 프록시를 수동으로 연결하고 있는데 귀찮고 번거롭다.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;자동으로 연결할 수 없을까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;iOS는 프록시 설정에 자동 옵션이 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;프록시를 설정할 때 수동으로 설정해서 IP와 포트를 지정하는 대신, &lt;b&gt;자동 &lt;/b&gt;옵션을 선택한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; 설정 &amp;gt; Wi-Fi &amp;gt; 프록시 구성 &amp;gt; 자동&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;URL을 입력칸에 아래와 같은 형태의 주소를 넣는다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; https://chls.pro/&lt;font color=&quot;#941751&quot;&gt;X.X.X.X:XXXX&lt;/font&gt;.pac&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;X.X.X.X:XXXX 는 본인 아이피:포트이다. (예: 192.168.1.92:55555)&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이렇게 설정해두면, 찰스가 켜져있을 땐 프록시로 동작하고 꺼진 경우엔 기본으로 동작한다.&lt;/div&gt;
&lt;div&gt;편하다!&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;논의:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;프록시 설정에서 SOCKS 모드를 설정해두고 SOCKS 모드로 연결하면, 프록시가 연결된 상태에서도 HTTP2 스펙으로 연결할 수 있다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;Proxy Setting 메뉴에서 아래와 같이 SOCKS 모드를 켜고,&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;p style=&quot;text-align: center; clear: none; float: none;&quot;&gt;&lt;span class=&quot;imageblock&quot; style=&quot;display: inline-block; width: 683px;  height: auto; max-width: 100%;&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99B5CE435B5AB37F15&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99B5CE435B5AB37F15&quot; width=&quot;683&quot; height=&quot;583&quot; filename=&quot;evernote_image_1.png&quot; filemime=&quot;image/png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;iOS의 프록시 &amp;gt; 자동 &amp;gt; &lt;a href=&quot;https://chls.pro/X.X.X.X:XXXX.socks.pac&quot;&gt;https://chls.pro/X.X.X.X:XXXX.socks.pac&lt;/a&gt;&amp;nbsp;으로 연결하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;# 2019.07.18 추가&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;iOS 앱에서 SSL 접속이 안되는 문제&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;iOS에서 찰스 인증서를 설치해도&amp;nbsp;브라우저에서는 SSL 접속이 되는데&amp;nbsp;앱에서는 안된다면, 인증서가 루트 권한에 적용되어 있는지 확인해보면 된다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;아이폰 설정 &amp;gt; 일반 &amp;gt; 정보 &amp;gt; 인증서 신뢰 설정&amp;nbsp;메뉴에서 찰스 인증서의 루트 인증서 전체 신뢰 활성화를 켜주면 된다.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://www.charlesproxy.com/documentation/configuration/proxy-settings/&quot;&gt;https://www.charlesproxy.com/documentation/configuration/proxy-settings/&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>Daylogs/DevTip</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/763</guid>
      <comments>https://ohgyun.com/763#entry763comment</comments>
      <pubDate>Fri, 27 Jul 2018 14:54:25 +0900</pubDate>
    </item>
    <item>
      <title>Node.js: 스트림의 'drain' 이벤트</title>
      <link>https://ohgyun.com/762</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2017.01.09&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; Readline, WriteStream, ReadStream, highWaterMark&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;한 행에 JSON 포맷의 데이터를 포함한 파일을 CSV 파일로 변환하려고 한다.&lt;/div&gt;
&lt;div&gt;파일 사이즈가 커서 readline을 사용해 스트림으로 읽어 변환 후 다시 스트림으로 쓸 생각이다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;WriteStream에 'drain' 이벤트가 있던데, 정확히 어떤 이벤트일까?&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;highWaterMark(내부 버퍼에 저장할 최대 바이트)에 지정한 값보다 큰 버퍼를 쓰려고 할 땐, write()가 false를 리턴한다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;스트림이 빠져나가는 동안 write()를 호출하면, 청크가 버퍼링되고 false가 리턴된다.&lt;/div&gt;
&lt;div&gt;현재 버퍼링된 모든 청크가 빠져나가면 'drain' 이벤트가 발생한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;write()가 false를 리턴했을 땐, drain 이벤트가 발생하기 전까지 write()를 호출하지 않는 것이 좋다.&lt;/div&gt;
&lt;div&gt;스트림이 빠져나가지 않은 상태에서 write()가 계속 발생되면, node.js는 최대 메모리 에러가 발생할 때까지 쓰려는 데이터를 모두 버퍼링하기 때문이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;이렇게 되면 불필요하게 GC가 동작할 수 있고, 심지어 메모리가 다시 반환되지 않는 문제가 발생할 가능성도 있다고 한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;아래는 변환하는 코드의 일부이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;function convertToCsvFile(options) {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const deferred = Q.defer();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const inputFilePath = options.inputFilePath;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const outputFilePath = options.outputFilePath || (inputFilePath + '.csv');&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const headers = options.headers;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const keyNames = options.keyNames || [];&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const filter = options.filter;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const transform = options.transform;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!inputFilePath) {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return Q.rejecct('inputFilePath는 필수 값입니다');&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const ws = fs.createWriteStream(outputFilePath, {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flags: 'w',&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;defaultEncoding: 'utf8'&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.on('open', () =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (headers) {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.write(headers.join(',') + '\n');&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const rl = readline.createInterface({&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;input: fs.createReadStream(inputFilePath)&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rl.on('line', (line) =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let data = JSON.parse(line);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!_.isFunction(filter) || filter(data)) { // 필터가 정의되어 있다면 사용한다.&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (_.isFunction(transform)) {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data = transform(data);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_writeToStream(ws, data, keyNames);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rl.on('close', () =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.end();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.on('finish', () =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.close();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.on('close', () =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;deferred.resolve(outputFilePath);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return deferred.promise;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;}&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;function _writeToStream(ws, data, keyNames) {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const values = [];&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let shouldDrain = false;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_.each(keyNames, (keyName) =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let value;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;value = _getValueByKeyName(data, keyName);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} catch (e) {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.error('값을 가져오는데 실패했습니다');&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log('Data:', data);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log('Key Name:', keyName);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw e;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;values.push(value);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shouldDrain = ws.write(values.join(',') + '\n') === false;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (shouldDrain) {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ws.once('drain', () =&amp;gt; {&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_writeToStream(ws, data, keyNames);&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font color=&quot;#005493&quot;&gt;}&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;참고:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;http://stackoverflow.com/questions/35532332/how-does-readline-rl-write-work&quot;&gt;http://stackoverflow.com/questions/35532332/how-does-readline-rl-write-work&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/Javascript</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/762</guid>
      <comments>https://ohgyun.com/762#entry762comment</comments>
      <pubDate>Sat, 5 Aug 2017 12:57:32 +0900</pubDate>
    </item>
    <item>
      <title>ML: MS의 Matchbox Recommender 이해하기</title>
      <link>https://ohgyun.com/761</link>
      <description>&lt;div&gt;
&lt;b&gt;발생일:&lt;/b&gt; 2017.01.07&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;키워드:&lt;/b&gt; Microsoft Matchbox Recommender, Matchbox Recommendation, Recommendation system, 마이크로소프트 매치박스, Azure Machine Learning Studio, 애저 머신러닝 스튜디오, 매치박스 알고리즘, 추천 알고리즘&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;문제:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;Matchbox Recommender는 MS Azure의 머신러닝 스튜디오에서 제공하는 추천 시스템이다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;이번에 MS의 제품을 활용해 추천 시스템을 구축하기 전에 정리한 내용이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;해결책:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;추천 시스템은 한 개 이상의 아이템이나 유저를 추천해주는 시스템.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;두 가지 접근 방식이 있다.&lt;/div&gt;
&lt;div&gt;하나는 content-based, 다른 하나는 collaborative filtering&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;content-based:&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;- 유저와 아이템의 피처를 기반으로 추천한다&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;collaborative filtering:&lt;/div&gt;
&lt;div&gt;- 모든 유저와 아이템의 점수를 매트릭스로 만든 데이터를 기반으로 한다&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;Matchbox recommender는 두 가지 방식을 모두 사용하는 하이브리드 리커멘더이다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;신규 사용자처럼 히스토리가 없는 경우, 아이템과 유저의 피처에 기반에 추천하는 방식으로 콜드 스타트 이슈를 해결할 수 있다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;시간이 지나 히스토리가 쌓이면 content-based 에서 collaborative filtering 방식으로 자연스럽게 전환할 수 있는 알고리즘이다.&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;Predict Ratings&lt;/div&gt;
&lt;div&gt;- user features 와 item features를 옵션으로 추가할 수 있다.&lt;/div&gt;
&lt;div&gt;- 하지만, 이미 평가했던 사용자나, 이미 평가되었던 아이템의 피처들은 스코어를 매길 때는 사용되지 않는다. 이 피처들은 콜드 스타트 사용자와 아이템을 위한 것이기 때문이다.&lt;/div&gt;
&lt;div&gt;- user-item-rating 데이터를 응답한다&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;Recommend Items&lt;/div&gt;
&lt;div&gt;- 사용자에게 1개 이상의 아이템을 추천한다.&lt;/div&gt;
&lt;div&gt;- Recommend item selection&lt;/div&gt;
&lt;div&gt;- From rated items: 개발 단계에서 모델 평가용으로 사용한다. 평가할 때 이미 평가가 됐던 아이템들을 대상으로 추천한다&lt;/div&gt;
&lt;div&gt;- From all items: 웹 서비스나 프로덕션에서 사용할 때 선택한다.&lt;/div&gt;
&lt;div&gt;- Dataset to score&lt;/div&gt;
&lt;div&gt;- From all items 라면 컬럼 한 개만 넣어야 한다. 사용자 아이디다.&lt;/div&gt;
&lt;div&gt;- From rated items 라면, user-item 페어 데이터를 전달한다.&lt;/div&gt;
&lt;div&gt;- 유저와 사용자의 피처 데이터는 동일한 방법으로 사용된다.&lt;/div&gt;
&lt;div&gt;- Maxium number of items to recommend to a user: 추천할 아이템의 최대 개수&lt;/div&gt;
&lt;div&gt;- Minimum size of the recommendation pool per user: 평가 단계에서, 최소 몇 개 이상 평가했던 사용자들 대상으로 할 것인지 여부&lt;/div&gt;
&lt;div&gt;- user, item1, item2, ..., itemn 컬럼을 응답한다&lt;/div&gt;
&lt;div&gt;- item1이 가장 연관성이 높은 데이터이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;Find Related User&lt;/div&gt;
&lt;div&gt;- 비슷한 사용자를 추천하는 모듈이다. &quot;people like you&quot;에 대한 추천이다.&lt;/div&gt;
&lt;div&gt;- Recommender prediction kind 를 Related users 로 선택&lt;/div&gt;
&lt;div&gt;- From all users, from users that rated items 는 동일한 방법으로 동작&lt;/div&gt;
&lt;div&gt;- 인풋으로는 user 아이디 하나만 받는다. 평가용이라면 user-item 페어 데이터를 받는다.&lt;/div&gt;
&lt;div&gt;- 비슷한 사용자 아이디를 응답한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;Predict related items&lt;/div&gt;
&lt;div&gt;- 아이템 아이디를 받아 연관된 아이템을 추천한다.&lt;/div&gt;
&lt;div&gt;- 인풋으로 item 아이디를 넘긴다. 평가용이라면 user-item 페어를 넘긴다.&lt;/div&gt;
&lt;div&gt;- 연관된 아이템 아이디를 반환한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;신규 유저에 대한 추천&lt;/div&gt;
&lt;div&gt;- 아이디가 없을 수도 있고, 평가 히스토리도 없을 것이다.&lt;/div&gt;
&lt;div&gt;- 이 때엔 사용자 피처로 추천한다.&lt;/div&gt;
&lt;div&gt;- 인풋은 트레이닝에 사용되지 않았던 아이디를 넘긴다. 단, 사용자 피처 데이터가 있어야 한다.&lt;/div&gt;
&lt;div&gt;- 만약 협업 필터링을 위한 데이터가 없다면(유저 피처), classification 이나 regression 알고리즘을 사용하는 것이 낫다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;사용자가 이미 봤던 것을 제외하고 싶다면, 요청을 여러 번 보낸 후에 직접 필터링해서 사용하면 된다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;현재는 온라인 업데이트는 제공하지 않는다.&lt;/div&gt;
&lt;div&gt;증가분을 추가로 트레이닝하는 건 불가능하다. 전체를 다시 트레이닝 해야한다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;Documentation:&amp;nbsp;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/azure/dn905970.aspx&quot;&gt;https://msdn.microsoft.com/en-us/library/azure/dn905970.aspx&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;Matchbox Recommender 논문:&amp;nbsp;&lt;a href=&quot;http://www2009.eprints.org/12/1/p111.pdf&quot;&gt;http://www2009.eprints.org/12/1/p111.pdf&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description>
      <category>Daylogs/AI</category>
      <author>ohgyun</author>
      <guid isPermaLink="true">https://ohgyun.com/761</guid>
      <comments>https://ohgyun.com/761#entry761comment</comments>
      <pubDate>Fri, 28 Jul 2017 09:58:27 +0900</pubDate>
    </item>
  </channel>
</rss>