<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>azi_zero.log</title>
        <link>https://velog.io/</link>
        <description>잘 하고 싶은 욕심을 가득 갖고 태어남</description>
        <lastBuildDate>Thu, 26 Jan 2023 13:34:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. azi_zero.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/azi_zero" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[아녜스의 얼굴이었던 그 모든 순간들]]></title>
            <link>https://velog.io/@azi_zero/%EC%95%84%EB%85%9C%EC%8A%A4%EC%9D%98-%EC%96%BC%EA%B5%B4%EC%9D%B4%EC%97%88%EB%8D%98-%EA%B7%B8-%EB%AA%A8%EB%93%A0-%EC%88%9C%EA%B0%84%EB%93%A4</link>
            <guid>https://velog.io/@azi_zero/%EC%95%84%EB%85%9C%EC%8A%A4%EC%9D%98-%EC%96%BC%EA%B5%B4%EC%9D%B4%EC%97%88%EB%8D%98-%EA%B7%B8-%EB%AA%A8%EB%93%A0-%EC%88%9C%EA%B0%84%EB%93%A4</guid>
            <pubDate>Thu, 26 Jan 2023 13:34:00 GMT</pubDate>
            <description><![CDATA[<p><em>2023.01.26</em></p>
<p>나도 꽤나 지성인일거라 생각했는데,
되돌아보니 오늘만큼 악의 없이 책을 즐긴 적은 없었던 것 같다.</p>
<p>밀란 쿤데라의 불멸, 1부를 읽었다.</p>
<p>글들을 보면서 느낀 점은, 장면 전환이 잦은데 그 전환점이 너무나도 이해가 된다는 것이었다.  </p>
<p>순식간에 참치형 몸을 가진 여인을 생각해내게 만들고,
그 아녜스의 손짓으로 아녜스는 내 지인이 되어버린다.
순식간에 아녜스의 가족사는 카페에 앉아 듣는 수다 마냥 턱을 괴고 몰입하게 되었다.</p>
<p>1부의 제목은 &#39;얼굴&#39;이다.
얼굴이란 내가 내가 되는 것이 라고 작가는 말했다.
그런 의미에서 1부의 순간들은 아녜스의 얼굴이었던 것 같다.</p>
<p>아녜스의 얼굴은 순수한 고독 속에서 진정한 개인적인 것을 찾아낸다.</p>
<hr>
<p>아주 인상적인 구절이 많았다.
나에게 흘러들어왔던 생각들이 이렇게 멋진 통찰력으로 다시 표현된다는게 정말 벅찼다.
<br></p>
<p>요즘에, 아주 가까운 사람을 지켜보면서 그 사람의 아주 사소한 무언가에 뒤섞인 시선을 만들어질 때가 가끔 있었다</p>
<pre><code>이런 식으로 우리는 우리 자신의 일부를 통해서 시간을 초월하여 살기도 한다.
어쩌면 우리는 대부분의 시간을 나이 없이 살면서, 
어떤 이례적인 순간들에만 나이를 의식하는 것이라.</code></pre><br>
치열하게 살다가 지칠 때, 사람 사는 건 다 똑같다고 느끼곤 했다.

<pre><code>얼굴은 단지 어떤 견본품의 일련번호일 뿐이다.
...
삶과 죽음에 대한 열정적인 동화가 필요했다.
그렇게 해야만 우리가 우리 자신의 눈에 인간 원형의 단순한 한 변이체로 비치지않고,
상호 교환이 불가능한 고유의 본질을 지닌 존재로 보이기 때문이다.</code></pre><br>
나에게 무례함을 보여주는 사람을 보면 쳐다도 보기 싫고 말도 하기 싫어진다.

<pre><code>증오의 올가미는 우리를 너무나 긴밀하게 증오 대상에 옭아맨다. 
...
서로 상대의 눈을 똑바로 쳐다보면서 상대의 몸을 꿰뚫는 두 병정의 외설스러운 친밀함.
아버지는 바로 그런 친밀함이 싫었다는 것을.</code></pre><hr>
<p>뭔가 자세하게 적고 싶은데,
내 생각을 포장하는게 여간 어려운 일이 아니다.</p>
<p>나는 이정도만 적어도 내 생각이 머릿속에 가득 들어오니 오늘은 여기서 만족해야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS-Coroutine 분해하기]]></title>
            <link>https://velog.io/@azi_zero/JS-Coroutine-%EB%B6%84%ED%95%B4%ED%95%98%EA%B8%B0-g5tptz3k</link>
            <guid>https://velog.io/@azi_zero/JS-Coroutine-%EB%B6%84%ED%95%B4%ED%95%98%EA%B8%B0-g5tptz3k</guid>
            <pubDate>Tue, 23 Nov 2021 08:24:59 GMT</pubDate>
            <description><![CDATA[<p>원글은 Notion에서 작성되었습니다.
<a href="https://simey1128.notion.site/js-coroutine-65ca212720af4cd4af500a927995c3f8">📌 원글 보러가기</a></p>
<p>해당 글은 본인의 개념 정리 용으로 작성하였으므로 불친절한 전달이라도 이해 부탁드립니다!</p>
<h1 id="🌟-coroutine-이해하기">🌟 Coroutine 이해하기</h1>
<h2 id="헷갈릴-수-있는-concepts">헷갈릴 수 있는 concepts</h2>
<h3 id="process--thread">Process &amp; Thread</h3>
<p>process는 Heap을 사용하며, thread는 process안에서 stack을 사용한다.
<img src="https://images.velog.io/images/azi_zero/post/bafb6129-2c68-4c23-b6de-c3b207869edf/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2021-11-17_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_5.26.26.png" alt=""></p>
<h3 id="concurrency--parallelism">Concurrency &amp; Parallelism</h3>
<p>동시성과 병렬성의 개념을 명확히! </p>
<p><strong><span style="background-color: #FFE8E9">Concurrency ( 동시성 )</span></strong></p>
<blockquote>
<p><strong>interleaving</strong>, 다수의 task들에 대해서 각각을 <strong>쪼개어서 조금씩 빠르게 실행</strong>하여 
<strong>전체로 보았을때는 동시에 실행되고 있는 것처럼</strong> 보이도록 실행하는 것.</p>
<p><img src="https://images.velog.io/images/azi_zero/post/84b3e475-8bc4-44ec-9140-9981e74ce207/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2021-11-17_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_5.28.40.png" alt=""></p>
</blockquote>
<p><strong><span style="background-color:#FFE8E9">Parallelism ( 병렬성 )</span></strong></p>
<blockquote>
<p>parallelizing, 다수의 task들이 한번에 수행되는 것</p>
<p><img src="https://images.velog.io/images/azi_zero/post/f86d11fe-0443-48ee-aeda-ffeabc7410c9/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2021-11-17_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_5.30.11.png" alt=""></p>
</blockquote>
<h3 id="thread--coroutine">Thread &amp; Coroutine</h3>
<p>두 개념 모두 Concurrency를 보장하기 위한 기술이다.</p>
<p><strong><span style="background-color:#E2F1F7">Thread - Thread의 효율성은 OS의 몫이다.</span></strong></p>
<ul>
<li><p><strong>task의 단위 = Thread</strong></p>
<ul>
<li>다수의 작업 각각에 Thread를 할당함 .</li>
<li>task는 stack 영역을 차지함.</li>
</ul>
</li>
<li><p><strong>OS level에서의 작업이 빈번함</strong></p>
<ul>
<li>thread 하나가 task 하나를 담당하고 있기 때문에, task를 어떻게 쪼갤지(thread를 어떻게 쪼개서 사용할지)를 OS level에서 정해줘야한다, <strong>Preempting Scheduling</strong>.</li>
<li>thread를 이동하며 concurrency를 성취하기 때문에, OS kernel을 이용한 <strong>Context Switching</strong>이 자주 발생한다.</li>
</ul>
</li>
<li><p><strong>Blocking 발생시, thread자체의 block을 의미함.</strong></p>
<p>  하나의 task를 blocking하고 다른 task가 수행되기까지 기다려야한다는 것은,</p>
<p>  하나의 thread의 사용을 중지하고 새로운 thread를 운영하는 것과 같은 의미이다.</p>
</li>
</ul>
<p><img src="https://images.velog.io/images/azi_zero/post/205e190e-7c1f-49fb-97aa-d6eb27fe9d86/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2021-11-17_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_5.45.59.png" alt=""></p>
<p><strong><span style="background-color: #E2F1F7">Coroutine - Coroutine의 효율성은 프로그래머의 몫이다.</span></strong></p>
<ul>
<li><strong>task의 단위 = Object</strong><ul>
<li>다수의 작업 각각에 Object를 할당함.</li>
<li>task는 heap의 영역을 차지함</li>
</ul>
</li>
<li><strong>OS level에서의 작업은 선택사항</strong><ul>
<li>thread는 heap영역의 Object를 처리하는 도구라고 보며, task를 어떻게 쪼갤지는 <strong>programmer가 정하면 된다.</strong></li>
<li>서로 다른 object(task)가 단일 thread에서 수행된다면, concurrency 성취를 위한 task전환은 단일 thread에서 벌어지기 때문에, context switching은 발생하지 않는다.</li>
</ul>
</li>
</ul>
<p><a href="https://aaronryu.github.io/2019/05/27/coroutine-and-thread/">출처 | Coroutine, Thread 와의 차이와 그 특징</a></p>
<h3 id="js에서의-coroutine">JS에서의 Coroutine</h3>
<p>js에서는 generator와 yield를 이용해서 coroutine을 수행한다.</p>
<ul>
<li><span style="background-color:#E2F1F7"><strong>Generator</strong>와 <strong>Yield</strong></span><br>Generator : 함수가 특정 지점(yield)에서 끝나고 다음 실행 때(Generator.next 호출)에는 끝난 시점에서 다시 시작하게 하는 것
Generator 함수를 호출한다 = iterator를 지정한다. 
  yield = return과 비슷</li>
</ul>
<ol>
<li><code>yield - generator</code>
순수한 yield는 상수를 취급하는게 맞다.      <pre><code class="language-jsx">function* call() {
 console.log(&#39;first call&#39;);
 yield 10;
    console.log(&#39;second call&#39;);
     yield 20;
     console.log(&#39;third call&#39;);
 yield 30;
}
</code></pre>
</li>
</ol>
<p>let gen = call(); // iterator를 지정한다. standby상태
console.log(gen.next()); //첫번째 yield까지 수행한다. 
console.log(gen.next());
console.log(gen.next()); // 아직 call함수의 return이 시행되지 않았기에 done은 false
console.log(gen.next()); // yield값은 없지만 함수는 return되었으므로, done은 true</p>
<pre><code>```javascript
first call
{ value: 10, done: false }
second call
{ value: 20, done: false }
third call
{ value: 30, done: false }
{ value: undefined, done: true }
</code></pre><ol start="2">
<li><code>yield* - generator</code> </li>
</ol>
<p><strong>iterator가 iterator를 호출하는 경우에는 yield*사용해준다.</strong></p>
<p>yield값이 generator인 경우에 **generator의 의미로서,<br>해당 generator의 yield를 수행한다.</p>
<pre><code class="language-jsx">function* func(){
    yield 42;
}

function* option1(){
    yield func(); // func의 iterator를 yield한다.
}
function* option2(){
    yield func; // func함수 자체를 yield한다.
}
function* option3(){ 
    yield* func(); // func함수의 iterator를 수행하여 func의 yield까지 수행한다.
}

console.log(&#39;option 1: &#39;, option1().next());
console.log(&#39;option 2: &#39;, option2().next());
console.log(&#39;option 3: &#39;, option3().next());</code></pre>
<pre><code class="language-bash">option 1:  { value: Object [Generator] {}, done: false }
option 2:  { value: [GeneratorFunction: func1], done: false }
option 3:  { value: 42, done: false }</code></pre>
<ol start="3">
<li><p><code>yield - 취급값 없음</code></p>
<p> ...</p>
</li>
</ol>
<p><a href="https://kamang-it.tistory.com/entry/JavaScript-16Generator%EC%99%80-Yield">출처 | [JavaScript-16]Generator와 Yield</a></p>
<hr>
<hr>
<h1 id="☄️-js-coroutine에-관하여">☄️ JS-coroutine에 관하여,</h1>
<h2 id="3가지-핵심-functionality">3가지 핵심 Functionality</h2>
<h3 id="span-stylecolor-dc6822process-in-parallelspan"><span style="color: #DC6822">Process in parallel</span></h3>
<p>보통, huge process들을 처리하기 위해서 worker thread가 사용된다. worker thread를 이용해서 데이터가 이동하는 것은 결함을 야기할 수 있다.</p>
<h3 id="span-stylebackground-colorf8ecdfjs-coroutine의-context-switching-대안책span"><span style="background-color:#F8ECDF"><strong>js-coroutine의 context switching 대안책</strong></span></h3>
<p>Collaborative multitasking를 통해 <strong>main thread를 sharing</strong>한다. (coroutine 개념, thread쪼개기)</p>
<ul>
<li>async 함수 안에서 js-coroutines의 모듈을 이용해서 사용가능하다</li>
<li>js-coroutines는 해당 코드를 main thread의 <strong>idle time(CPU가 쉬는 시간)에 처리</strong>한다.</li>
<li>해당 작업을 제외한 나머지를 다 분배하고 난 후 <strong>남은 시간에 huge process를 실행</strong>한다.</li>
</ul>
<p>⇒ <strong>Coroutine에 우선 순위</strong>를 두는 것! huge process는 후순위가 됨</p>
<p>⇒ Context switching은 일어나지 않음! <strong>main Thread만 사용</strong></p>
<h3 id="span-stylecolor-dc6822imperative-animation-명령형-animationspan"><span style="color: #DC6822">Imperative Animation (명령형 animation)</span></h3>
<p>*imperative; 어떻게 동작하는지에 관한 statements에 초점을 둔다. (C, C++, JAVA ...)</p>
<p>*declarative; 내부 동작 원리 보다는 논리 자체에 초점을 둔다. (HTML, CSS, ...)</p>
<ul>
<li><p>ex) 로봇을 옆건물까지 보내야할 때</p>
<p>  —&gt; imperative : 문 밖을 나간다 - 몇층인지 확인한다 - ... -옆 건물로 이동한다.</p>
<p>  —&gt; declarative : 옆건물로 간다.</p>
</li>
</ul>
<h4 id="css로-animation구현하기">CSS로 animation구현하기</h4>
<p>css declarative 언어이기 때문에(loop나 이런거 못씀) </p>
<p><strong>requestAnimationFrame</strong>을 사용해서 multiple frame을 사용할 수는 있지만, 점점 코드는 난해해져간다.</p>
<h4 id="span-stylebackground-colorf8ecdfjs-coroutine의-declarative-animation-대안책span"><span style="background-color:#F8ECDF"><strong>js-coroutine의 declarative animation 대안책</strong></span></h4>
<p><strong>generator function</strong>을 이용해서 복잡한 애니메이션 논리가 구성되는 다음 frame을 기다려야할 때, 그 시간을 채울 수 있도록 할 수 있다!</p>
<h3 id="span-stylecolor-dc6822create-own-coroutines-in-idle-timespan"><span style="color: #DC6822">Create Own Coroutines in Idle Time</span></h3>
<p>js-coroutines를 사용하면, generator function을 이용해서 <strong>collaborative multitasking</strong>을 수행가능하다.</p>
<p>어쨌든 generator를 사용해서 iterator를 만드는 것은 coroutine을 이용하겠다는 의미!</p>
<ul>
<li><p>js-coroutines의 controlling time check ( 특정 frame이 처리되기 위한 시간을 조절하는 법 )</p>
<ul>
<li><p><span style="background-color: #E9F3F7;"><strong>yield</strong></span></p>
<p>  standard time check, 내부적으로 idle time이 대략 충분하게 남아있는지 확인하는 작업</p>
<p>  js의 yield와 의미는 비슷하지만 사용법은 다르다</p>
</li>
<li><p><span style="background-color: #E9F3F7;"><strong>yield n</strong></span></p>
<p>  특정 frame을 idle time에서 처리하기 위한 <strong>n m/s가 남아있는지</strong> 확인</p>
</li>
<li><p><span style="background-color: #E9F3F7;"><strong>yield true</strong></span></p>
<p>  current work는 this frame을 중단하고, next idle에서 처리하도록!</p>
</li>
<li><p><span style="background-color: #E9F3F7;"><strong>result = yield new Promise(...)</strong></span></p>
<p>  yield any promises이며, coroutine은 promises가 완료될때 까지 suspended되고 완료 되고 return을 받은 후 coroutine은 다시 시작된다.</p>
</li>
<li><p><span style="background-color: #E9F3F7;"><strong>result = yield * subFunction()</strong></span></p>
<p>  또다른 coroutine을 수행하지만, 해당 루틴의 결과값으로 소통할 필요가 없을때 사용</p>
</li>
</ul>
</li>
<li><p>js-coroutine은 어쨌든 generator.next()를 <span style="color: #DC6822"><strong>내부적으로 idle타임에서 수행</strong></span>해준다!</p>
</li>
</ul>
<pre><code>사용자가 할 일은, 그냥 load가 큰 함수를 넣어주고, yield할 위치만 정하면 됨!</code></pre><h2 id="coroutine을-어떻게-생성할까">coroutine을 어떻게 생성할까?</h2>
<p>coroutine은 <strong>main thread</strong>의 logical processing state machine이다.</p>
<p>👩‍💻 <strong>코드 분석</strong></p>
<p><strong>skeleton code</strong></p>
<blockquote>
<p><span style="background-color: #FAF2DD;">Promise와 requestIdleCallback이 main concept</span></p>
<pre><code class="language-tsx">export async function run(coroutine, loopWhileMsRemains=1, timeout){};</code></pre>
<p><strong><code>coroutine</code></strong></p>
<p>generator | Iterator</p>
<p>coroutine으로서 수행될 task를 의미한다.</p>
<p><strong><code>loopWhileMsRemains</code></strong></p>
<p>current frame의 idle time이 해당 시간보다 적으면 next idle frame에서 coroutine이 수행됨</p>
<p><strong><code>timeout</code></strong></p>
<p>system이 Idle time에 있지 않다면, task를 수행할 시간. </p>
<pre><code class="language-tsx">let terminated = false;
let resolver = null;</code></pre>
<p>*<em><code>terminated</code> *</em></p>
<p>task의 종료 여부를 결정하는 변수</p>
<p>*<em><code>resolver</code> *</em></p>
<p>task의 반환 함수(resolve)를 담는 변수</p>
<pre><code class="language-tsx">const result = new Promise(function(resolve, reject){});</code></pre>
<p><strong><code>promise</code></strong></p>
<p>promise를 통해 비동기로 수행되는 작업의 마지막 end point를 알 수 있게 해준다.</p>
<pre><code class="language-tsx">result.terminate = function(result) {
    terminated = true;
    if(resolver){
        resolver(result)
    }
}</code></pre>
<p>수행중이던 coroutine을 멈추고, 수행 중이던 결과까지를 반환함!</p>
</blockquote>
<p><strong>coroutine in idle time 작동 원리 (Promise 함수 분석)</strong></p>
<blockquote>
<p><span style="background-color: #FAF2DD;">generator/yield와 resolve를 통해서 Idle Time coroutine을 실행함</span></p>
<pre><code class="language-tsx">resolver = resolve;
const iterator = coroutine.next ? coroutine : coroutine();</code></pre>
<p><strong><code>iterator</code></strong></p>
<p>coroutine의 Iterator가 생성되었는지 확인 후, 생성되지 않았으면 생성. 아니면(이미 coroutine은 iterator임.) 그대로 사용함.</p>
<pre><code class="language-tsx">request(run)

// request
request = typeof window === &#39;undefined&#39; 
                    ? getNodeCallback() // requestIdleCallback 대체함수. 거의 사용 안함
                    : window.requestIdleCallback;</code></pre>
<p><strong><code>window.requestIdleCallback(callback)</code></strong></p>
<p>전달받은 <strong>callback을 browser의 idle period에서 호출할 수 있도록 queue</strong>해놓는 method</p>
<p>background 작업 수행과, main event loop에서 priority에 맞게 작업을 수행할 수 있도록 함.</p>
<p>해당 함수가 호출되면, callback으로 <strong><code>IdleDeadline</code></strong>object를 넘려준다.</p>
<p><code>IdleDeadline</code></p>
<p><strong><code>IdleDeadline.timeRemaining</code></strong></p>
<p>current idle period에서 남은 ms를 반환한다.</p>
<pre><code class="language-tsx">let id = 0;
let parameter = undefined; // coroutine안에 coroutine(Promiese)의 경우, 그 값을 담는 변수
let running = false; // background에서 수행중인 task의 유무. true이면 pending

async function run(api){
    if(running) return;
    try{
        running = true; // 현재 끝나지 않은 coroutine이 있음을 의미
        clearTimeout(id) // 이전에 timeout 변수에 맞게 scheduling되어있던 setTimeout 제거 ~ 지금 수행할거니까!

        if(terminated){ // 외부 종료 명령에 따라서 coroutine을 수행하지 않음
            iterator.return()
            return
        }
        let minTime = Math.max(minRemainingTime = 1.75, loopWhileMsRemains);

        try{
            // 프로그램 상 남은 idle time이 설정치보다 커야 계속 작업 수행함.
            while(api.timeRemaining() &gt; minTime) { 
                const {value, done} = iterator.next(await parameter)
                parameter = undefined; // 보통은 다음 coroutine을 위한 parameter는 필요하지 않으므로 undefined로 정의

                if(done){ // coroutine 작업이 다 끝났으므로 결과값을 반환하기!
                    resolve(value);
                    return;
                }
                if(value === true){ // yield done의 경우 coroutine을 중지하고, next idle에서 수행할 수 있도록 함
                    break;
                } else if (typeof value === &#39;number&#39;){ // value만큼의 idleTime이 남았는지!
                    minTime = +value; // value가 number인지 한번 더 확인 후, minTime을 value로 
                    if(isNaN(minTime)) minTime = minRemainingTime
                } else if (value &amp;&amp; value.then) { // value가 Promise인 경우!
                        parameter = value;
                }
            }
        } catch (e) {
                console.log(&#39;error: &#39;, e);
                reject(e);
                return;
        }
        // 끝날때까지 계속 수행
        request(run);
        if(timeout){
            id = setTimeout(runFromTimeout, timeout);
        }
    } finally {
        // coroutine의 작업이 모두 종료되었음을 의미함
        running = false;
    }
}</code></pre>
<p><strong><code>minTime</code></strong></p>
<p>programmer가 지정한 최소 Idle time과 권장되는 최소 Idle Time중 더 큰 것으로 default를 가진다.</p>
<p><strong><code>iterator.next(await parameter)</code></strong></p>
<p>yield가 가지는 값이 <strong>또다른 coroutine(Promise)인 경우</strong>를 위해서 await parameter로 전달됨</p>
</blockquote>
<h3 id="react-native-ios문제">react-native iOS문제</h3>
<p>react-native ios에서 js-coroutine의 run함수가 아예 실행이 안됨</p>
<p>그거는 <code>window.requestIdleCallback</code> 의 문제!!!</p>
<p>아래 글을 보고 requestIdleCallback을 바꿔줬더니 잘~돌아감</p>
<p><a href="https://github.com/facebook/react-native/issues/28602">https://github.com/facebook/react-native/issues/28602</a></p>
<ul>
<li>requestIdleCallback 오류시 <strong>해결법</strong> ~ 직접 선언해서 사용</li>
</ul>
<pre><code class="language-bash">window.requestIdleCallback = function (cb) {
    var start = Date.now();
    return setTimeout(function () {
        cb({
            didTimeout: false,
            timeRemaining: function () {
                return Math.max(0, 50 - (Date.now() - start));
            },
        });
    }, 1);
    };</code></pre>
<ul>
<li>RN coroutine 예시 코드</li>
</ul>
<pre><code class="language-tsx">    import React, {useRef, useState} from &#39;react&#39;;
    import {useEffect} from &#39;react&#39;;
    import {memo} from &#39;react&#39;;
    import {Dimensions, Image, View, Animated} from &#39;react-native&#39;;
    // import {
    //   append,
    //   forEach,
    //   map,
    //   reduce,
    //   run,
    //   singleton,
    //   update,
    //   yielding,
    // } from &#39;js-coroutines&#39;;
    import run from &#39;../utils/run&#39;;

    import Typography from &#39;../components/atoms/Typography&#39;;
    import {append, forEach, map, reduce, update, yielding} from &#39;js-coroutines&#39;;
    const image = require(&#39;../assets/images/app_logo.png&#39;);
    const width = 100;
    function* coroutine1() {
      console.log(&#39;in coroutine!&#39;);
      let results;
      results = new Array(2000000);
      for (let i = 0; i &lt; 2000000; i++) {
        if ((i &amp; 127) === 0) yield;
        results[i] = (Math.random() * 10000) | 0;
      }
    }

    async function createAsync() {
      console.log(&#39;createAsync!&#39;);
      await run(coroutine1);
    }

    const CoroutineTest: React.FC&lt;{}&gt; = ({}) =&gt; {
      const [x, setX] = useState(0);
      const [y, setY] = useState(0);
      const [text, setText] = useState(&#39;first&#39;);
      const {format} = new Intl.NumberFormat();

      function animate() {
        console.log(&#39;-animate-&#39;);
        let multiplier = Dimensions.get(&#39;window&#39;).width / 300;
        return update(function* () {
          while (true) {
            //Move left to right

            //Move top to bottom
            for (let y = 0; y &lt; 200; y++) {
              setY(y * multiplier);
              yield;
            }
            for (let y = 200; y &gt;= 0; y--) {
              setY(y * multiplier);
              yield;
            }
          }
        });
      }
      async function calculateAsync() {
        return await run(function* () {
          let results;

          //Create 2 million rows of random values
          results = new Array(2000000);
          for (let i = 0; i &lt; 2000000; i++) {
            if ((i &amp; 127) === 0) {
              yield;
            }
            results[i] = (Math.random() * 10000) | 0;
          }
          setText(`CO: Created ${format(results.length)} items`);

          //Double all the values
          yield* forEach(
            results,
            yielding((r, i) =&gt; (results[i] = r * 2)),
          );
          setText(`CO: Doubled the value of ${format(results.length)} items`);

          //Get the square roots
          const sqrRoot = yield* map(
            results,
            yielding((r) =&gt; Math.sqrt(r)),
          );
          setText(
            `CO: Created a new array with the square roots of ${format(
              sqrRoot.length,
            )} items`,
          );

          //Sum all of the items
          setText(
            `CO: Sum of ${format(results.length)} items is ${format(
              yield* reduce(
                results,
                yielding((c, a) =&gt; c + a, 64),
                0,
              ),
            )}`,
          );

          //Join the arrays
          yield* append(results, sqrRoot);
          setText(
            `CO: Appended the square roots to the normal values making ${format(
              results.length,
            )} items in the array`,
          );

          // Sort the results
          yield* sort(results, (a, b) =&gt; a - b);
          setText(`CO: Sorted ${format(results.length)} items`);
          return results;
        });
      }
      useEffect(() =&gt; {
        animate();
        calculateAsync().then((r) =&gt; {
          console.log(&#39;calculation done: &#39;, r);
        });
        createAsync().then((v) =&gt; {
          console.log(&#39;done!&#39;);
        });
      }, []);
      return (
        &lt;View
          style={{
            width: &#39;100%&#39;,
            height: &#39;100%&#39;,
            alignItems: &#39;center&#39;,
            paddingTop: 100,
            borderWidth: 10,
          }}&gt;
          &lt;Animated.View
            style={{
              height: width * 0.8,
              width: width * 0.8,
              backgroundColor: &#39;green&#39;,
              borderRadius: width * 0.8,
              borderWidth: 10,
              borderColor: &#39;blue&#39;,
              transform: [
                {
                  translateY: y,
                },
              ],
            }}
          /&gt;
          &lt;Typography&gt;{text}&lt;/Typography&gt;
        &lt;/View&gt;
      );
    };

    export default memo(CoroutineTest);
    function* sort(results: any, arg1: (a: any, b: any) =&gt; number) {
      throw new Error(&#39;Function not implemented.&#39;);
    }</code></pre>
<h2 id="결론--나쁘지-않다">결론,  나쁘지 않다.</h2>
<p>JS에서 실현할 수 있는 coroutine(generator &amp; yield)은 프로그래머의 선택에 따른다.</p>
<p>그 말인 즉, thread를 얼마나 사용하고 <strong>어떤 thread에서 어떤 task를 수행할지</strong>도 다 정해야한다!</p>
<p>또한, thread가 <strong>어떤 time period에서 사용되는지</strong>도 선택해야한다는 것!
<br/></p>
<p>js-coroutine은 로드가 큰 작업들은 완전히 background에서 수행될 수 있도록, </p>
<p>idle time을 내부적으로 체크해서 해당 period에서 수행되도록 기능을 제공한다.</p>
<p>또한, main Thread만 사용하므로 context switching도 발생하지 않는다.</p>
<hr>
<hr>
<p>아래는 JS-coroutine에서 핵심적으로 사용된 background Tasks API에 관한 포스트이다.</p>
<h1 id="📲-background-tasks-api---span-stylecolorc5554drequestidlecallbackspan">📲 Background Tasks API - <span style="color:#C5554D">requestIdleCallback()</span></h1>
<p>= cooperative Scheduling of Background Tasks API는 해당 작업을 수행할 <strong>free time이 존재하면</strong> 자동으로 수행될 수 있도록 <strong>queuing tasks</strong>를 제공한다.</p>
<p><span style="background-color:#F3EEEE">JS-coroutine은 background라는 개념을 사용했고, requestIdleCallback을 통해서 해당 개념을 수행한다.</span></p>
<h2 id="concepts">Concepts</h2>
<p>requestIdleCallback을 이용해서 system lag 없이 event loop를 수행할 수 있는 <strong>시간을 파악</strong>하고, 수행한다.</p>
<h2 id="idle-callbacks를-최대한-활용하는-방법">Idle Callbacks를 최대한 활용하는 방법</h2>
<ul>
<li><span style="background:#FAECEC"><strong>low priority</strong></span>를 갖는 task를 idle callback으로 처리하기
 보통은 callback이 얼마나 수행될지(timeout을 설정하지 않으면), user의 system이 어떤 상태인지 등을 모르기 때문에 <strong><em>매 frame마다 idle time이 존재하는지 알 수 없다</em>.</strong> 
 <br/> 그렇기 때문에 최대한 low priority의 작업을 수행하는 것이 좋다.<br/>    </li>
<li>idle callback은 <strong>할당된 시간을 초과하지 않도록</strong>!
 timelimit은 작업이 종료할 시간을 충분히 확보하기 위해서 만들어 진 것이다.
  timeRemaining()이 50ms까지 리밋이 걸려있긴 하지만, 실제로는 여러가지 다른 복잡한 요인들로 <strong><em>50ms보다 적은 시간이 남아있을 수 있다.</em></strong> 
<br/>그렇기 때문에 할당도 너무 빡빡하게 하지말도록 하고, 최대한 할당된 시간 안에서 처리될 수 있도록!<br/>    </li>
<li>idle callback안에서 <span style="background:#FAECEC"><strong>making changes to DOM은 피하도록!</strong></span>
  <strong><em>callback이 수행될때는, 이미 current frame은 할 일을 끝낸 상태!</em></strong> 
그렇기 때문에 여기에서 추가적인 변경은 지양해야한다.
<br/>만약, idleTime에서의 changes to DOM이 필요하면 <code>window.requestAnimationFrame()</code>을 사용하도록! ~ JS-coroutine의 update도 이 개념을 그대로 사용하고 있다.</li>
</ul>
<br/>

<ul>
<li><span style="background:#FAECEC"><strong>run time이 예측되지 않는 task는 지양</strong></span>하자!
callback에서 아래의 작업들은 사용하지 말도록!
  -layout을 건드리는 task<ul>
<li>Promise를 return하는 callback (비동기는 예측불허니!)</li>
</ul>
</li>
</ul>
<ul>
<li><span style="background:#FAECEC"><strong>timeout</strong>(main loop에서 남은 시간 정하기)<strong>는 진짜 필요할때만!</strong></span>
  원래는 idle time에 queue되어있는 task들이 FIFO의 순서대로 처리되는데, 
  timeout 옵션을 줘버리면 queue되어 있는 순서에 상관없이, timeout 시간동안 해당 calback(task)가 수행이 안되면, 그냥 먼저 실행시켜버리는 옵션이다</li>
</ul>
<br/>

<h2 id="settimeout으로-background-tasks-api-호환성-보장하기">setTimeout으로 Background Tasks API 호환성 보장하기</h2>
<p>이 부분이 아래 RN에서 requestIdleCallback을 지원하지 않는 문제 해결 가능!!</p>
<pre><code class="language-tsx">window.requestIdleCallback = window.requestIdleCallback || function(handler) {
  let startTime = Date.now();

  return setTimeout(function() {
    handler({
      didTimeout: false,
      timeRemaining: function() {
        return Math.max(0, 50.0 - (Date.now() - startTime));
      }
    });
  }, 1);
}</code></pre>
<h2 id="background-tasks-api-사용해보기">Background Tasks API 사용해보기</h2>
<p><a href="https://yari-demos.prod.mdn.mozit.cloud/en-US/docs/Web/API/Background_Tasks_API/_sample_.Example.html">Background Tasks API - Example - code sample</a></p>
<p>페이지 소스 보기로 예시 코드 볼 수 있음!</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API">Background Tasks API - Web APIs | MDN</a></p>
<p>출처 | requestIdleCallback 코드 설명</p>
<p>requestIdleCallback이 호출되면, 호출되는 순서대로 callback이 queue되어서 시스템 내부적으로 FIFO 순서에 맞게 각 callback이 idleTime에 맞게 알아서들 호출되지만,</p>
<p>개발자의 입장에서 시스템의 내부에 맡기는 것이 아니라 직접 task 수행 실행/완료를 control하는 것이 더 맞기 때문에, <code>taskList</code> 변수를 이용해서 queue를 명시적으로 수행해준다.</p>
<h2 id="결론--다양한-조합으로-background-task-handling이-가능하다">결론,  다양한 조합으로 background task handling이 가능하다.</h2>
<p>위의 예시를 조금만 뜯어서 살펴보면</p>
<p>requestIdleCallback과 queue개념을 이용해서 load가 큰 task를 background에서 효율적으로 사용했음을 알 수 있다.</p>
<p>수행중인 task가 끝나기 전에 새로운 task에 대한 사용자 요청이 생기더라도 </p>
<p><strong>task는 queue에 accumulate</strong>되기 때문에 <strong>FIFO 순서대로 실행된다.</strong></p>
<p>사용방식에 따라서 효율적으로 background task를 수행할 수 있을 것 같다.</p>
<ul>
<li><p>조금 더 공부해보기</p>
<p>  <a href="https://yceffort.kr/2021/08/requestIdlecallback">requestIdleCallback으로 최적화하기</a></p>
</li>
</ul>
<hr>
<hr>
<h1 id="💡-js-coroutine을-사용해야만-할까">💡 JS-coroutine을 사용해야만 할까?</h1>
<p>답을 하기전, 지금까지 소개했던 개념들의 차이를 정확하게 알아야할 것 같다.</p>
<h3 id="span-stylebackground-color-f8ecdfcoroutinespan은"><span style="background-color: #F8ECDF">Coroutine</span>은</h3>
<p>task와 thread를 1:1로 대응하는 방식이 아닌, N:1로 대응하는 방식이다.</p>
<p>선택에 따라서 1개 이상의 thread를 사용할 수 있으며, thrad에서 수행될 task들도 지정할 수 있다. </p>
<p><strong>JS에서는 coroutine을 generator와 yield를 통해 수행</strong>할 수 있다.</p>
<br/>

<h3 id="span-stylebackground-color-f8ecdfrequestidlecallbackspan은"><span style="background-color: #F8ECDF">RequestIdleCallback</span>은</h3>
<p>함수 자체는, mainThread의 idle time에서 task(callback)를 수행할 수 있도록 해준다.</p>
<p>callback으로 받은 task는 idle period에 실행될 수 있도록 시스템 자체에서 queue되며, 언젠가 앞에서 먼저 queue되어있던 task가 끝나면, 다음 task가 실행되는 구조를 갖고 있다.</p>
<p>위의 <code>Background Tasks API 사용해보기</code>의 코드를 살펴보면, </p>
<p>queue 개념을 명시적으로 사용하므로써 pending되어있는 task들을 끝까지 처리해주는 것을 알 수 있다.</p>
<p><strong>사용자의 요청에 따라 거대한 task를 계속해서 accumulating하고, 해당 task를 중단할 필요가 없이 순차적으로 수행하기만 하면</strong> 된다면 직접 queue logic을 설계하여 사용하는 것도 좋은 방법이다.</p>
<br/>

<h3 id="span-stylebackground-color-f8ecdfjs-coroutinespan은"><span style="background-color: #F8ECDF">JS-coroutine</span>은</h3>
<p>mainThread만을 사용해서 개별 task들을 background에서 처리한다.</p>
<p>coroutine과 requestIdleCallback을 통합한 개념이라고 볼 수있다.</p>
<p>JS-coroutine은 <strong>task를 handling할 수 있는 옵션을 제공</strong>한다. </p>
<p>yield | yield n | yield true | yield generator | yield Promise 와 같은 옵션을 통해서</p>
<p>하나의 huge task를 쪼개어서(iterator) requestIdleTime의 callback에 넣어서 task 수행을 진행한다.</p>
<p><strong>하나의 거대한 task를 쪼개야한다면, coroutine + requestIdleCallback 조합을 사용하는 건 좋은 방법이다.</strong></p>
<h2 id="결론">결론</h2>
<p>up to you</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[write csv file]]></title>
            <link>https://velog.io/@azi_zero/write-csv-file</link>
            <guid>https://velog.io/@azi_zero/write-csv-file</guid>
            <pubDate>Fri, 03 Sep 2021 06:55:23 GMT</pubDate>
            <description><![CDATA[<h1 id="csv-파일을-생성하는-방법들">CSV 파일을 생성하는 방법들</h1>
<blockquote>
<ol>
<li><strong>csv-writer</strong> library를 사용하기</li>
</ol>
</blockquote>
<p>👍 파일에 쓰기를 할 때, <code>header를 옵션</code>으로 넣어서 처리할 수 있다.
👍 각각의 value를 <code>따로 string화 할 필요가 없다.</code>
👎 하나의 파일 안에 들어갈 데이터가 많아지면 짤려서 들어간다. </p>
<blockquote>
<ol start="2">
<li><strong>txt파일</strong>에 csv형태로 넣어주기</li>
</ol>
</blockquote>
<p>👍 <code>어지간한 용량은 다 커버</code>가 가능하다.
👎 csv(comma separated value) 형태로 가공해야한다.</p>
<p>각각의 방법이 장단점이 있지만 !!
본인의 상황에서 <strong>가장 필요한 부분</strong>이 무엇인지 판단하여 선택하면 된다.</p>
<p>필자의 경우, 처음에는 csv-writer를 사용했지만 용량 문제로 txt로 전환해서 사용중이다.</p>
<p>두가지 방법을 간단한 예제 프로젝트를 통해서 알아보자.</p>
<h2 id="logic">logic</h2>
<ol>
<li>header 설정하기</li>
<li>데이터 가공하기</li>
<li>파일 쓰기</li>
</ol>
<p>** 예시 데이터를 얻기 위해서 fake.js를 이용했다.**</p>
<pre><code class="language-javascript">import faker from &#39;faker&#39;;</code></pre>
<h1 id="csv-writer">csv-writer</h1>
<ol>
<li><code>header 설정</code></li>
</ol>
<ul>
<li>csv-writer의 경우, header의 형식이 정해져 있다.</li>
<li>{id: value, title: value} 형식을 갖는 header를 만들어 줘야한다.</li>
</ul>
<pre><code class="language-javascript">const txtHeader = [&#39;name&#39;, &#39;gender&#39;, &#39;job&#39;, &#39;country&#39;, &#39;city&#39;];
const csvWriterHeader = txtHeader.map((el) =&gt; {
  return { id: el, title: el };
});</code></pre>
<ol start="2">
<li><code>데이터 가공</code></li>
</ol>
<ul>
<li>각 데이터(for each row)의 형태는 
header의 각 값을 key로 갖고, 원하는 value를 넣은 object 이어야한다</li>
<li>해당 Object의 key값이 header안에 없으면 파일에 쓰기가 안된다.
(header와 key를 매칭하여 값을 넣기 때문에)</li>
<li>각 row들은 하나의 array안에 최종적으로 위치해야한다!</li>
</ul>
<pre><code class="language-javascript">
// User type
interface User {
    name: string;
    gender: string;
    job: string;
    country: string;
    city: string;
  }

let result: User[] = [];
for (let i = 0; i &lt; 20; i++) {
  let data: User = {
    name: faker.name.firstName(),
    gender: faker.name.gender(),
    job: faker.name.jobTitle(),
    country: faker.address.country(),
    city: faker.address.cityName(),
  };
  result.push(data);
}</code></pre>
<ol start="3">
<li><code>파일 쓰기</code>
아주 간단하다!
최종 파일의 경로와 header를 설정하고 난 후, 쓰기를 실행하면 된다!</li>
</ol>
<pre><code class="language-javascript">const createCsvWirter = require(&#39;csv-writer&#39;).createObjectCsvWriter;
const csvWriter = createCsvWirter({
  path: &#39;./csv-writer.csv&#39;, 
  header: csvWriterHeader,
});

csvWriter.writeRecords(result).then(() =&gt; {
  console.log(&#39;done!&#39;);
});
</code></pre>
<h1 id="txt">txt</h1>
<ol>
<li><code>header 설정</code></li>
</ol>
<ul>
<li>csv-writer처럼 header와 각 data의 key matching이 일어나지 않기 때문에 string으로 선언해주면 된다.<pre><code class="language-javascript">const txtHeader = &#39;name,gender,job,country,city\n&#39;</code></pre>
</li>
</ul>
<ol start="2">
<li><code>데이터 가공</code>
두가지 방법이 있다.</li>
</ol>
<ul>
<li><p>data를 object로 가공한 후, stringify해주기
데이터가 복잡할 때 사용</p>
<pre><code class="language-javascript">let result = txtHeader;
for (let i = 0; i &lt; 20; i++) {
let data: User = {
  name: faker.name.firstName(),
  gender: faker.name.gender(),
  job: faker.name.jobTitle(),
  country: faker.address.country(),
  city: faker.address.cityName(),
};

let stringified = Object.values(data).join(&#39;,&#39;) + &#39;\n&#39;;
result += stringified;
}
</code></pre>
</li>
</ul>
<pre><code>- 처음부터 string으로 만들기
데이터가 단순할 때 사용
가독성은 떨어지지만, Array.join()의 사용을 줄일 수 있다는 장점이 있다.
```javascript
let result = txtHeader.join(&#39;,&#39;) + &#39;\n&#39;;
for (let i = 0; i &lt; 20; i++) {
  result += `${faker.name.firstName()},${faker.name.gender()},${faker.name.jobTitle()},${faker.address.country()},${faker.address.cityName()}\n`;
}</code></pre><ol start="3">
<li><code>파일 쓰기</code></li>
</ol>
<ul>
<li>fs를 사용한다.<pre><code class="language-javascript">import fs from &#39;fs&#39;;
</code></pre>
</li>
</ul>
<p>fs.writeFileSync(&#39;./csv-txt.txt&#39;, result, { encoding: &#39;utf-8&#39; });</p>
<p>```</p>
<h1 id="마치며">마치며</h1>
<p>가장 중요한 부분은 csv파일은 결국 string형태의 value들이 comma(,)로 구분되어 있는 형식의 파일이라는 것이다. </p>
<p>최종 파일의 특성, 코드의 가독성 등등을 잘 따져서 본인의 상황에 맞게 잘 사용하면 좋을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[How to increase mongodb data processing performance]]></title>
            <link>https://velog.io/@azi_zero/How-to-increase-mongodb-data-processing-performance</link>
            <guid>https://velog.io/@azi_zero/How-to-increase-mongodb-data-processing-performance</guid>
            <pubDate>Tue, 27 Jul 2021 06:23:19 GMT</pubDate>
            <description><![CDATA[<h4 id="🐥-주절주절--🐥">🐥 주절주절  🐥</h4>
<p>회사에 들어와서 처음 맡게 된 업무가 DB에서 데이터를 추출해서 
원하는 형태에 맞게 가공한 후 csv파일로 만드는 것이었다.</p>
<p>관련 스크립트에 대해서는 이미 내부에서 만들어 진게 있어서 처음에는 그걸 사용해서 다듬는 작업을 했다.</p>
<p>하지만 그 코드는 읽으면 읽을 수록 
가독성이 떨어지고, 보수가 필요한 부분이 많이 보여서
거의 다 새로 만들었고, 현재까지 csv 추출 모듈로 잘 사용되고 있다.</p>
<p>중간중간에 오류도 많이 났고, 문제점도 많이 생겨서 고군분투했던 순간이 많이 기억난다.</p>
<p>다행히도 공부할 때는 자존심이 센 스타일이라 오기로 구글링하고 공부하며 
처음에는 데이터 양이 너무 많아서 서버가 터지거나 몇십분이 걸리던 것을
이제는 <em><strong>✨ 어지간해도 2분도 채 안걸리는 수준✨</strong></em> 까지 끌어올렸다.</p>
<p>만족해서는 안되지만 뿌듯한 지금이기에, 그동안의 코드 history를 공유하고자한다.</p>
<hr>
<hr>
<h1 id="history">history</h1>
<h2 id="1-performance에-관심이-없던-시절">1. Performance에 관심이 없던 시절</h2>
<p>가장 처음에 사용되었던 코드는 아래와 같다.</p>
<pre><code class="language-javascript">const data = db.collection(&#39;COLLECTION_NAME&#39;).find({}).toArray();</code></pre>
<p>위의 방법은 mongodb 공식문서에도 알려주는,
DB에서 가져온 데이터를 array형태로 바로 볼 수 있게 해주는 코드이다.</p>
<p>참고로 이때는 query속도에 대해서 1도 관심이 없던 시절이었다..</p>
<p>하나씩 살펴보면,</p>
<h3 id="코드-분석">코드 분석</h3>
<pre><code class="language-javascript">Collection.find({})</code></pre>
<ul>
<li><strong>parameter</strong>
  -query object를 넣어야 한다.
  -보통은 아래와 같은 형태로 들어간다.<pre><code class="language-javascript">const query = {date: {$gte: start, $lte: end}};</code></pre>
  (mongodb 공식 문서로 가면 더 다양한 query 방법을 볼 수 있다)</li>
<li><strong>return</strong> 
-query filtering을 통해 filtering 된 documents들을 순회하는 <strong>cursor</strong>(포인터 개념)을 반환한다.
-cursor를 활용하는 방법은 굉장히 많다. </li>
</ul>
<hr>
<pre><code class="language-javascript">Cursor.toArray();</code></pre>
<p>cursor를 활용하는 여러가지 방법들 중, 가장 흔한 방법이다.</p>
<ul>
<li><strong>return</strong> </li>
<li>query filtering을 통해 걸러진 모든 데이터들을 array안에 넣어서 반환해준다.</li>
<li>각각의 document들은 object형태로 존재하며, 반환되는 array는 모든 documents(objects)를 담고있다.</li>
</ul>
<h3 id="문제점">문제점</h3>
<p>위의 코드를 그대로 사용할 경우, 모든 데이터를 다 받아오고 그 많은 document들을 돌면서 하나의 array안으로 넣는 작업이 이루어 지면서 시간이 진----짜 오래 걸린다!!</p>
<p><em>만약 전체 document의 갯수도 적고, 하나의 document가 갖고있는 데이터도 많지 않다면 그대로 써도 되지만</em>  그게 아니라면 다른 방법을 생각해봐야 한다.</p>
<pre><code>필자의 경우 분단위 심박수 데이터를 추출하는 상황이었다.
하루치 심박수 데이터가 하나의 document에 들어가있는데, 
하루치 심박수는 분단위로 한다고 해도 1440개가 있고, 
1분당 데이터 안에도 심박수 수치나 다른 요소들이 많았기 때문에 저 방법은 무조건 피해야 했다. </code></pre><hr>
<h2 id="2-covered-query-시행">2. covered query 시행</h2>
<p>속도를 어떻게 향상 시켜야 하는지 계속 공부하다가, 아래의 문서를 발견했다.
[Performance Best Practices: Indexing] (<a href="https://www.mongodb.com/blog/post/performance-best-practices-indexing">https://www.mongodb.com/blog/post/performance-best-practices-indexing</a>)
혹시나 아직 index, query에 관해서 잘 모르는 사람들은 꼭! 한번 읽어봤으면 좋겠다.</p>
<pre><code class="language-javascript">db.collection(&#39;COLLECTION_NAME&#39;)
  .find({
     date: {$gte:start, $lte:end},
     user: {$in:target_users},
  })
  .forEach(data=&gt;{})</code></pre>
<p>query에 관해서 공부하면서 진짜 크게 느낀 점은
내가 DB를 만들어 쓰는 것도 아니고, DB에서 데이터를 긁어와서 쓰는거면 
💡 <strong>잘 만들어진 시스템을 아주 말라 비틀어질 때까지 짜서 써야한다</strong> 💡는 것이었다.</p>
<pre><code>1번에서 2번으로 오기까지 사실 우여곡절도 많았고, 
query만 공부하고 바로 수정작업에 들어가면서 
toArray()가 너무나도 강력한 것(array를 만들어야 forEach를 쓸 수 있다고 생각함..) 이라고 생각했기 때문에..
cursor.forEach()가 있는 줄도 모르고 
toArray().filter()이런 식으로 
쓸데없이 데이터를 다 갖고와서 다시 filtering하는 지옥을 몇주간 사용한 적도 있었다..</code></pre><h3 id="코드-분석-1">코드 분석</h3>
<p><code>아래의 query가 제대로 작동(collection scan되면 안됨)해야했기 때문에, compound index를 만들어서 진행했다.</code></p>
<pre><code class="language-javascript">Collection.find({
     date: {$gte:start, $lte:end},
     user: {$in:target_users},
  })</code></pre>
<p>여기에서 눈여겨봐야 할 부분은 아래이다.</p>
<pre><code class="language-javascript">user: {$in: target_users}</code></pre>
<p>user field는 ObjectId를 값으로 가지는 field이며,
target_users 변수는 필터링 할 유저들의 고유 id 담고있는 array이다.
<code>ObjectId는 mongoDB에서 제공해준다</code></p>
<p>user와 관련된 query가 제대로 작동을 하려면 
_<strong>target_users속 id들이 string형태가 아니라 ObjectId형태</strong>_로 존재해야한다!</p>
<hr>
<pre><code class="language-javascript">cursor.forEach(el=&gt;{})</code></pre>
<p>query filtering을 통해 걸러진 documents들을 하나씩 전부다 도는 것! 
Array.forEach()랑 형태가 아주 그냥 똑같지만 array가 아니라 cursor다!
arrow function의 el 변수는 document를 담고있게 된다.</p>
<h3 id="개선점">개선점</h3>
<ol>
<li>첫번째에 비해서 query를 제대로 사용했고, 그 query가 covered query였기 때문에 속도가 더 빨라졌다.</li>
<li>toArray()를 기다릴 필요없이 각 document를 돌면서 data processing을 처리할 수 있기 때문에 더 효율적이다.</li>
</ol>
<h3 id="문제점-1">문제점</h3>
<p>여전히 심박수와 같은 대용량 데이터를 다룰 때에는 시간이 오래 걸린다. 
앞에서 부터 하나씩 차례차례 진행되기 때문에 오래걸릴 수 밖에 없는 구조이다.</p>
<hr>
<h2 id="3-🌈-worker-thread-사용">3 🌈 worker thread 사용</h2>
<p>1, 2번과 달라진 세번째 방식의 가장 큰 변화는
nodejs의 <strong>worker thread</strong>를 사용한다는 것이다!!!!!!</p>
<pre><code>아 솔직히 진짜 이 방법 너무 좋고,, 
속도도 진짜 엄청 빨라지고,,
이제 모든 사용자의 심박수 6개월치 데이터 정도는 그냥 뽑을 수 있게 되었다.
매번 너무 느리다, 서버 터진다, 더 빠르게는 안되나 하는 컴플레인을 듣다가
진짜 속이 뻥 뚫리는 기분이었다.
내부적으로 슈퍼컴퓨터라는 별멍도 얻었다ㅋㅋㅋㅋㅋ 
그냥 너무 뿌듯해서 자랑한번 해봤다..ㅎㅎㅎ</code></pre><p>물론 worker thread도 한계가 있을 거고, 
다른 문제는 계속 생기겠지만 
현재는 이 방법이 최선이라고 생각한다.</p>
<pre><code class="language-javascript">exports.user = new Promise(async (resolve, reject) =&gt; {
  if (isMainThread) {
    const num_docs = await getNumOfDocs();
    const num_threads = getNumOfThreads(num_docs, QUERY_LIMIT);

    let collection = {};
    const threads = createThreads(path, num_threads, worker_data);
    for (let worker of threads) {
      worker.on(&#39;message&#39;, (data) =&gt; {
        //collect data in collection
      });
      worker.on(&#39;exit&#39;, () =&gt; {
        threads.delete(worker);
        if (threads.size === 0) {
          // collection variable contains all the data
          resolve(/*return data | collection*/);
        }
      });
    }
  } else {
    const {workerData, parentPort} = require(&#39;worker_threads&#39;);
    const {limit, skip} = workerData;
    let result = {};
    await db
      .collection(&#39;COLLECTION_NAME&#39;)
      .find({})
      .skip(skip)
      .limit(limit)
      .forEach((data) =&gt; {
        //process data and push it to result object;
          result.push(processed_data);
      });

    client.close();
    parentPort.postMessage(result);
    parentPort.close();
  }
});</code></pre>
<pre><code class="language-javascript">const getNumOfDocs = async()=&gt;{
  const nums = await db.collection(&#39;COLLECTION_NAME&#39;).find(query_obj).count();
  return nums;
}
const getNumOfThreads = (num_docs, limit) =&gt; {
  return Math.floor(num_docs/limit) + 1;
}
const createThreads = (path, num_threads, worker_data) =&gt; {
  /*
    worker_data[i] = {limit: number, skip: number, ...}
  */
  const threads = new Set();
  for(let i=0; i&lt;num_threads; i++){
    threads.add(new Worker(path, {workerData: worker_data[i]}))
  }
  return threads;
}</code></pre>
<h3 id="코드-분석-2">코드 분석</h3>
<pre><code class="language-javascript">new Promise(async(resolve, reject)=&gt;{});</code></pre>
<p>Promise가 아니라 그냥 async()=&gt;{} 였다면, 
표면적으로 모듈 호출이 의미하는 것은 
worker thread를 만들고, 각 worker thread가 어떤 코드를 실행할지 정해주고, 어떤 메세지를 주고 받을지 결정하는 것이 끝이다.</p>
<p>즉, 우리가 원하는 일(각각의 worker thread들이 추출한 데이터를 main thread에서 마지막에 모아서 반환하는 것)은 흐름에 맞게 이루어질 수 없다.</p>
<p>그렇기 때문에 <code>promise의 resolve()를 이용</code>해야 한다.
resolve에 각각의 worker threads를 통해서 모은 데이터를 모아서 반환해주면 된다!</p>
<pre><code class="language-javascript"> worker.on(&#39;exit&#39;, () =&gt; {
        threads.delete(worker);
        if (threads.size === 0) {
          // collection variable contains all the data
          resolve(/*return data | collection*/);
        }
      });</code></pre>
<hr>
<pre><code class="language-javascript">const worker_data = [
  {skip:0, limit:1000,},
  {skip:1000, limit: 1000,},
  ...
]</code></pre>
<p>각각의 worker thread가 꼭 갖고있어야 하는 값이다. 
data query를 할 때, <code>pagination(skip, limit)을 사용</code>하기 때문에 각 worker thread는 자신에게 맞는 값을 갖고 있어야한다.</p>
<hr>
<pre><code class="language-javascript">// in MainThread
const threads = createThreads(path, num_threads, worker_data);

const createThreads = (path, num_threads, worker_data) =&gt; {
  /*
    worker_data[i] = {limit: number, skip: number, ...}
  */
  const threads = new Set();
  for(let i=0; i&lt;num_threads; i++){
    threads.add(new Worker(path, {workerData: worker_data[i]}))
  }
  return threads;
}</code></pre>
<p>각각의 worker thread는 <code>Set 자료구조</code>에 저장된다. 
특히 path는 worker thread가 시행되는 파일의 경로를 담고 있어야하는데,
필자의 경우 main thread와 같은 모듈이 시행되어야 하기 때문에 해당 모듈로 연결해주는 파일을 만들어주고 해당 파일의 경로로 설정했다.
<code>이렇게 하지 않으려면 ``안에 worker thread가 실행되어야하는 코드를 넣으면 되는데, 가독성이 떨어져서 사용하지 않았다.</code></p>
<hr>
<pre><code class="language-javascript">// in MainThread
for(let worker of threads){
  worker.on(&#39;message&#39;,(data)=&gt;{});
  worker.on(&#39;exit&#39;,()=&gt;{
      threads.delete(worker);
    if(threads.size === 0){}
  })
}</code></pre>
<p>main thread에서 worker thread로 부터 전달받은 signal을 처리하는 코드이다.
<code>message</code>를 전달 받으면, 전달받은 데이터를 모으고
<code>exit</code>를 전달받으면, 해당 worker thread를 삭제하며 모든 worker thread가 삭제 되었을 때(작업이 다 끝났을 때) 우리가 원하는 일을 처리해주도록 했다.</p>
<hr>
<pre><code class="language-javascript">// in worker thread
await db
      .collection(&#39;COLLECTION_NAME&#39;)
      .find({})
      .skip(skip)
      .limit(limit)
      .forEach((data) =&gt; {
        //process data and push it to result object;
          result.push(processed_data);
      });</code></pre>
<p>worker thread가 수행하는 코드이다.
<code>pagination</code>을 수행한다. 이 부분이 가장 큰 속도 향상을 가져다 주었다.</p>
<hr>
<pre><code class="language-javascript">// in worker thread
client.close();
parentPort.postMessage(result);
parentPort.close();</code></pre>
<p>pagination을 통해 원하는 데이터를 query한 worker thread는 
해당 데이터를 main thread로 전달해주고 종료 메세지를 남겨주면 된다.</p>
<p><strong>위에 보이는 <code>client.close()</code>는 db연결을 종료하는 것이다.</strong>
이 작업을 꼭!!!! 해줘야한다! 그래야 worker thread가 종료될 수 있다.</p>
<h3 id="개선점-1">개선점</h3>
<p>thread 여러개를 동시에 사용하고, pagination을 사용하면서 속도가 엄청 빨라졌다.</p>
<h3 id="문제점-2">문제점</h3>
<p>현재까지 발생한 문제점은 없다.
지금은 pagination limit을 1000으로 두어서 진행하고있는데, 나중에 데이터가 더 많아져서 limit을 더 올리게 되었을 때 속도가 그대로 유지될 지는 의문이다.. </p>
<hr>
<hr>
<h1 id="conclusion">conclusion</h1>
<p>DB에서 데이터를 다룰 때 가장 중요한 부분은 Index이다.
본인의 상황에 맞게 index를 설정하고 그걸 잘 이용해서 query를 진행해야 반은 먹고 들어간다. 
또한 필자의 경우 data query와 동시에 CPU사용량이 높아서 worker threads가 잘 사용되었다.
각자의 operation이 어떤 타입인지를 보고 다시 결정하면 좋을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[worker_threads]]></title>
            <link>https://velog.io/@azi_zero/workerthreads</link>
            <guid>https://velog.io/@azi_zero/workerthreads</guid>
            <pubDate>Fri, 23 Jul 2021 08:45:47 GMT</pubDate>
            <description><![CDATA[<p>오늘은 분신술을 가능케하는 worker thread에 대해서 공부해보았다.
worker thread라는 키워드에 도달하기까지 하루가 걸렸다.
그 과정에 대해서는 다른 포스트에 worker thread의 이론적인 내용과 함께 적어보겠다.
일단 worker thread를 사용하기 위한 기본적인 코드, 응용 부분을 공부해보았다.</p>
<h1 id="👯♀️-worker_threads란">👯‍♀️ worker_threads란?</h1>
<p>node에서 multi thread방식을 가능하게 해주는 모듈이다</p>
<pre><code class="language-javascript">const {Worker, parentPort, isMainThread} = require(&#39;worker_threads&#39;);

// mainThread = not Worker thread
if (isMainThread) {
    const worker = new Worker(__filename);
    worker.on(&#39;message&#39;, (message) =&gt; console.log(&#39;from worker :&#39;, message));
    worker.on(&#39;exit&#39;, () =&gt; console.log(&#39;worker exit&#39;));
    worker.postMessage(&#39;ping&#39;);
}
// worker thread
else {
    workerThreads.parentPort.on(&#39;message&#39;, (value) =&gt; {
        console.log(&#39;from parent :&#39;, value);
        workerThreads.parentPort.postMessage(&#39;pong&#39;);
        workerThreads.parentPort.close();
    });
    console.log(&#39;workerThread logic&#39;);
}</code></pre>
<h2 id="line-by-line-explanation">line by line explanation</h2>
<blockquote>
</blockquote>
<p><strong>main thread == parent thread **
**worker thread == child thread</strong></p>
<h4 id="worker-class">Worker class</h4>
<blockquote>
</blockquote>
<p><code>Worker 클래스의 인스턴스들은 하나의 thread로서 기능한다.</code></p>
<pre><code class="language-javascript">const {Worker} = require(&#39;worker_threads&#39;)
const worker = new Worker(__filename,option);</code></pre>
<p><strong>__filename 안의 코드를 실행</strong>하는 Javascript execution thread를 생성한다.
만약 thread가 Worker이면(Worker class의 인스턴스), 
이 <strong>thread는 MessagePort</strong>로서, parent thread와 communication할 수 있다.
messagePort의 기능들을 사용할 수 있다는 말 = parentPort를 이용하지 않아도 된다는 말!</p>
<blockquote>
</blockquote>
<p>여러가지 option중에서 {workerDate:...}가 가장 잘 쓰인다!
worker thread가 갖고있는 workerData에 접근하려면</p>
<pre><code class="language-javascript">const data = worker.workerData</code></pre>
<p>를 사용하면 된다!</p>
<hr>
<blockquote>
<p><code>worker.on과 worker.postMessage는 worker thread와 communication하는 도구이다.</code></p>
</blockquote>
<pre><code class="language-javascript">worker.on(&#39;message&#39;, (message)=&gt;{});
worker.on(&#39;error&#39;, (error)=&gt;{});
worker.on(&#39;exit&#39;,(code)=&gt;{});</code></pre>
<p>worker thread로 부터 작업 준비(메세지), 작업 에러, 작업 종료 지시가 오면 실행된다.</p>
<pre><code class="language-javascript">worker.postMessage(message)</code></pre>
<p>worker thread로 메세지를 보내는 것을 의미한다.</p>
<h4 id="parentport">parentPort</h4>
<blockquote>
</blockquote>
<p><code>parentPort는 parent thread와 communication하는 도구이다.</code></p>
<pre><code class="language-javascript">const {parentPort} = require(&#39;worker_threads&#39;)</code></pre>
<p>parentPort는 <strong>messagePort의 인스턴스</strong>이다.
parentPort는 from/to parent thread의 중간자라고 보면 된다.</p>
<hr>
<pre><code class="language-javascript">parentPort.postMessage(&#39;pong&#39;);</code></pre>
<p>parent thread로 메세지를 보내는 것을 의미한다.</p>
<pre><code class="language-javascript">parentPort.on(&#39;message&#39;,()=&gt;{})</code></pre>
<p>parent thread로 부터 메세지를 받는 것을 의미한다.</p>
<h4 id="ismainthread">isMainThread</h4>
<blockquote>
</blockquote>
<pre><code class="language-javascript">const {isMainThread} = require(&#39;worer_threads&#39;)</code></pre>
<p>true if code is <strong>not running inside of</strong> Worker thread 
worker thread는 말그대로 만들어진(파생된?) thread고,
가장 처음 실행되는 thread는 main thread다!</p>
<h2 id="whole-code-analyze">whole code analyze</h2>
<p>하나의 코드를 두개의 thread에서 실행한다고 보면 됨!</p>
<blockquote>
</blockquote>
<p>main thread</p>
<pre><code class="language-javascript">&gt;&gt; main.js
const {Worker, parentPort, isMainThread} = require(&#39;worker_threads&#39;);
// 1) 여기는 main thread이기 때문에 isMainThread===true
if (isMainThread) { 
// 2) 새로운 Worker thread를 생성함 -&gt; multi thread 시작
// 이 시점부터는 worker thread도 할 일을 하고있게됨.
    const worker = new Worker(__filename);
// worker thread로부터 응답을 받으면 처리함
    worker.on(&#39;message&#39;, (message) =&gt; console.log(&#39;from worker :&#39;, message));
    worker.on(&#39;exit&#39;, () =&gt; console.log(&#39;worker exit&#39;));
// 3) worker thread로 메세지를 보냄
    worker.postMessage(&#39;ping&#39;);
}
else { 
    parentPort.on(&#39;message&#39;, (value) =&gt; {
        console.log(&#39;from parent :&#39;, value);
        workerThreads.parentPort.postMessage(&#39;pong&#39;);
        workerThreads.parentPort.close();
    });
    console.log(&#39;workerThread logic&#39;);
}</code></pre>
<blockquote>
</blockquote>
<p>worker thread</p>
<pre><code class="language-javascript">&gt;&gt; main.js
const {Worker, parentPort, isMainThread} = require(&#39;worker_threads&#39;);
if (isMainThread) {
    const worker = new Worker(__filename);
    worker.on(&#39;message&#39;, (message) =&gt; console.log(&#39;from worker :&#39;, message));
    worker.on(&#39;exit&#39;, () =&gt; console.log(&#39;worker exit&#39;));
    worker.postMessage(&#39;ping&#39;);
}
else { 
// parentPort로부터 메세지가 오면 처리하게 됨
    parentPort.on(&#39;message&#39;, (value) =&gt; {
        console.log(&#39;from parent :&#39;, value);
// parentPort로 메세지를 보내고
        parentPort.postMessage(&#39;pong&#39;);
// parentPort에게 종료한다는 메세지를 보냄
        parentPort.close();
    });
// worker thread만들어진 후 바로 실행됨
    console.log(&#39;workerThread logic&#39;);
}</code></pre>
<p>result</p>
<pre><code>workerThread logic
from parent : ping
from worker : pong
worker exit</code></pre><h1 id="👯♀️-worker-thread-여러개-만들기">👯‍♀️ worker thread 여러개 만들기</h1>
<p>Worker class의 인스턴스를 여러개 만들면 되는데,, 어떻게 관리할까?</p>
<pre><code class="language-javascript">const {Worker, workerData, parentPort, isMainThread} = require(&#39;worker_threads&#39;);
if (isMainThread) {
    const threads = new Set();
    threads.add(
        new Worker(__filename, {
            workerData: &#39;hello world&#39;,
        })
    );
    threads.add(
        new Worker(__filename, {
            workerData: &#39;hello js&#39;,
        })
    );

    for (let worker of threads) {
        worker.on(&#39;message&#39;, (message) =&gt;
            console.log(&#39;from worker :&#39;, message)
        );
        worker.on(&#39;exit&#39;, () =&gt; {
            threads.delete(worker);
            if (threads.size === 0) console.log(&#39;worker done&#39;);
        });
    }
} else {
    const data = workerData;
    parentPort.postMessage(data + &#39; from here&#39;);
}</code></pre>
<h2 id="line-by-line-explanation-1">line by line explanation</h2>
<h4 id="multi-threads">multi threads</h4>
<blockquote>
</blockquote>
<p><code>worker class의 인스턴스들은 독립적이다</code></p>
<pre><code class="language-javascript">const threads = new Set();
threads.add(
    new Worker(__filename, {workerData: ...})
)  </code></pre>
<p>생성된 thread들은 공통되거나 겹칠 필요가 없기 때문에 worker class의 여러 인스턴스들은 <strong>set</strong>자료 구조에 저장될 수 있다.</p>
<h4 id="workerdata-option">workerData option</h4>
<blockquote>
</blockquote>
<p><code>workerData는 인스턴스가 갖고있는 데이터이다</code></p>
<pre><code class="language-javascript">new Worker(__filename, {workerData: script});</code></pre>
<p>workerData의 값으로는 <strong>어떤 것이든지</strong> 들어갈 수 있다.
workerData에 값을 주면, thread의 Worker constructor에 전해진다.
postMessage를 할 때 clone된다!</p>
<hr>
<p><code>workerData를 불러와서 사용하면 된다.</code></p>
<pre><code class="language-javascript">const {workerData} = require(&#39;worker_threads&#39;)
const data = workerData;</code></pre>
<p>workerData를 불러오면 현재 thread를 알 수 있는 지표가 된다.</p>
<h1 id="👯♀️-worker-thread-적용해보기">👯‍♀️ worker thread 적용해보기</h1>
<p>여러가지 숫자들을 출력하는 thread</p>
<pre><code class="language-javascript">const {
  Worker,
  workerData,
  parentPort,
  isMainThread,
} = require(&#39;worker_threads&#39;);
const makeNumsArr = (start, end) =&gt; {
  let result = [];
  for (let i = start; i &lt; end; i++) {
    result.push(i);
  }
  return result;
};
if (isMainThread) {
  //make three threads
  const threads = new Set();
  for (let i = 0; i &lt; 3; i++) {
    threads.add(
      new Worker(__filename, {
        workerData: {
          workerId: `0${i}_worker`,
          start: i * 10,
          end: (i + 1) * 10,
        },
      })
    );
  }
  for (let worker of threads) {
    worker.on(&#39;message&#39;, (message) =&gt; {
      const { workerId, value } = message;
      console.log(`0${workerId} gets ${value}`);
    });
  }
} else {
  const { workerId, start, end } = workerData;
  const nums = makeNumsArr(start, end);
  parentPort.postMessage({ workerId: workerId, value: nums });
}</code></pre>
<p>result</p>
<pre><code>00_worker gets 0,1,2,3,4,5,6,7,8,9
01_worker gets 10,11,12,13,14,15,16,17,18,19
02_worker gets 20,21,22,23,24,25,26,27,28,29</code></pre><h1 id="🤷♀️-더-알아보기">🤷‍♀️ 더 알아보기</h1>
<ol>
<li>worker thread 장단점</li>
<li>single thread의 장점</li>
<li>worker thread를 어떤 경우에 사용하면 좋을지? CPU intensive의 예시 케이스 찾아보기</li>
</ol>
<h1 id="reference">reference</h1>
<p><a href="https://velog.io/@goblin820/Node.js-workerthreads-module">https://velog.io/@goblin820/Node.js-workerthreads-module</a>
<a href="https://nodejs.org/api/worker_threads.html">https://nodejs.org/api/worker_threads.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Strapi image upload (multipart/form-data)]]></title>
            <link>https://velog.io/@azi_zero/Strapi-image-upload-multipartform-data</link>
            <guid>https://velog.io/@azi_zero/Strapi-image-upload-multipartform-data</guid>
            <pubDate>Thu, 08 Jul 2021 07:26:15 GMT</pubDate>
            <description><![CDATA[<h1 id="➖-image-upload">➖ image upload</h1>
<h3 id="multipartform-data는-선을-잘-지켜줘야-하더라구요">multipart/form-data는 선을 잘 지켜줘야 하더라구요..?</h3>
<p> #️⃣  *<em>multipart/form-data *</em> type의 data를 axios를 이용해 strapi로 업로드하기 #️⃣ </p>
<p>이번주의 ⛏ 퍽퍽한 삽질 이야기 ⛏ 입니다.</p>
<h3 id="📜-절구절구-📜">📜 <em>절구절구</em> 📜</h3>
<p>이미지를 업로드 하는 것을 처음 도전한 것은 월요일..
strapi 서버로 이미지를 전송하는 것은 예전에도 해본 적이 있었기 때문에
어떠한 방심도, 긴장도 하지 않았건만..
프론트에서 보내는 것과 일반 서버나 axios로 보내는 것은 다른 것인지.. 
끝없는 error 대 잔 치 💕🌈 💐 매일 한시간씩 연구했는데도 끝까지 날 밀어내던 놈이.. 
드디어 목요일에 나에게 🟨 &#39;선넘지마 선 잘 지켜&#39; 🟨 라며 내 품으로 와주었다..</p>
<h1 id="➖-code-part">➖ code part</h1>
<h2 id="👊-code">👊 code</h2>
<pre><code class="language-javascript">import FormData from &#39;form-data&#39;;
import fs from &#39;fs&#39;;

const form_data = new FormData();
✨ const header = form_data.getHeaders(); ✨
const file_path = path.join(__dirname, &#39;../file/FILE.jpg&#39;);

✨ const file_stream = fs.createReadStream(file_path); ✨


form_data.append(&#39;files.image&#39;, file_stream);
form_data.append(&#39;data&#39;, JSON.stringify({
    FIELDNAME: FIELDDATA
});


await axios.post(URI_PATH, form_data, ✨{headers: header}✨);       
</code></pre>
<h2 id="💪-code-logic">💪 code logic</h2>
<p>이미지 업로드 하는 방법?</p>
<p>1️⃣  로컬 이미지 파일을 form-data로 형성한다</p>
<pre><code class="language-javascript">const file_path = path.join(__dirname, &#39;../file/FILE.jpg&#39;);
const file_stream = fs.createReadStream(file_path); 

const form_data = new FormData();
form_data.append(&#39;files.image&#39;, file_stream);</code></pre>
<p>2️⃣ 서버로 전송 시 multipart/form-data라는 header를 꼭 설정해줘야한다.</p>
<pre><code class="language-javascript">const header = form_data.getHeaders();
...
await axios.post(URI_PATH, form_data, {headers: header}); 
/* value of headers
{
&#39;content-type&#39;: &#39;multipart/form-data; boundary=--------------------------653276028816157565965859&#39;
}
*/</code></pre>
<p>3️⃣  파일이 쪼개져서 이동하기 때문에, 파일들의 경계를 설정해주는 boundary를 꼭 지정해줘야한다
이 부분은 form-data객체를 형성할 때 자동으로 된다!</p>
<p>boundary는 원하는 값으로 임의 지정이 가능한데, 방법은 아래와 같다.</p>
<pre><code class="language-javascript">form_data.setBoundary(&#39;BOUNDARY_STRING&#39;);</code></pre>
<h1 id="➖-study">➖ study</h1>
<h2 id="✋-extra-information">✋ Extra information</h2>
<ol>
<li><p>form data란?
AJAX로 폼 전송을 가능하게 해주는 객체이다. 보통 <strong>파일을 업로드</strong> 할 때 사용된다.</p>
</li>
<li><p>multipart/form-data란?
form data가 여러부분으로 <strong>쪼개져서</strong> 서버로 전송되는 것을 말한다.</p>
</li>
<li><p>boundary,<strong>선을 지키라는 것</strong>의 의미는?
multipart/form-data type으로 데이터를 전송할 것을 설정한다는 것은 
위에 나와있는 개념처럼 파일을 쪼개서 전송한다는 것을 의미한다.
조각난 파일들을 나중에 모아서 합쳐야하는데, 
그 경계 즉, <strong>쪼개진 파일들의 어디까지가 원시 파일의 구성요소인지를 알려주는 것</strong>이 중요하다.
그 역할을 하는 것이 바로 <strong>boundary</strong>!!</p>
</li>
</ol>
<p>boundary가 어떻게 위치하는지 살펴보면 아래와 같다 (console.log(form_data);) </p>
<pre><code class="language-javascript">_streams: [
 //여기서부터 이미지 파일 영역
&#39;----------------------------389101968969285569395704\r\n&#39; +
           &#39;Content-Disposition: form-data; name=&quot;files.image&quot;; filename=&quot;pill.jpg&quot;\r\n&#39; +
           &#39;Content-Type: image/jpeg\r\n&#39; +
           &#39;\r\n&#39;,
         DelayedStream {
           source: [ReadStream],
           dataSize: 0,
           maxDataSize: Infinity,
           pauseStream: true,
           _maxDataSizeExceeded: false,
           _released: false,
           _bufferedEvents: [Array],
           _events: [Object: null prototype],
           _eventsCount: 1
         },
         [Function: bound ],
//여기서부터는 data 영역
         &#39;----------------------------389101968969285569395704\r\n&#39; +
           &#39;Content-Disposition: form-data; name=&quot;data&quot;\r\n&#39; +
           &#39;\r\n&#39;,
         &#39;{&quot;product_name&quot;:&quot;테스트&quot;}&#39;,
         [Function: bound ]
       ],
       _currentStream: null,
       _insideLoop: false,
       _pendingNext: false,
//boundary값을 따로 지정해주지 않으면 자동으로 설정해줌!
       _boundary: &#39;--------------------------389101968969285569395704&#39;</code></pre>
<h2 id="⛏-삽질-후기⛏">⛏ 삽질 후기⛏</h2>
<p>데이터를 전송할 때 가장 중요한 것은 
<strong>데이터를 어떤 type으로 명시</strong>해서 전송할 지 정해야 하는 것이다.</p>
<p>데이터의 type을 아는 것은 간단한 한 줄의 코드를 쓰더라도 중요한 건데, 
왜 이렇게 자주 놓치고 지나가는 것인지.. 앞으로 타입을 더 중요하게 생각해야겠다고 느꼈다.</p>
<p>타입을 하나 알게되니, 어떤 부분을 신경써야 하는지가 줄줄이 해결되는게 참 어이없고 재밌다.</p>
<p>내가 선을 안 지켜서 이 사태가 났다는게 너무 웃겼다.
평소에 선 잘지키려고 용을 쓰는 성격인데, 데이터는 너무 만만하게 본 듯 하다ㅋ</p>
<h4 id="reference">reference</h4>
<p>what is form data ? <a href="https://2ham-s.tistory.com/307">https://2ham-s.tistory.com/307</a> 
what is form data ? <a href="https://velog.io/@bsy/FormData">https://velog.io/@bsy/FormData</a>
what is multipart/form-data? <a href="https://velog.io/@bclef25/multipart-form-data-upload">https://velog.io/@bclef25/multipart-form-data-upload</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[send ZIP file from back to front]]></title>
            <link>https://velog.io/@azi_zero/send-ZIP-file-from-back-to-front-qd1joxen</link>
            <guid>https://velog.io/@azi_zero/send-ZIP-file-from-back-to-front-qd1joxen</guid>
            <pubDate>Thu, 01 Jul 2021 07:11:15 GMT</pubDate>
            <description><![CDATA[<h2 id="📨-backend에서-만든-zip파일-front로-보내기-📨">📨 backend에서 만든 zip파일 front로 보내기 📨</h2>
<p><img src="https://images.velog.io/images/azi_zero/post/cc03521b-73a0-41cd-9870-81dc9281fece/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-01%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.01.24.png" alt="">저처럼 고통받는 중생이 없기를 바라며.. 🤦‍♀️</p>
<h4 id="💡-사용-목적">💡 사용 목적</h4>
<p>string 형태의 csv 파일을 하나의 zip 파일로 압축</p>
<h3 id="🌿-개발-환경_1">🌿 개발 환경_1</h3>
<pre><code>back = Express.js
front = React.js</code></pre><h4 id="🌞-사용한-library">🌞 사용한 library</h4>
<p><strong>express-zip</strong>   </p>
<h4 id="🎀-code">🎀 code</h4>
<pre><code class="language-javascript">// backend
const zip = require(&#39;express-zip&#39;)

let zip_file = [];
zip_file.push({
  path: `${local_file_path}`,
  name: `${file_name}`
});
...
res.zip(zip_file, `${zip_file_name}`,);</code></pre>
<pre><code class="language-javascript">// frontend
import {saveAs} from &#39;file-saver&#39;;

const {data} = await axios.post(...,{responseType:&#39;blob&#39;});
const blob = new Blob([data], {type: &#39;application/zip&#39;});
saveAs(blob, filename);</code></pre>
<h3 id="🌿-개발-환경_2">🌿 개발 환경_2</h3>
<pre><code>back = 웹 프레임워크 koa를 사용 (in strapi)
front = react.js 사용</code></pre><h4 id="🌞-사용한-library-1">🌞 사용한 library</h4>
<p><strong>adm-zip</strong></p>
<h4 id="🎀-code-1">🎀 code</h4>
<pre><code class="language-javascript">// backend
const Admzip = require(&#39;adm-zip&#39;);

const zip = new AdmZip(); //create archives
...
zip.addFile(FILENAME, Buffer.from(STRINGDATA, &#39;utf-8&#39;)); //add file directly
const bufferResult = zip.toBuffer(); 

ctx.send(bufferResult);</code></pre>
<pre><code class="language-javascript">// frontend
import {saveAs} from &#39;file-saver&#39;;

const {data} = await axios.post(...,{responseType:&#39;arraybuffer&#39;});
const blob = new Blob([data], {type: &#39;application/zip&#39;});
saveAs(blob, filename);</code></pre>
<h3 id="🧱-삽질-이유-제일-중요">🧱 삽질 이유 (제일 중요)</h3>
<p>가장 근본적인 원인은 express.js기반의 백엔드 코드를 koa 웹 프레임워크로 옮기면서 발생!
삽질의 세부적인 원인을 살펴보면...</p>
<ol>
<li>koa를 사용하기 때문에 express-zip을 쓸 수 없음</li>
<li>express-zip 라이브러리가 관리가 안된지 워낙 오래됨</li>
<li>JSZip라이브러리는 이 플랫폼(koa?)에서 blob을 지원안해줌</li>
<li>blob을 쓰고 싶음( 💩 똥고집 💩 )</li>
<li>csv파일을 로컬에 저장해서 불러오기보다 csv가 생성되면 바로 zip에 넣어주고 싶었음</li>
</ol>
<p>하나씩 되짚어보며 앞으로 같은 실수를 하지 않도록! ( 지금부터는 혼잣말 )</p>
<p>*<em>🚧 🚧 🚧 🚧 🚧 🚧 🚧 🚧 *</em>
1~2 <span style="color:blue">short</span>
express-zip은 사실 너무 편했다...
csv파일 생성하고 같은 array에 넣어주고 res.zip으로 보내주기만 하면되니까...
그런데 너무 관리가 오랫동안 안된 라이브러리이기도 했고,
애초에 기반 프레임워크가 달라졌으니 바꿀 수 밖에 없었다!</p>
<p><strong>🚧 🚧 🚧 🚧 🚧 🚧 🚧 🚧 **
3~4 <span style="color:red">long</span>
가장 먼저, **JSZip</strong>에 매달린 이유는 다운로드수가 가장많았고... (대세를 따르는게 안전하다는 생각에..?)
예시코드를 보고 프론트단에서 아무 파일이나 생성해서 실행해보니 zip파일이 잘 만들어지더라..</p>
<pre><code class="language-javascript">// in backend
zip.generateAsync({type:&quot;blob&quot;}).then(function(content) {
    ctx.send(content)
});</code></pre>
<p>이 코드를 적으니 돌아오는 👺 에러놈..
<strong>blob is not supported by this platform</strong>
해당 에러 내용에 대해 열심히 구글링해보니 여기저기에서 이 플랫폼에서 blob을 쓸 수 있게 해주는 코드를 공유하고 있는 상황 발견.. 여기서부터 💩 고집 시작 (젭알 멈춰...plz..)
🐤 : &#39;그래! 쓸 수 있게 해주는 코드가 몇개 보이니까 나도 쓸 수 있게 잘 써먹어보자!&#39;
<img src="https://images.velog.io/images/azi_zero/post/59ab640f-d6e7-4fc7-91aa-c8266b32fb58/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-01%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.33.17.png" alt=""></p>
<p>🐤 : blob.. binary large object.. 나 사실 얘가 type이라는 것만 알지 아무것도 모르는데 내가 얘 쓰고싶다고 얘가 여기에서 잘 쓰여줄까..? 
그러다 확인사살의 순간에 <strong>JSZip은 저희와 함께할 수 없게되었습니다</strong></p>
<pre><code class="language-javascript">console.log(JSZip.support);
//출력문
{
  base64: true,
  array: true,
  string: true,
  arraybuffer: true,
  nodebuffer: true,
  uint8array: true,
  👀 blob: false,👀
  nodestream: true
}</code></pre>
<p>보고싶지도.. 믿고싶지도 않았는데 수많은 true들 사이에서 슬프도록 빛나는 false놈...</p>
<p>🐤 : &#39;여기서 지면 나는 평생 zip의 패배자다라는 마인드로 바로 회선변경&#39;</p>
<p> <strong>adm-zip설치</strong>
역시 1등보단 2등이 인간미 넘치고 괜히 정감가고 친해지고싶고 그런걸까..
예시코드를 봤는데 너무너무 쉬워보이잖아!!!</p>
<pre><code class="language-javascript">var zip = new AdmZip();

// add file directly
var content = &quot;inner content of the file&quot;;
zip.addFile(&quot;test.txt&quot;, Buffer.alloc(content.length, content), &quot;entry comment goes here&quot;);

// get everything as a buffer
var willSendthis = zip.toBuffer();</code></pre>
<p>*<em>🚧 🚧 🚧 🚧 🚧 🚧 🚧 🚧 *</em>
5 <span style="color:blue">short</span></p>
<p>원래 express 환경에서 작성한 csv 다운로드 코드는 
하나의 csv파일 안에 들어갈 데이터를 다 갖추면 파일을 생성해서 local에 저장하고, 
local에 저장한 파일들을 압축하는 방식이었는데,
strapi 특성상 파일이 바뀌면 매번 서버가 재시작되기 때문에 아주 곤란쓰하고 머리가 아팠다..</p>
<p>처음에는 ✨ <strong>.tmp 폴더에 넣고 gitignore을 통해서 인식하지않게</strong> ✨ 할 수 있었는데
그렇게되면 git에 push하고 다른 개발자가 쓸 때에는 .tmp를 만들어줘야하는
사실 그렇게 귀찮지는 않지만 뭔가 거슬리는 지점이 있었기에..!
string형태로 저장해서 파일 생성없이 바로 zip파일에 파일형태로 저장되는 것이 필요했다!</p>
<pre><code class="language-javascript">zip.addFile(&quot;test.txt&quot;, Buffer.alloc(content.length, content), &quot;entry comment goes here&quot;);</code></pre>
<p>이 아름다운 코드 한 줄...
나의 모든 needs를 해결해주는 🔅 빛 🔅 그리고 🔅 빛 🔅 그저  🔅 빛 🔅</p>
<p>사실 api 문서는 엄청 읽기 싫게 생겨서 크게 맘에 들지는 않았는데
긴가민가하며 코드를 적어보았더니!!!!!
<img src="https://images.velog.io/images/azi_zero/post/a29f1fe3-6703-494e-91d9-7e4992e076cf/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-01%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.59.25.png" alt=""></p>
<p>원하는 zip파일에 원하는 파일들이 쏙쏙 예쁘게 들어가서 아주 예쁜 파일이 다운받아졌다!!!!!!!!!</p>
<h3 id="🧐-더-알아봐야-할-부분">🧐 더 알아봐야 할 부분</h3>
<ul>
<li>XMLHttpRequest.responseType 공부하기</li>
<li>blob, arraybuffer 공부하기 (arraybuffer쓴 이유.. 그냥 buffer들어있길래..)</li>
</ul>
<h3 id="👊-remind">👊 remind</h3>
<ul>
<li>api 읽기 싫게 생겼다고 대충 읽지 말자</li>
<li>너무 삽질해서 지칠때는 일단 지금은 하지말자
  6시간동안 zip에 시달리고 꼴도보기 싫어서 바로 접고 다음날 다시 시작했더니 성공!
  🙈 *<em>꼴보기 싫을 땐 꼴보지 말아야 하는 법칙 *</em> 🙈  이번에도 증명 성공</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[MobX + TypeScript + React 로 슈퍼마켓 구현하기 (2)]]></title>
            <link>https://velog.io/@azi_zero/MobX-TypeScript-React-2</link>
            <guid>https://velog.io/@azi_zero/MobX-TypeScript-React-2</guid>
            <pubDate>Tue, 08 Jun 2021 07:18:57 GMT</pubDate>
            <description><![CDATA[<h1 id="👩💻들어가며">👩‍💻들어가며</h1>
<p>첫번째 포스팅에서 화면 뼈대 구성까지 진행하였습니다.
큰 어려움은 없었는데요!
🐣이번에는 <strong>store</strong>를 만들어보고자 합니다🐣</p>
<h3 id="💻-슈퍼마켓-구현하기-2-요약">💻 슈퍼마켓 구현하기 (2) 요약</h3>
<p> 슈퍼마켓 구현하기(2) 포스트는 상태관리를 위한 store를 만들고, 각각의 store들을 rootStore를 통해서 연결시켜주는 내용을 담고있습니다.</p>
<h1 id="코드-작성">코드 작성</h1>
<p>mobX 공식문서를 읽어보니, decorator는 ES standard가 아니라서 잘 안쓴다고 나와있더라구요! 그래서 <strong>makeObservable</strong>을 이용해서 annotation을 설정하도록 하겠습니다.</p>
<p>(저는 decorator가 더 귀엽고 아기자기한데.. 귀여운게 밥먹여주지는 않으니 😏)</p>
<h2 id="👨🌾-step-1-판매상품-store-만들기">👨‍🌾 step 1. 판매상품 store 만들기</h2>
<ul>
<li>ProductStore에서 Product가 먼저 생성, 저장되니 상품 interface를 해당 파일에서 export시켜줍니다.</li>
<li>mobX에서 return값은 <strong>proxy</strong>형태이니,** computed에서는 toJS를 씌워<strong>서 계산 값을 **반환</strong>시켜줍니다.</li>
</ul>
<p><strong>src/stores/ProductStore.ts</strong></p>
<pre><code class="language-javascript">import {makeObservable, action, observable, computed, toJS} from &#39;mobx&#39;;

export interface Product{
    id: number;
    name: string; 
    price: number;
    choice: number;
}

export default class ProductStore{
    constructor(){
        makeObservable(this,{
            productList: observable,
            addProduct: action,
            removeProduct: action,
            getProducts: computed,
            getProductsNum: computed,
        })
    }
    productList: Product[] =[
        {id:0, name:&#39;매운새우깡&#39;, price:1800, choice:1},
        {id:1, name:&#39;콘쵸&#39;, price: 1200, choice: 1},
        {id:2, name:&#39;허니버터칩&#39;, price:1500, choice:1}
    ]

    addProduct(newProduct: Product){
        this.productList = [...this.productList, newProduct]
    }

    removeProduct(id: number){
        this.productList.splice(id, 1)
    }

    get getProducts(){
        return toJS(this.productList);
    }

    get getProductsNum(){
        return toJS(this.productList.length)
    }
}</code></pre>
<h3 id="step1-정리-🥕">step1 정리 🥕</h3>
<ol>
<li>makeObservable사용</li>
<li>Product interface 정의</li>
<li>Array.push()대신 spread Syntax 사용</li>
<li>return값은 toJS로 처리함으로써 proxy 반환 방지</li>
</ol>
<h2 id="👨🌾-step-2-장바구니-store-만들기">👨‍🌾 step 2. 장바구니 store 만들기</h2>
<ul>
<li>ProductStore과 비슷하게 makeObservable로 annotation을 설정해줍니다.</li>
<li>Product interface를 import 합니다.</li>
<li>BasketStore에서는 item과 관련하여 3가지 기능이 필요합니다.
  -item 추가 (add + update)
  -item 빼기 (1개씩 choice 감소)
  -item 제거 (list에서 삭제)</li>
</ul>
<p><strong>src/srotes/BasketStore.ts</strong></p>
<pre><code class="language-javascript">import {makeObservable, action, observable, computed, toJS} from &#39;mobx&#39;;
import {Product} from &#39;./ProductStore&#39;;

export default class BasketStore {
    constructor(){
        makeObservable(this,{
            itemList: observable,
            totalPrice: observable,
            updateItem: action,
            returnItem: action,
            removeItem: action,
            setTotalPrice: action,
            getItems: computed,
            getTotalPrice: computed,
        })
    }
    itemList: Product[]=[];
    totalPrice: number = 0;

    updateItem(item: Product){
        const found = this.getItems.findIndex((el)=&gt;el.id === item.id);

        if(found&gt;=0) this.itemList[found].choice++;
        else this.itemList = [...this.itemList, item];

        this.setTotalPrice();
    }

    returnItem(id: number){
        this.itemList = this.itemList.map((item)=&gt;{
            if(item.id === id) item.choice--;
            return item;
        })
        this.setTotalPrice();
    }

    removeItem(id: number){
        const idx = this.itemList.findIndex(el=&gt;el.id===id);
        this.itemList.splice(idx,1);
        this.setTotalPrice();
    }

    setTotalPrice(){
        this.totalPrice = this.itemList.reduce((acc: number, current: Product)=&gt;{
            return acc + (current.price * current.choice);
        }, 0)
    }

    get getItems(){
        return toJS(this.itemList);
    }

    get getTotalPrice(){
        return toJS(this.totalPrice);
    }
}</code></pre>
<h3 id="step2-정리-🥕">step2 정리 🥕</h3>
<p>step1과 동일</p>
<h2 id="👨🌾-step-3-rootstore-설정하기">👨‍🌾 step 3. rootStore 설정하기</h2>
<ul>
<li>모든 store들을 연결해주는 하나의 rootStore를 만들어줍니다.</li>
<li>각각의 store들은 rootStore를 매개로 필요한 값을 주고받을 수 있습니다.</li>
</ul>
<ul>
<li>✨<strong>rootStore 적용 방법</strong>✨<ol>
<li>rootStore 생성  _ src/stores/rootStore.ts </li>
<li>Provider로 rootStore를 프로젝트에 적용 _ src/index.tsx</li>
<li>각 store component에서 constructor argument로 rootStore 받기 _ src/stores/*</li>
</ol>
</li>
</ul>
<p><strong>1. rootStore생성</strong>✨</p>
<p><strong>src/stores/rootStore.ts</strong></p>
<pre><code class="language-javascript">import BasketStore from &#39;./BasketStore&#39;;
import ProductStore from &#39;./ProductStore&#39;;

export default class RootStore{
    constructor(){
        this.basketStore = new BasketStore(this);
        this.productStore = new ProductStore(this);
    }

    basketStore: BasketStore;
    productStore: ProductStore;
}</code></pre>
<p>Store 인스턴스 생성시 꼭 this를 넣어줘야 rootStore를 통한 다른 store로 접근이 가능해집니다!!</p>
<p><strong>2. Provider로 rootStore를 프로젝트에 적용</strong>✨</p>
<p><strong>src/index.tsx</strong></p>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;;
import App from &#39;./App&#39;;

import {Provider} from &#39;mobx-react&#39;
import RootStore from &#39;./stores/rootStore&#39;

const rootStore = new RootStore();
ReactDOM.render(
  &lt;Provider {...rootStore}&gt;
    &lt;App /&gt;
  &lt;/Provider&gt;

,
  document.getElementById(&#39;root&#39;)
);</code></pre>
<p><strong>3. rootStore를 constructor argument로 지정</strong>✨
constructor의 argument로 rootStore를 받아줍니다!
<strong>src/stores/BasketStore.ts</strong></p>
<pre><code class="language-javascript">...
import RootStore from &#39;./rootStore&#39;;
...
export default class BasketStore{
    constructor(rootStore: RootStore){
        ...
    }  
    ...
 }
</code></pre>
<p><strong>src/stores/ProductStore.ts</strong></p>
<pre><code class="language-javascript">...
import RootStore from &#39;./rootStore&#39;;
...
export default class ProductStore{
    constructor(rootStore: RootStore){
        ...
    }  
    ...
 }
</code></pre>
<h3 id="step3-정리-🥕">step3 정리 🥕</h3>
<ol>
<li>rootStore에서 this를 넘겨주어야 store끼리 상호작용 가능</li>
<li>Provider로 프로젝트에 store적용</li>
<li>store를 정의하고 있는 각각의 class component는 생성자의 argument로 rootStore꼭 받아줘야함! (타입 정의 까지!)</li>
</ol>
<h1 id="🤷♀️-지금까지-궁금한-내용-🤷♀️">🤷‍♀️ 지금까지 궁금한 내용 🤷‍♀️</h1>
<ul>
<li>Provider의 디테일한 사용법</li>
<li>interface를 모아두는 파일을 만들어서 쓰는 것이 효율적인지?</li>
<li>spread syntax는 어떤 때에 어떤 이유로 사용해야 하는지?
   -&gt; JS스럽게 쓰는 것(reference에서 참고함)이 잘 쓰는 것인지?<h1 id="reference">Reference</h1>
✨ Spread-syntax
<a href="https://velog.io/@kwonh/ES6-%ED%8E%BC%EC%B9%A8%EC%97%B0%EC%82%B0%EC%9E%90%EC%A0%84%EA%B0%9C%EC%97%B0%EC%82%B0%EC%9E%90-Spread-Syntax-Spread-Operator">https://velog.io/@kwonh/ES6-%ED%8E%BC%EC%B9%A8%EC%97%B0%EC%82%B0%EC%9E%90%EC%A0%84%EA%B0%9C%EC%97%B0%EC%82%B0%EC%9E%90-Spread-Syntax-Spread-Operator</a>
✨ JS스럽게 코드 쓰는 법
<a href="https://velog.io/@wooder2050/JS%EC%8A%A4%EB%9F%BD%EA%B2%8C-%EC%A2%8B%EC%9D%80-%EC%BD%94%EB%93%9C-%EC%93%B0%EA%B8%B0-%EA%BF%80%ED%8C%81">https://velog.io/@wooder2050/JS%EC%8A%A4%EB%9F%BD%EA%B2%8C-%EC%A2%8B%EC%9D%80-%EC%BD%94%EB%93%9C-%EC%93%B0%EA%B8%B0-%EA%BF%80%ED%8C%81</a>
✨ 슈퍼마켓 구현하기
<a href="https://hyeok999.github.io/2020/04/16/mobx-hooks-market/">https://hyeok999.github.io/2020/04/16/mobx-hooks-market/</a>
<a href="https://velog.io/@velopert/MobX-3-%EC%8B%AC%ED%99%94%EC%A0%81%EC%9D%B8-%EC%82%AC%EC%9A%A9-%EB%B0%8F-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95-tnjltay61n">https://velog.io/@velopert/MobX-3-%EC%8B%AC%ED%99%94%EC%A0%81%EC%9D%B8-%EC%82%AC%EC%9A%A9-%EB%B0%8F-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95-tnjltay61n</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[MobX + TypeScript + React 로 슈퍼마켓  구현하기 (1)]]></title>
            <link>https://velog.io/@azi_zero/MobX-TypeScript-React-1</link>
            <guid>https://velog.io/@azi_zero/MobX-TypeScript-React-1</guid>
            <pubDate>Tue, 08 Jun 2021 05:00:37 GMT</pubDate>
            <description><![CDATA[<h1 id="👩💻-들어가며">👩‍💻 들어가며</h1>
<p>저는 🐥<strong>삐약이 개발자</strong>🐥 입니다. 
오늘은 그동안 다뤄보고싶었던 프로그래밍언어, 라이브러리를 이용해서 아주아주 간단한 슈퍼마켓 🛒 기능을 구현해보고자 합니다. </p>
<p>이런 글을 ❗️처음❗️ 써보는 터라 어디까지가 TMT가 되지않는 적절한 선인지 모릅니다..
삐약이 개발자는 ✨유익하며 친절한✨ 포스팅을 좋아하기 때문에 최대한 자세히 적어보도록 하겠습니다.</p>
<h3 id="🐣-목표">🐣 목표</h3>
<ol>
<li>TypeScript와 친해지기</li>
<li>mobX에서 rootStore에 대한 감 익히기</li>
<li>css를 이용해서 styling 구성하기</li>
</ol>
<h3 id="💻-슈퍼마켓-구현하기-1-요약">💻 슈퍼마켓 구현하기 (1) 요약</h3>
<p>슈퍼마켓 구현하기 (1) 포스트는 기본 설정, 구조 계획 그리고 화면 뼈대 구성에 관한 내용을 담고있습니다.</p>
<h1 id="코드-작성">코드 작성</h1>
<h2 id="👨🌾-step-1----기본-설정">👨‍🌾 step 1  - 기본 설정</h2>
<p><em>TypeScript 기반 React앱 생성하기</em></p>
<pre><code>npx create-react-app supermarket --template typescript</code></pre><p><em>mobX설치하기</em></p>
<pre><code>cd supermarket
npm i mobx-react</code></pre><p><em>불필요한 파일 제거 &amp; 필요한 파일 생성</em></p>
<pre><code>rm -r src/ 
mkdir src
cd src
mkdir components css stores
touch App.tsx index.tsx</code></pre><p><strong>supermarket/src/App.tsx</strong></p>
<pre><code class="language-javascript">import React from &#39;react&#39;;
function App() {
  return (
    &lt;div className=&quot;App&quot;&gt;
    &lt;/div&gt;
  );
}
export default App;</code></pre>
<p><strong>supermarket/src/index.tsx</strong></p>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;;
import App from &#39;./App&#39;;

ReactDOM.render(
  &lt;App /&gt;
,
  document.getElementById(&#39;root&#39;)
);
</code></pre>
<h3 id="step1-정리-🥕">step1 정리 🥕</h3>
<ol>
<li>typescript 기반 react 앱(supermarket) 생성</li>
<li>mobX 설치</li>
<li>components, css, stores 디렉토리 생성
 해당 폴더들은 각 기능에 맞는 파일들이 위치하는 곳입니다.</li>
<li>빈 화면 생성</li>
</ol>
<p>자, 이제 기본적인 설정이 끝났습니다!
디렉토리 및 파일 생성은 명령어를 꼭 사용하지 않아도 됩니다.
(제가 아는 명령어가 몇개 없어서 써먹을 수 있는건 최대한 써먹어 보려구요..🤦‍♀️) </p>
<p>📑...잠깐 유사과학
저의 mbti에는 P가 들어있는데
왜 이렇게 코드만 짜려고 하면 계획을 세우고 싶어질까요..</p>
<p>그렇기에 본격적인 코드 작성에 앞서서 
구조를 간단히 생각해보고 들어가고자 합니다.😎</p>
<h2 id="👨🌾-step-2---구조-생각해보기">👨‍🌾 step 2 - 구조 생각해보기</h2>
<h3 id="21-목표-ui-설정">2.1 목표 UI 설정</h3>
<p>제가 생각한 UI는 아래와 같습니다.
아래 사진은 최종 결과물이지만, 제일 처음에는 여러 곳에서 레퍼런스를 한 후 간단한 그림👩‍🎨으로 시작하였습니다. 
분석해보면, 크게 *<em>판매상품 list + 장바구니 list + 총 가격 *</em> 이 필요함을 알 수 있습니다.</p>
<p><img src="https://images.velog.io/images/azi_zero/post/75b8c48d-5700-4a59-98a2-7c47f603d159/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-06-08%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.58.37.png" alt=""></p>
<h3 id="22-세부-구조-설정--store-공통-interface--">2.2 세부 구조 설정 ( store, 공통 interface  )</h3>
<p>상태관리 라이브러리, mobX에서 사용될 store의 기본적인 구조를 생각해 보았습니다. 또한 store에서 공통적으로 사용될 interface도 생각해 보았습니다.</p>
<p>-공통 <strong>interface</strong> 
    Item {id: number; name: string; price: number; choice: number }</p>
<p>-판매상품 <strong>store</strong>
        addProduct, removeProduct, productList</p>
<p>-장바구니 <strong>store</strong>
     addItem, removeItem, ItemList, totalPrice</p>
<p>이 정도의 기능이 필요할 것 같군요! </p>
<h3 id="step2-정리-🥕">step2 정리 🥕</h3>
<ol>
<li>목표 UI 스케치 해보기</li>
<li>주요 상태 관리 구조 생각해보기</li>
</ol>
<p>이제 진짜 본격적으로  🛒슈퍼마켓🛒을 구현해보겠습니다!</p>
<h2 id="👨🌾-step-3----마켓-화면-뼈대-구현">👨‍🌾 step 3  - 마켓 화면 뼈대 구현</h2>
<p><strong>src/components/Market.tsx</strong></p>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import &#39;../css/Market.css&#39;
const SuperMarket = () =&gt;{
    return(
        &lt;div className=&quot;MarketTemplate&quot;&gt;
            &lt;div className=&quot;Market&quot;&gt;
                &lt;div className=&quot;products-wrapper&quot;&gt;
                    &lt;h2&gt;판매상품&lt;/h2&gt;
                    {/* ProductList */}
                &lt;/div&gt;
                &lt;div className=&quot;basket-wrapper&quot;&gt;
                    &lt;h2&gt;장바구니&lt;/h2&gt;
                    {/* BasketList */}
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div className=&quot;AddProduct&quot;&gt;
                &lt;h4&gt;add&lt;/h4&gt;
                {/* addProduct */}
            &lt;/div&gt;
        &lt;/div&gt;
    )
}

export default SuperMarket;</code></pre>
<p><strong>src/css/Market.css</strong></p>
<pre><code class="language-css">.Market {
  width: 768px;
  border: 1px solid black;
  display: flex;
  flex-direction: row;
  margin-left: auto;
  margin-right: auto;
  margin-top: 3rem;
  flex: 1;
}

.Market h2 {
  margin-top: 0;
}

.Market &gt; div {
  padding: 1rem;
  flex: 1;
}

.Market .products-wrapper {
  background: #f8f9fa;
}

.AddProduct {
  width: 384px;
  border: none;
  margin-left: auto;
  margin-right: auto;
  position: relative;
  left: -192px;
}
.MarketTemplate {
  display: flex;
  flex: 1;
  flex-direction: column;
  margin-left: auto;
}

.AddProduct h4 {
  margin: 0;
  margin-top: 3px;
}
</code></pre>
<p><strong>src/App.tsx</strong><br>    작성한 MarketScreen을 삽입해줍니다.</p>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import MarketScreen from &#39;./components/Market&#39;
function App() {
  return (
    &lt;div className=&quot;App&quot;&gt;
       &lt;MarketScreen /&gt; 
    &lt;/div&gt;
  );
}
export default App;</code></pre>
<p>여기까지 작성하고 나면, 아래와 같은 화면을 볼 수 있습니다!
큰 뼈대는 다 완성이 되었습니다!
<img src="https://images.velog.io/images/azi_zero/post/58dd1646-e843-4c91-90d2-de6a3a65b7db/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-06-08%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.36.32.png" alt=""></p>
<h3 id="step3-정리-🥕">step3 정리 🥕</h3>
<ol>
<li>div 블록 태그 이름 지정, css 설정</li>
<li>메인 화면으로 설정</li>
</ol>
<h1 id="🤷♀️-지금까지-궁금한-내용-🤷♀️">🤷‍♀️ 지금까지 궁금한 내용 🤷‍♀️</h1>
<ul>
<li>css 정확한 작성법
  style작성을 ReactNative 에서 시작한 터라 아직 올바르게 작성하는 법 제대로 모름</li>
<li>flex, position 의미
  매번 검색해보는데 매번 헷갈리는 부분</li>
</ul>
<h1 id="reference">Reference</h1>
<p>✨ div, span
<a href="https://analysis-flood.tistory.com/142#:~:text=%ED%83%9C%EA%B7%B8%EB%8A%94%20%EB%B8%94%EB%A1%9D%20%ED%83%9C%EA%B7%B8,%EC%99%80%20%EB%86%92%EC%9D%B4%EB%8A%94%20%EC%A7%80%EC%A0%95%ED%95%A0%20%EC%88%98%20%EC%97%86%EB%8B%A4">https://analysis-flood.tistory.com/142#:~:text=%ED%83%9C%EA%B7%B8%EB%8A%94%20%EB%B8%94%EB%A1%9D%20%ED%83%9C%EA%B7%B8,%EC%99%80%20%EB%86%92%EC%9D%B4%EB%8A%94%20%EC%A7%80%EC%A0%95%ED%95%A0%20%EC%88%98%20%EC%97%86%EB%8B%A4</a>.</p>
<p>✨ 슈퍼마켓 구현하기
<a href="https://hyeok999.github.io/2020/04/16/mobx-hooks-market/">https://hyeok999.github.io/2020/04/16/mobx-hooks-market/</a>
<a href="https://velog.io/@velopert/MobX-3-%EC%8B%AC%ED%99%94%EC%A0%81%EC%9D%B8-%EC%82%AC%EC%9A%A9-%EB%B0%8F-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95-tnjltay61n">https://velog.io/@velopert/MobX-3-%EC%8B%AC%ED%99%94%EC%A0%81%EC%9D%B8-%EC%82%AC%EC%9A%A9-%EB%B0%8F-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95-tnjltay61n</a></p>
]]></description>
        </item>
    </channel>
</rss>