<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>BRAVE GISELE</title>
        <link>https://velog.io/</link>
        <description>한약은 거들뿐</description>
        <lastBuildDate>Tue, 09 Aug 2022 15:40:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>BRAVE GISELE</title>
            <url>https://velog.velcdn.com/images/ouo_yoonk/profile/ba4ee511-bec4-4b2f-80d8-7c1abc7e4374/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. BRAVE GISELE. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ouo_yoonk" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[2022년 6,7월 회고]]></title>
            <link>https://velog.io/@ouo_yoonk/2022%EB%85%84-67%EC%9B%94-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@ouo_yoonk/2022%EB%85%84-67%EC%9B%94-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 09 Aug 2022 15:40:16 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ouo_yoonk/post/c5003259-6874-4920-9b96-051661417bd3/image.png" alt=""></p>
<p>상반기가 지나버렸다. 굵직굵직한 일들이 많았고, 바빴지만 의미있는 시간을 보낸 6,7월, 상반기 회고를 해보자</p>
<h1 id="fact">Fact</h1>
<h2 id="파트너센터-대시보드-내재화">파트너센터 대시보드 내재화</h2>
<p>퀵사이트로 개발되어있던 파트너센터의 대시보드를 자체 개발했다. 차트 라이브러리는 POC를 통해 Rechart로 결정했는데, 리액트 컴포넌트 기반이라 사용성이 좋다고 판단했기 때문이다</p>
<p>각종 select box로 이루어진 control 작업에 시간을 예상보다 많이 썼는데 그 이유는 디자이너분이 원하셨던 모양대로 antd에서 구현하기가 어려웠기 때문이다. 최대한 많은 antd 레퍼를 찾고, 다른 select 라이브러리도 찾아봤는데 적합한 것을 찾지 못했다. 혼자 해결해보려다가 공유가 늦어져서 전체 개발 일정이 늦어졌다. 디자이너와의 작업이 처음이라 얼라인을 맞추는 과정이었지만, 공유를 잘하자!는 것을 다시 배웠다. 그리고 select box 작업을 하면서 타입스크립트의 제네릭이나 재사용성이 높은 컴포넌트, headless 컴포넌트에 대해서도 공부를 했다. </p>
<p>차트나 테이블의 데이터가 여러 유형이었지만, fetch 한 번으로 전체 raw data를 받아와 각 차트에 맞게 데이터를 가공하는 작업을 프론트에서 했다. 항목별로 group by를 비롯한 처리가 많이 필요해서 lodash 라이브러리를 사용했다. 솔직히 로직을 다 이해하진 못했고, 함수형 프로그래밍을 공부해야 할 필요성을 느꼈다.</p>
<p>그렇게 자체 개발한 대시보드는 5초 이상 걸리던 초기 로딩속도가 2초 이내로 줄었고, 디자인도 깔끔해지고 가독성이 좋아졌다. </p>
<h2 id="면접준비와-퇴사-그리고-이직">면접준비와 퇴사, 그리고 이직</h2>
<p>회사 다닌지 1년이 되어가고, 개발자로 일한지 만 2년이 되면서 실력에 대한 고민, 커리어에 대한 고민이 또 스멀스멀 피어났다. 그래서 멘토링도 받고, 온라인 컨퍼런스도 참여하면서 액션 아이템을 정하고, 한편으로는 여러 회사에 지원을 했다. 연봉협상도 앞두고 있었기 때문에 1년동안 회사에서 한 일, 혼자 공부한 것들을 정리할 수 있었고, 과제 전형이나 면접을 보면서 부족한 부분을 채울 수 있었다. 실무를 할지, 기초 이론을 공부할지, 실력은 도대체 어떻게 해야 느는지에 대해 갈피를 못 잡고 있었는데 면접 준비를 하면서 이 부분이 많이 해소가 됐다. 재정비 시간을 가지면서 리프레쉬를 했고, 연봉협상도 그럭저럭 잘 했다고 생각했는데, 최종합격 연락을 받았다. 두둥. </p>
<p>좋은 팀원들과 일하고 있었기 때문에 정말정말 고민을 많이 했는데, 더 큰 조직에서 일해보고 싶었고, 프론트엔트 개발자로서 UI/UX 개발과 성능 개선 경험을 하고싶었다. <del>조건도 좀 더 좋았다.</del> 몸이 점점 편해진다고 생각했기 때문에 환경을 바꾸고 싶은 생각도 있어서 조금 갑작스럽지만 이직을 하게되었다. </p>
<h2 id="사이드-프로젝트">사이드 프로젝트</h2>
<p><a href="https://github.com/HanghaeE5">https://github.com/HanghaeE5</a>
공부를 해야지 해야지 하면서도 퇴근하고 뭔가를 하기가 쉽지 않아 의무감과 책임감을 가져야하는 환경에 나를 던져넣기 위해 사이드 프로젝트를 진행했다. 약 6주간 진행했고, 그렇게 힘들지 않을거라고 생각했는데...매일 퇴근 후와 주말까지 엄청나게 코딩을 했다. 오랜만에 코딩에 몰입해서 힘들었고, 즐거웠다. 사이드 프로젝트에 대한 회고는 따로..</p>
<h1 id="find">Find</h1>
<ul>
<li>font family</li>
<li>로직처리는 같은 방식으로 통일성있게 하기</li>
<li>자바스크립트에서의 날짜처리</li>
<li>지속 가능한 성장과 컴포넌트</li>
<li>promise, aync-await</li>
<li>PWA</li>
</ul>
<h1 id="feeling">Feeling</h1>
<h2 id="회고는-3f-방식으로">회고는 3F 방식으로</h2>
<p>정리하는 시간을 가지면서 내가 쓴 회고들도 쭉 봤는데 3F 방식이 나에게는 맞다는 결론을 내렸다.  내가 한 일, 거기서 느낀 것과 배운 것, 공부한 것을 한 눈에 파악할 수 있어서 기록으로서도 가치가 있었고, 성취감도 느낄 수 있었고, 정리하는 것에 대한 동기부여도 된다고 생각했기 때문이다. </p>
<h2 id="첫-회사에서의-1년">첫 회사에서의 1년</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[CORS]]></title>
            <link>https://velog.io/@ouo_yoonk/CORS</link>
            <guid>https://velog.io/@ouo_yoonk/CORS</guid>
            <pubDate>Mon, 13 Jun 2022 13:17:19 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ouo_yoonk/post/fce8a279-331e-4340-8f7b-29640a510bdf/image.png" alt=""></p>
<p>회사에서 작업을 하는데 API 요청할 때 부쩍 CORS가 났다. 정상적으로 요청이 들어가다가 CORS가 터지는 보니 초기 설정문제는 아닌 것 같았고, 원인은 사실 AWS의 IAM 계정 설정 문제로 서버 자체가 죽는게 원인이었다. 회사 문제가 잘 해결된 기념으로 CORS의 개념을 다시 짚고 가고싶어서 공부하고 정리한 글.</p>
<h1 id="개요">개요</h1>
<h2 id="origin출처">Origin(출처)</h2>
<p>웹에서는 출처를 공유하는 것과 관련해 두 가지 정책이 존재한다. <code>CORS(Cross origin resource)</code>는 교차 출처 공유 정책과 <code>SOP(Same Origin Policy)</code> 동일 출처 정책이 있다.</p>
<p>여기서 <code>출처(origin)</code>이란 URL 구조에서 <code>프로토콜</code>+<code>호스트주소</code>+<code>포트번호</code>를 의미한다. 즉, 동일 출처란 프로토콜, 호스트주소, 포트번호가 같은 것을 말한다. </p>
<p><img src="https://velog.velcdn.com/images/ouo_yoonk/post/4dd35ca2-84c3-42bf-8b56-cbdc225affd1/image.png" alt="">
<a href="https://www.grabbing.me/URL-018cdd1bb4b541fab6246569244fcf93">사진출처 - 그랩의 블로그</a></p>
<h2 id="sopsame-origin-poilcy">SOP(Same Origin Poilcy)</h2>
<p>SOP란 다른 출처의 리소스를 사용하는 것을 제한하는 정책이다. XMLHttpRequest와 FetchAPI는 동일 출처 정책을 따른다. 즉, 이 API를 사용하는 웹 어플리케이션은 자신과 동일한 출처의 리소스만 불러올 수 있고, 다른 출처의 리소스를 불러오기 위해서는 그 출처에서 올바른 CORS 헤더를 포함한 응답을 반환해야 한다.</p>
<p>서로 다른 출처의 어플리케이션이 서로 통신하는 것에 대해 아무런 제약이 존재하지 않으면 CSRF(Cross Site Request Forgery)나 XSS(Cross Site Scriping) 공격에 노출될 수 있다. 예를들면, 사용자가 웹사이트에 로그인하여 정상적인 쿠키를 발급 받은 후, 악성코드가 삽입된 링크를 열었을 때, 대상 웹사이트는 믿을 수 있는 사용자가 실행한 것으로 판단한다. </p>
<h2 id="corscross-origin-resource-sharing">CORS(Cross Origin Resource Sharing)</h2>
<p>교차출처 자원공유. 한 출처에서 실행중인 웹 어플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 것을 말한다.</p>
<h1 id="cors의-접근제어-시나리오">CORS의 접근제어 시나리오</h1>
<h2 id="preflight-request">Preflight Request</h2>
<p><img src="https://velog.velcdn.com/images/ouo_yoonk/post/15a0344a-0596-423d-a4eb-9ad7af80df12/image.png" alt="">
<a href="https://blog.bitsrc.io/4-ways-to-reduce-cors-preflight-time-in-web-apps-1f47fe7558">사진출처 - 4 Ways to Reduce CORS Preflight Time in Web Apps</a></p>
<p>브라우저는 요청을 한 번에 보내지 않고 preflight 요청을 보낸 후 실제 요청을 보낸다. preflight request는 <code>option</code> 메서드를 통해 다른 도메인의 리소스에 요청이 가능한지 확인작업을 한다.</p>
<p>preflight의 요청에는 아래와 같이 Origin, Access-Control-Request-Method, Access-Control-Request-Header가 포함되어 있어야하고, 응답은 Code 200, body가 빈 상태이다.</p>
<pre><code>Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
</code></pre><p>preflight 요청이 필요한 이유는 CORS가 서버가 아닌 브라우저에 구현되어 있는 스펙이기 때문이다. CORS 정책을 위반하는 리소스 요청을 하더라도 서버는 정상적으로 응답을 하고, 이후 브라우저가 응답을 분석해 CORS 정책 위반이라고 판단하면, 그 응답을 버리는 순서다. 정책에 위반된 요청이 와도 서버는 판단을 하기 못하기 때문에 이를 방지하기 위해 preflight 요청을 보내 CORS 에러가 나는지를 미리 확인한다.</p>
<h2 id="simple-request">Simple Request</h2>
<p>아래의 조건을 모두 충족했을 때, Preflight 요청 없이 바로 요청을 보낸다</p>
<ol>
<li>메서드 : <code>GET</code> <code>HEAD</code> <code>POST</code></li>
<li>Content Type : <code>application/x-www-form-urlencoded</code> <code>multipart/form-data</code>
<code>text/plain</code></li>
<li>Header : <code>Accept</code> <code>Accept-Language</code> <code>Content-Language</code> <code>Content-Type</code></li>
</ol>
<h2 id="credentialed-request">Credentialed Request</h2>
<p>인증정보를 포함한 요청. credentialed reqeusts는 <code>쿠키</code>와 <code>HTTP Authentication</code> 정보를 인식한다. 기본적으로 cross site XMLHttpRequest나 Fetch 호출에서 브라우저는 자격증명을 보내지 않기때문에 XMLHttpReqeust 객체나 Reqeust 생성자가 호출될 때 특정 플래그를 설정해야 한다.</p>
<ul>
<li>client : <code>withCredentiales</code>를 <code>true</code> </li>
<li>Server : <code>access-control-allow-credentials : true</code>, 
이 때 access-control-allow-origin이 *면 안된다.</li>
</ul>
<h1 id="cors를-해결하는-방법">CORS를 해결하는 방법</h1>
<h2 id="서버에서-header에-access-control-allow-origin-셋팅하기">서버에서 header에 Access-Control-Allow-Origin 셋팅하기</h2>
<p><code>*</code>를 사용해 이 헤더를 세팅하면 모든 출처에서 오는 요청을 받기 때문에 보안상 좋지 않기 때문에 출처를 정확히 명시해주는 것이 좋다.</p>
<h2 id="개발환경에서-프록시-서버-만들기">개발환경에서 프록시 서버 만들기</h2>
<ul>
<li>웹팩<pre><code class="language-javascript">// webpack.config.js
module.exports = {
devServer: {
  proxy: {
    &#39;/api&#39;: &#39;http://localhost:3000&#39;
  }
}
};</code></pre>
</li>
<li>vite<pre><code class="language-javascript">export default defineConfig({
server: {
  proxy: {
    // string shorthand
    &#39;/foo&#39;: &#39;http://localhost:4567&#39;,
    // with options
    &#39;/api&#39;: {
      target: &#39;http://jsonplaceholder.typicode.com&#39;,
      changeOrigin: true,
      rewrite: (path) =&gt; path.replace(/^\/api/, &#39;&#39;)
    },
    // with RegEx
    &#39;^/fallback/.*&#39;: {
      target: &#39;http://jsonplaceholder.typicode.com&#39;,
      changeOrigin: true,
      rewrite: (path) =&gt; path.replace(/^\/fallback/, &#39;&#39;)
    },
    // Using the proxy instance
    &#39;/api&#39;: {
      target: &#39;http://jsonplaceholder.typicode.com&#39;,
      changeOrigin: true,
      configure: (proxy, options) =&gt; {
        // proxy will be an instance of &#39;http-proxy&#39;
      }
    },
    // Proxying websockets or socket.io
    &#39;/socket.io&#39;: {
      target: &#39;ws://localhost:3000&#39;,
      ws: true
    }
  }
}
})</code></pre>
</li>
</ul>
<hr>
<ul>
<li><a href="https://www.youtube.com/watch?v=-2TgkKYmJt4">테코톡 - 나봄의 CORS</a></li>
<li><a href="https://developer.mozilla.org/ko/docs/Web/HTTP/CORS#%EC%9D%B4_%EA%B8%80%EC%9D%80_%EB%88%84%EA%B0%80_%EC%9D%BD%EC%96%B4%EC%95%BC_%ED%95%98%EB%82%98%EC%9A%94">MDN - CORS</a></li>
<li><a href="https://evan-moon.github.io/2020/05/21/about-cors/#sopsame-origin-policy">Evan Moon - CORS는 왜 이렇게 우리를 힘들게 할까</a></li>
<li><a href="https://vitejs.dev/config/#server-proxy">Vite</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022년 5월 회고]]></title>
            <link>https://velog.io/@ouo_yoonk/2022%EB%85%84-5%EC%9B%94-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@ouo_yoonk/2022%EB%85%84-5%EC%9B%94-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 04 Jun 2022 09:17:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ouo_yoonk/post/8f8b1548-b879-4411-8866-32f068a85e64/image.png" alt=""></p>
<p>또 한달이 지났다. 시간 너무 빠르다... 이번 달은 제주도 여행도 갔다오고, 친구들도 많이 만났다. 모든 주말에...6월은 모든 토요일에 집에 있는 것을 목표로 하며 5월 회고 시작</p>
<h1 id="한-것">한 것</h1>
<h2 id="드디어-정산-검수-시스템-인수인계">드디어 정산 검수 시스템 인수인계</h2>
<p>무려 작년 9월?10월부터 부침을 거듭하던 정산 검수 시스템을 드디어 베트남 담당자에게 인수인계 했다! 기획이 여러번 바뀌고, 베트남 측과 트러블도 있었고, 회사 내부적으로도 변화가 있었고, 그러는 와중에 추가 개발을 하며 디벨롭을 하고....끝이 확실히 보이지 않는 긴 시간이었는데 이렇게 결과를 내서 기쁘다! 그래도 정산 시스템 프론트 담당자(?)가 되어 주도적으로 개발하면서 프로덕트에 대한 오너쉽이나 사용성과 코드의 개선점을 찾기 위해 적극적으로 노력하는 태도를 배웠다. 디벨롭을 계속해 정산시스템을 회사에서 가장 중요한 프로덕트 중 하나로 만들고 싶다.</p>
<h2 id="udemy-clean-code-javascript-완강">Udemy clean code javascript 완강</h2>
<p>지하철에서 보내는 시간을 잘 활용하기 위해 여러가지를 시도하고 있는데 이번 달은 유데미 강의를 봤다. 작은 스마트폰 화면으로 서있는 상태로 보기에 무리가 없는 강의여서 그 시간을 잘 활용할 수 있었다. 실용적인 내용이 많아서 아침에 출근하면서 들은 내용을 그 날 바로 회사가서 적용해보기도 하고, 코드를 작성하면서 늘 고민이었던 부분을 많이 해소할 수 있는 좋은 강의였다.
<a href="https://velog.io/@ouo_yoonk/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-udemy">강의정리 링크 </a></p>
<h2 id="헤이-조이스-조이콘과-멘토링">헤이 조이스 조이콘과 멘토링</h2>
<p>커리어가 만 2년이 되어가면서 이런저런 고민이 많았다. 영원한 숙제인것 같은 성장과 지금 당장 부족한 것을 어떻게 찾아서 채워갈지, 앞으로 커리어 로드맵을 어떻게 가져갈지, 결혼-출산-육아 같은 중요한 인생과업과 커리어를 병행할 수 있을지 등.... 조이콘을 보면서 모두들 비슷한 고민을 안고 있고, 그 와중에 무언가를 하는 사람들이 해낸다는 것을 또 한번 느꼈다. 컨퍼런스에서 얻을 수 있는 건 연사들의 경험와 인사이트다. 그것을 녹여 결과를 만들어 내는 것은 결국 나다. 멘토링을 받으면서는 우선 당장 내가 해야할 일을 명확하게 정리할 수 있었다. 여러가지 일들고 고민도 많고 힘든 5월이었는데 리프레시를 할 수 있었고 또 앞으로 나아갈 추진력을 얻었다.</p>
<h1 id="6월-목표">6월 목표</h1>
<ul>
<li>출퇴근 시간에 유데미 react 완벽 가이드 듣기</li>
<li>javascript 이론 공부하기</li>
<li>체력 기르기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vite]]></title>
            <link>https://velog.io/@ouo_yoonk/Vite</link>
            <guid>https://velog.io/@ouo_yoonk/Vite</guid>
            <pubDate>Mon, 02 May 2022 10:29:38 GMT</pubDate>
            <description><![CDATA[<h1 id="모듈과-모듈-번들링">모듈과 모듈 번들링</h1>
<p>양한 빌드툴의 출시 배경 : 네이티브 자바스크립트 모듈</p>
<aside>
💡 네이티브 자바스크립트 모듈???

</aside>

<ul>
<li>번들러가 지원해야하는 기능<ul>
<li>네이티브 자바스크립트 모듈을 위한 일급 객체 지원</li>
<li>타입스크립트 컴파일</li>
<li>JSX</li>
<li>확장 가능한 플러그인 API</li>
<li>내장된 개발 서버</li>
<li>CSS-in-JS 라이브러리를 위한 CSS 번들링</li>
</ul>
</li>
</ul>
<h3 id="기존-존재하는-도구들과의-차이점">기존 존재하는 도구들과의 차이점</h3>
<ul>
<li>기존의 번들러(webpack, Rollup, parcel) : 모두 소스 코드와 node_modules 폴더의 전체 코드 베이스를 번들로 묶고, 빌드 프로세스(babel, typescript,postCSS)를 통해 실행한 다음 번들된 코드를 브라우저에 제공한다. 이 모든 작업으로 인해, 캐싱 및 최적화 작업을 하더라도 대규모의 크롤링을 하는 개발 서버를 느리게 만들 수 있다.</li>
<li>새 번들러(snowpack, vite,wmr) : 브라우저가 삽입 구문을 찾고 http로 해당 모듈을 요청할 때까지 기다린다. 이 요청이 이루어진 후에만 도구는 요청된 모듈과 모듈의 가져오기 트리에 있는 모든 잎 노드에 변환을 적용한 다음 이를 브라우저에 제공한다. 이렇게 하면 개발 서버로 푸시하는 과정에서 작업이 줄어들기 때문에 작업 속도가 빨라진다.</li>
<li>esbuild : 다른 도구들처럼 번들링을 우회하지 않고 비용이 많이 드를 변환을 피하고 병렬화를 활용하며 Go언어를 사용해 빠르게 코드를 작성한다.</li>
</ul>
<h1 id="vite">Vite</h1>
<p>브라우저가 ES modules를 지원함에 따라 일반적인 번들링 과정을 생략한다.</p>
<p>프로젝트의 모든 의존성을 esbuild를 이용해 단일 기본 자바스크립트 모듈로 사전 번들링을 진행한다. 그 후에 많은 캐시 된 HTTP 헤더와 함께 제공된다. 즉, 첫 페이지 로드 후 가져온 의존성을 컴파일, 제공 또는 요청하는 데 시간이 낭비되지 않는다. 또한 Vite는 명확한 에러 메시지들을 제공하고 문제를 해결하기 위해 정확한 코드 블록과 줄 번호를 보여준다. </p>
<ul>
<li>hot module replacement
<img src="https://s3.us-west-2.amazonaws.com/secure.notion-static.com/2676b7ef-6c4d-41e9-9250-e6949a8298cc/%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_2022-04-08_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_12.30.39.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220502%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220502T102049Z&X-Amz-Expires=86400&X-Amz-Signature=506faed58b4c69db7f378e1a40481d51be1a6d0261016dc3932a45ebfbb80b69&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22%25E1%2584%2589%25E1%2585%25B3%25E1%2584%258F%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%25E1%2586%25AB%25E1%2584%2589%25E1%2585%25A3%25E1%2586%25BA%25202022-04-08%2520%25E1%2584%258B%25E1%2585%25A9%25E1%2584%2592%25E1%2585%25AE%252012.30.39.png%22&x-id=GetObject" alt="스크린샷 2022-04-08 오후 12.30.39.png"></li>
</ul>
<p><img src="https://oopy.lazyrockets.com/api/v2/notion/image?src=https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fda266387-64d3-4650-8124-e3b558aa340f%2FFrame_4.png&blockId=83bacbc4-a3a4-43b1-9ffa-eebfee552aa4" alt="bundle-navtiveESM"></p>
<hr>
<p><strong>reference</strong></p>
<ul>
<li><a href="https://ui.toast.com/weekly-pick/ko_20220127">https://ui.toast.com/weekly-pick/ko_20220127</a></li>
<li>원문 : <a href="https://css-tricks.com/comparing-the-new-generation-of-build-tools/">https://css-tricks.com/comparing-the-new-generation-of-build-tools/</a></li>
<li><a href="https://eddie-sunny.tistory.com/107">https://eddie-sunny.tistory.com/107</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022년 4월 회고]]></title>
            <link>https://velog.io/@ouo_yoonk/2022%EB%85%84-4%EC%9B%94-%ED%9A%8C%EA%B3%A0-61vlphfr</link>
            <guid>https://velog.io/@ouo_yoonk/2022%EB%85%84-4%EC%9B%94-%ED%9A%8C%EA%B3%A0-61vlphfr</guid>
            <pubDate>Mon, 02 May 2022 04:38:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ouo_yoonk/post/8935fec3-cdb8-4ce4-8c41-e32c9f88315c/image.png" alt=""></p>
<h1 id="정산-검수-추가-개발-완료-및-파트너-페이지-개발">정산 검수 추가 개발 완료 및 파트너 페이지 개발</h1>
<p>정산 시스템을 계속 디벨롭하고 있다. 정산 검수 주체가 바뀌면서 데이터가 변경되었고, 대응작업을 하며 개발 되어있던 로직을 리팩토링했다. 그리고 주문 검수에 여러 기능을 추가 개발했다. 수정을 무작정하기 전에 소스를 전체적으로 보고 이해하는게 중요하다고 느꼈고, 소스 코드를 잘 읽는 능력과, 다른 사람이 읽어도 이해하기 쉬운 코드를 작성해야겠다고 생각했다. 그래서 내 코드를 리팩토링하면서 연관있는 코드들끼리 묶고, 주석이나 설명을 신경써서 달았다. 또 정산에 대한 문서가 없어서 작성을 했다. </p>
<p>양쪽 테이블을 비교해 같은 데이터가 있으면 맞다-안맞다를 바꾸는 로직을 작성하면서 고생을 많이했는데, 사수님의 도움을 받아 해결했다. map, splice, forEach를 이용해서 복잡하고 메모리 효율도 좋지 못한 로직으로 어렵게 짜고 있던걸 일반 for문과 break를 이용해 심플하게 짤 수 있다는 걸 알았다. for문 보다는 array 함수를 고집하고 있었는데 하나를 고집하기 보다 적합한 툴을 사용해야지. 그래도 array 함수로 리팩토링을 해보자.</p>
<p>또 서로 다른 데이터 타입을 다루지만 같은 기능을 하는 다른 타입의 객체를 리턴해야해 각각 함수를 만들었다. typescript의 <code>as</code>를 새로 알게되고, 데이터 객체만 만드는 함수를 만들어 리팩토링했다!! 타입이 다른 데이터를 처리하는게 항상 어려웠는데 해결하는 하나의 도구를 알게돼서 기분이 좋다! </p>
<h1 id="공부한-것">공부한 것</h1>
<p>길고 긴 통근 시간을 효율적으로 보낼 수 있는 여러가지 시도를 하다가 udemy 강의를 보게되었다. 지금 보는 강의는 javascript clean code 강의인데 javascript의 개념도 다시 정리하고, 당장 코드에 적용해 볼 수도 있는 내용이라 유익하다. </p>
<p>회사에서 rebase 전략을 쓰는데 rebase merge 하면서 항상 비슷한 문제를 만나고, 잘 해결을 못하는 것 같아 rebase가 뭔지 다시 확실히 개념을 공부했다.</p>
<p>회사 프로젝트를 webpack에서 요즘 핫한 vite로 전환했다. 전환하면서 모듈러와 vite에 대해서 공부했다.</p>
<p>프로그래머의 뇌!라는 책을 읽고 있다.</p>
<p>시간내서 따로 블로그에 정리하자..</p>
<h1 id="아쉬움과-해결방법">아쉬움과 해결방법</h1>
<p>nomad coder의 리액트 마스터 강의를 다 듣는게 목표였는데 결국 완강을 하지 못했다. 회사가 이사하면서 출퇴근 시간이 길어지고 지하철에서 꼬박 1시간 20분을 서서오는 피로가 누적되고 있는 것 같다. 피로 - 운동못함-피로의 악순환이어서 퇴근 후의 시간을 잘 활용하지 못했다. 그래서 해결 방법으로 야근!을 활용해보기로 했다. 사정상 매일 야근은 못하지만 일주일에 2~3일은 회사에 남아 필요한 공부를 하고, 지하철 혼잡한 시간을 슬쩍 피해보기로..</p>
<p>책, javascript 공부, toy project, CS 공부 등 하고싶은 것만 많고, 못해서 오는 스트레스가 힘든 한달이었다. 다 할 수 있으면 좋겠지만 그만한 역량이 안 된다는 것을 받아들이기로 했다. ㅠㅠ 그리고 못한게 몇개 있다고 해서 내가 해낸 것까지 폄하하지 말기로. 못한 건 못한대로 받아들이고, 왜 못 했는지를 찾고, 해결하는 방법을 찾아내면 되는 사실 아주 간단한 일인데 못하는 걸 인정하는 것부터가 힘들었던 것 같다. 성장이 더디면 더딘대로...그래도 포기하지 말기.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[클린코드 자바스크립트  - udemy]]></title>
            <link>https://velog.io/@ouo_yoonk/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-udemy</link>
            <guid>https://velog.io/@ouo_yoonk/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-udemy</guid>
            <pubDate>Thu, 28 Apr 2022 00:30:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ouo_yoonk/post/b3673d95-9224-4238-9f55-13c2cf56e27c/image.jpeg" alt=""></p>
<blockquote>
<p>Udemy의 클린코드 자바스크립트 강의를 보고, 내용을 정리하고, 코드에 적용해본 것을 기록한 글</p>
</blockquote>
<h1 id="강의-내용-정리">강의 내용 정리</h1>
<h2 id="변수">변수</h2>
<h3 id="var의-사용을-지양하자">var의 사용을 지양하자</h3>
<p><strong><code>var</code>의 문제점</strong></p>
<ul>
<li>중복 선언이 가능하기 때문에, 예기치 못한 값을 반환할 수 있다.</li>
<li><code>함수 스코프</code>를 가지기 때문에 함수 외부에서 선언한 변수는 모두 전역 변수가 된다.</li>
<li><code>호이스팅</code>이 일어나기 때문에 선언문 이전에 변수를 참조할 수 있고, undefined를 반환한다. </li>
</ul>
<p><strong><code>let</code>과 <code>const</code></strong></p>
<ul>
<li>let : 블록 스코프, 선언은 한번, 할당은 여러번, 선언과 할당을 동시에 하지 않아도 된다.</li>
<li>const : 블록스코프. 선언과 할당을 동시에 해야하고, 재할당을 할 수 없다. 상수값이라면 const를 사용하는 것을 권장한다.</li>
</ul>
<h3 id="호이스팅">호이스팅</h3>
<p>호이스팅은 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다. <code>변수선언</code>과 <code>함수선언</code>이 해당 스코프의 최상단으로 끌어 올려진 것 같은 현상을 말한다. 선언을 undefined로 초기화해 코드에서 변수나 함수를 정의하기 전에 해당 변수/함수를 사용할 수 있다.</p>
<p>다만, <code>let</code>과 <code>const</code>변수는 호이스팅 시에도 변수를 초기화 하지 않지 않고, 함수 선언이 아닌 함수 표현식은 호이스팅이 일어나지 않는다.</p>
<h2 id="타입">타입</h2>
<h3 id="타입검사--typeof-instaceof">타입검사 : typeof, instaceof</h3>
<p>참조형 타입은 typeof로 검사가 어렵다. null도 object로 나온다. 따라서 참조형 타입은 instanceof로 프로토 타입 체인을 확인하는 것이 좋다.</p>
<h3 id="eqeq-대신-eqeqeq">eqeq(==) 대신 eqeqeq(===)</h3>
<p><code>==</code>는 자동으로 형변환이 일어난다. <code>===</code>은 strinct eqe이므로 값과 자료형을 함께 비교한다.</p>
<pre><code class="language-javascript">1==&#39;1&#39; // true
1===&#39;1&#39; // false</code></pre>
<h3 id="isnan">isNaN</h3>
<p><code>isNaN()</code>는 넘어오는 인자를 number로 변환을 시도한다. </p>
<pre><code class="language-javascript">Number(null) // 0
Number(&#39;&#39;) // 0
Number(&#39;   &#39;)// 0
Number(false) // 0</code></pre>
<p>따라서 null, &#39;&#39;,&#39; &#39;,false 등의 값이 0을 반환하므로 아래와 같은 결과가 나온다.</p>
<pre><code class="language-javascript">isNaN(null) // false
isNaN(&#39;&#39;) // false
isNaN(&#39;   &#39;)// false
isNaN(false) // false</code></pre>
<p>따라서 숫자 타입 검사를 할 때는 <code>Number.isNaN()</code>으로 검사하는 걸 추천한다. Number.isNaN() 함수는 Numver형에서만 사용가능하고, 강제로 Number 변환을 시도하지 않는다.</p>
<p>Number.isNaN의 함수는 아래와 같다.</p>
<pre><code class="language-javascript">Number.isNaN = function(x){
    return typeof x === &#39;number&#39; &amp;&amp; isNaN(x);
}</code></pre>
<h2 id="경계다루기">경계다루기</h2>
<ul>
<li>포함여부(이상, 초과, 미만, 이하)를 나타낼 수 있는 변수를 사용한다.</li>
<li>매개변수 순서가 경계가 될 수 있다.</li>
<li>호출하는 함수의 네이밍과 인자의 순서의 연관성을 고려한다</li>
<li>매개변수를 2개가 넘지 않도록하고, 매개변수를 객체에 담아서 넘긴다.</li>
<li>랩핑하는 함수를 만든다</li>
</ul>
<h2 id="분기다루기">분기다루기</h2>
<h3 id="값식문을-구분하기">값식문을 구분하기</h3>
<ul>
<li>리액트의 jsx문 안에는 값과 식만 들어가야한다. 
ex] for문, if문을 넣지않음</li>
</ul>
<h3 id="truthy와--참같은-값-falthy--거짓같은-값">truthy와 = 참같은 값, falthy = 거짓같은 값</h3>
<ul>
<li>falthy : false null undefined 0 NaN &#39;빈 문자열&#39;</li>
</ul>
<h3 id="short-circuit-evaluation-단축평가">short-circuit evaluation 단축평가</h3>
<ul>
<li>and는 모두가 참이어야 도달하고, or는 하나만 참이어도 됨<pre><code class="language-javascirpt"></code></pre>
</li>
</ul>
<p>console.log(true &amp;&amp; true &amp;&amp; &#39;도달ㅇ&#39;) // 도달 ㅇ
console.log(true &amp;&amp; false &amp;&amp; &#39;도달X&#39;) // false</p>
<p>console.log(false || false || 도달) // 도달 d
console.log(true || true || &#39;도달 X&#39;) // true</p>
<pre><code>
- 삼항연산자 대신 단축 평가 쓰기, 기본값이 있을 때 쓰면 좋다

``` javascript
function getUserName(user,isLoging){
    if(isLogin &amp;&amp; user){
        return user.name || &#39;이름없음&#39;
         // return user.name? user.name : &#39;이름 없음&#39;
    }
}
</code></pre><h3 id="else-if피하기">else if피하기</h3>
<ul>
<li>else if는 if와 else를 처리하고 if를 처리하는 것과 같다. 커버리지가 커지는것보다 조건을 분리하는게 낫다.</li>
<li>else if를 계속 쓰는 것보다는 switch문 사용</li>
</ul>
<p><strong>before😔</strong></p>
<pre><code class="language-javascript">      if (data.queue.status === QueueStatusEnum.Done) {
        refetchAllVerificationData();      
      } else if (data.queue.status === QueueStatusEnum.Error) { // 여기!!
        if (retryCount &gt;= 3) {
          setRetryCount(0);         
        } else {
          openUploadRetryNotification({ ...})
        }
}</code></pre>
<p><strong>after😌</strong></p>
<pre><code class="language-javascript">      if (data.queue.status === QueueStatusEnum.Done) {
        refetchAllVerificationData();       
        return;
      }

      if (data.queue.status === QueueStatusEnum.Error) {
        if (retryCount &gt;= 3) {         
          setRetryCount(0);
          completeUploadMutation();
          useNotification(
            &#39;error&#39;,
            t(&#39;notification.settlement.failUploadFile&#39;),
            t(&#39;common.notification.contactManager&#39;)
          );
          return; // early return으로 처리
        }

        openUploadRetryNotification({...});
      }</code></pre>
<h3 id="else피하기">else피하기</h3>
<ul>
<li>else는 단축 평가문으로 대체 가능하다. </li>
</ul>
<h3 id="early-return">early return</h3>
<p>함수를 미리 종료하는 것을 의미한다. if-else가 많거나 중첩되는 것보다 이해하기 편한 코드를 작성할 수 있다.</p>
<p><strong>before😔</strong></p>
<pre><code class="language-javascript">function loginService(isLogin,user){
    if(!isLogin){
        if(checkToken()){
            if(!user.nickName){
                return registerUser(user);
            }else{
                refreshToken();

              return &#39;로그인 성공&#39;;
            }
        }else{
            throw new Error(&#39;No Token&#39;)
        }
    }
}</code></pre>
<p><strong>after😌</strong></p>
<pre><code class="language-javascript">function loginServer(isLogin, user){
    if(isLogin) return;

      if(!checkToken()) throw new Error(&#39;no Token&#39;)

      if(!user.nickName) return registerUser(user);

      refreshToken();

      return &#39;로그인 성공&#39;

}</code></pre>
<h3 id="부정-조건문-지양하기">부정 조건문 지양하기</h3>
<ul>
<li><code>if(!isNaN)</code> 같은 표현은 헷갈린다. </li>
<li>early return이나 form validation을 사용할 때는 예외적으로 사용한다.</li>
</ul>
<h3 id="default-case-고려하기">Default Case 고려하기</h3>
<p>default case를 고려하면 안전하고 확정성 높은 코드를 작성할 수 있다. 엣지 케이스를 고려해 디폴트 값을 넣자.</p>
<p>case 1. 매개변수를 넣지 않았을 때</p>
<pre><code class="language-javascript">function sum(x,y){
    x = x||1;
      y = y||1;

  return x+y;
}

sum() // 매개변수를 넣지않았을 때</code></pre>
<p>case 2. switch-case 문에서</p>
<pre><code class="language-javascript">function registerDay(inputDay){
    switch(inputDay){
      case &#39;월요일&#39; : 
      case &#39;화요일&#39; : 
      case &#39;수요일&#39; : 
      case &#39;목요일&#39; : 
      case &#39;금요일&#39; : 
      case &#39;토요일&#39; : 
      case &#39;일요일&#39; : 
      default : 
        throw Error(&#39;입력값이 유효하지 않습니다&#39;)
    }
}</code></pre>
<h3 id="nullish-coalescing-operator-널-병합-연산자-">Nullish coalescing operator (널 병합 연산자 ??)</h3>
<ul>
<li><code>??</code> : 왼쪽 피연산자가 <code>null</code> 또는 <code>undefined</code>일 때 오른쪽 피연산자를 반환하고, 그렇지 않으면 왼쪽 피연산자를 반환하는 논리 연산자</li>
<li><code>||</code> : null, undefined뿐 아니라 falsy 값에 해당할 경우 오른쪽 피연산자를 반환한다.</li>
</ul>
<h3 id="드모르간-법칙">드모르간 법칙</h3>
<pre><code class="language-javascript">!(A || B) = !A &amp;&amp; B!</code></pre>
<p>조건이 추가될 때는 드모르간의 법칙을 쓰는 것이 직관적이다.</p>
<h2 id="배열">배열</h2>
<h3 id="javascript-배열은-객체다">javascript 배열은 객체다</h3>
<pre><code class="language-javascript">    const arr = [1, 2, 3];

    const obj = {
        0: 1,
          1: 2,
          2: 3
    }    
</code></pre>
<h3 id="arraylength">Array.length</h3>
<ul>
<li><p>length는 배열의 길이, 즉 마지막 인덱스다</p>
<pre><code class="language-javascript">  arr[10] = 4;

  console.log(arr.length) // 10
  console.log(arr) // [1,2,3,...,4]</code></pre>
</li>
</ul>
<h3 id="배열요소에-접근할-때는-구조분해-할당을-이용">배열요소에 접근할 때는 구조분해 할당을 이용</h3>
<pre><code class="language-javascript">const btob = [&#39;서은광, &#39;이민혁&#39;, &#39;이창섭&#39;, &#39;임현식&#39;, &#39;프니엘&#39;, &#39;육성재&#39;]

const [a,b,...rest] = btob;

console.log(a) // 서은광
console.log(b) // 이민혁</code></pre>
<h3 id="유사-배열-객체">유사 배열 객체</h3>
<ul>
<li><code>Array.from(object)</code>  : 유사 배열 객체나 반복 가능한 객체를 얕게 복사해 새로운 Array 객체를 만든다.</li>
</ul>
<pre><code class="language-javascript">const arrayLikeObject = {
    0:&#39;Hello&#39;,
      1 : &#39;World&#39;,
      length : 2
}

const arr = Array.from(arrayLikeObject);

console.log(Array.isArray(arrayLikeObject))) // false
console.log(Array.isArray(arr))) // true
</code></pre>
<ul>
<li><strong>argument</strong> : 함수의 매개변수를 따로 선언하지 않아도 arguments를 사용할 수 있다. 하지만 유사배열 객체이므로 배열 메서드를 사용할 수 없다.<pre><code class="language-javascript">function haha(){
  console.log(argument) // 1,2,3
}
</code></pre>
</li>
</ul>
<p>haha(1,2,3)</p>
<pre><code>
### 불변성(immutable)
- 배열은 참조형 자료형이므로 변수에 담았을 때 원본 배열의 영향을 받는다.
- 불변성을 지키는 법 : 배열복사, 새로운 배열을 반환하는 메서드를 사용한다
    - 새로운 배열을 반환하는 메서드 : filter, map, ...
    - push, unshift 등은 원본배열을 바꾼다

### for문을 배열 고차 함수로 리팩터링하기
불필요한 임시변수를 사용하지 않게 되고, 명확하고 선언적으로 코드를 작성할 수 있다.

### break와 continue
반복문을 제어한다. for문에서는 동작하지만 배열 메서드에서는 동작하지 않는다. 흐름을 제어하고 싶을 때는 for문을 사용하되 `for of`, `for in`을 사용하면 좀 더 편하게 사용할 수 있다.

## 객체
### 단축속성 shorthand property

``` javascript
const firstName = &#39;yoon&#39;
const lastName = &#39;jo&#39;

const person = {
    firstName : &#39;yoon&#39;,
    lastName : &#39;jo&#39;,
      getFullName : function(){
          return this.firstName + &#39; &#39; + this.lastName;
      }
}

const shortHandPerson = {
    firstName,
      lastName,
      getFullName(){ // 메서드도 축약이 가능하다
        return this.firstName + &#39; &#39; + this.lastName;
    }
}</code></pre><h3 id="conputed-property-name">conputed property name</h3>
<p>객체의 key로 <code>[]</code>를 사용해 계산된 값을 넣을 수 있다. </p>
<pre><code class="language-javascript">const key  = color;

const banana = {
    krName : &#39;바나나&#39;,
    [key] : &#39;yellow&#39;  
}

console.log(banana) //{krName:&#39;바나나&#39;, color:&#39;yellow&#39;}</code></pre>
<h3 id="lookup-table">lookup table</h3>
<p>key와 value를 이용해 switch-case문이나 조건문을 객체로 처리가능 하다.</p>
<p><strong>before😔</strong> </p>
<pre><code class="language-javascript">function getUserType(type){
    switch(key){
      case &#39;ADMIN&#39;: return &#39;관리자&#39;;
      case &#39;INSTRUCTOR&#39; : return &#39;강사&#39;;
      case &#39;STUDENT&#39; : return &#39;수강생&#39;;
      default: 
        return &#39;해당없음&#39;       
    }
}</code></pre>
<p><strong>after😌</strong></p>
<pre><code class="language-javascript">function getUserType(type){
    const USER_TYPE = {
        ADMIN:&#39;관리자,
          INSTRUCTOR:&#39;강사&#39;,
          STUDENT:&#39;수강생&#39;
    }

    return USER_TYPE[type] || &#39;해당없음&#39;;
}</code></pre>
<h3 id="objectfreeze">Object.freeze</h3>
<p>객체를 동결하는 함수다. 재할당해도 원본을 유지하고, 값을 추가해도 동결한다. 단, 객체 안의 객체까지는 동작을 안하므로 중첩된 freeze 함수를 사용해야 한다.</p>
<h3 id="객체에-직접-접근-지양하기">객체에 직접 접근 지양하기</h3>
<p>객체에 직접 접근하기 보다 대신 없근하는 함수를 만들어 레이어를 분리하는 것이 좋다.</p>
<p><strong>before😔</strong> </p>
<pre><code class="language-javascript">const model = {
    isLogin : false,
      isValidToken : false
}

function login(){
    model.isLogin = true;
      model.isValidToken = true;
}

function logout(){
    model.isLogin = false;
      model.isValidToken = false;
}</code></pre>
<p><strong>after😌</strong></p>
<pre><code class="language-javascript">const model = {
    isLogin : false,
      isValidToken : false
}

function setLogin(value){
    model.isLogin = value
}

function setIsValidToken(value){
    model.isValidToken = value;
}

function login(){
    setLogin(true)
      setIsValidToken(true)
}

function logout(){
    setLogin(false)
      setIsValidToken(false)
}
</code></pre>
<h2 id="함수">함수</h2>
<p>자바스크립트의 함수는 일급객체다. </p>
<ol>
<li>변수나 데이터에 담길 수 있다.</li>
<li>매개 변수로 전달가능하다.</li>
<li>함수가 함수를 반환할 수 있다.</li>
</ol>
<ul>
<li><code>메서드</code>란 객체에 의존성이 있는 함수다. OOP 행동을 의미한다.</li>
</ul>
<h3 id="복잡한-인자-관리하기">복잡한 인자 관리하기</h3>
<p>매개변수를 보고 맥락을 유추할 수 있도록 작성해야 한다. 맥락유추가 어렵고 매개변수가 많은 경우 객체로 매개변수를 전달하는 것이 좋다.</p>
<h3 id="void와-return">void와 return</h3>
<p>javascript 함수는 기본적으로 <code>undefined</code>를 리턴한다. 반환값이 없을 때 습관적으로 return 뒤에 값을 넣지 말것. early return 할 때도 반환값이 없으면 return;만해도 된다.</p>
<p><strong>before😔</strong> </p>
<pre><code class="language-javascript">function handleClick(){
    return setState(false)
}</code></pre>
<p><strong>after😌</strong></p>
<pre><code class="language-javascript">function handleClick(){
    setState(false)
}</code></pre>
<h3 id="콜백함수">콜백함수</h3>
<p>콜백함수는 다른 함수로 넘겨서 제어권을 위임할 수 있다.</p>
<h1 id="회고">회고</h1>
<p>통근 시간 지하철에서 듣기에 여러모로 적합한 강의였고, 당장 코드에 적용할 수 있는 실용적인 내용이 많았으며, javascript의 개념들을 한번 더 정리하면서 갈 수 있었다. </p>
<p>늘 고민이었던 if-else문 사용에 힌트를 얻을 수 있었고, 이번 스프린트를 진행하면서 for문으로 좀 더 편하게 작성할 수 있는 코드를 forEach를 굳이 사용하려고 해 오래 헤맸는데, 둘 중 무조건 써야한다는 것 없이 상황에 적합한 것을 쓰는 것이 좋다는 당연한 사실을 배웠다. 그리고 switch-case문 대신 lookup table을 사용할 때 input value와 key가 없을 때 처리하는 방법을 잘 몰랐는데, undefined default 값을 줄 수 있다는 것을 배웠다. 또 default 값을 위해 삼항연산자로 처리하던 코드를 단축평가로 수정했다. </p>
<hr>
<ul>
<li><a href="https://www.udemy.com/course/clean-code-js/">udemy - 클린코드 자바스크립트</a></li>
<li><a href="https://developer.mozilla.org/ko/docs/Glossary/Hoisting">MDN 호이스팅</a></li>
<li><a href="https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=z1004man&amp;logNo=221565620666">isNaN과 Number.isNaN()</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ESLint와 Prettier]]></title>
            <link>https://velog.io/@ouo_yoonk/ESLint%EC%99%80-Prettier</link>
            <guid>https://velog.io/@ouo_yoonk/ESLint%EC%99%80-Prettier</guid>
            <pubDate>Sat, 23 Apr 2022 09:06:14 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ouo_yoonk/post/2396e79a-be83-4fde-b8a2-78915d6458cf/image.png" alt=""></p>
<p>최근 토이 프로젝트 리팩토링을 시작했는데 회사에서 쓰던 개발환경과 달라 불편함을 느꼈다.
그래서 최대한 비슷하고 편한 환경을 셋팅하면서 정리하는 글. </p>
<h1 id="eslint">ESLint</h1>
<p><strong>ESLint는 Javascript 코드에서 발견된 문제 패턴을 식별하기 위한 정적 코드 분석 도구이다. 코드 퀄리티를 보장해준다.</strong> 대부분의 프로그래밍 언어에는 컴파일하는 과정에서 수행되는 Linter가 기본적으로 내장되어 있다. 그러나 역동적이고 느슨한 언어인 JavaScript에는 Linter가 존재하지 않는다. 왜냐하면 JavaScript는 별로의 컴파일 과정이 없고, Node나 Browser에서 바로 실행되기 때문이다. </p>
<h2 id="eslint-setting">ESLint Setting</h2>
<h3 id="eslint-설치">ESLint 설치</h3>
<pre><code>$ yarn add -D eslint</code></pre><h3 id="eslint-extension-설치">ESLint extension 설치</h3>
<p><img src="https://velog.velcdn.com/images/ouo_yoonk/post/2cf7daa9-b12e-4e2e-a337-8c97a0f2e10e/image.png" alt=""></p>
<h3 id="eslintrc-파일-생성---eslint-설정">.eslintrc 파일 생성 - eslint 설정</h3>
<ul>
<li><strong>parser</strong> : 각 코드 파일을 검사할 파서를 설정하는 부분</li>
<li><strong>extends</strong> :eslint rule 설정이 저장되어 있는 외부 file을 extends하는 부분. 해당 플러그인에서 제공하는 rule set이 적용된다. 변경하고 싶은 부분이 있다면 rules쪽에서 커스터마이징 하면 된다.</li>
<li><strong>rule</strong> : 직접 lint rule을 적용하는 부분. extends로 자동으로 설정된 rules 중에 특정 rule을 끄거나, error를 warning으로 나오도록 변경하는 등 설정을 바꿀 수 있다.</li>
</ul>
<p>아래는 현재 토이프로젝트에서 사용하고 있는 설정이다.</p>
<pre><code class="language-javascript">module.exports = {
  // parser : 각 코드 파일을 검사할 파서
  parser: &#39;@typescript-eslint/parser&#39;, // typescript
  plugins: [&#39;@typescript-eslint&#39;, // typescript
            &#39;prettier&#39;],
  extends: [ // 해당 플러그인에서 사용하는 rule set이 적용된다.
    &#39;airbnb&#39;,
    &#39;plugin:import/errors&#39;,
    &#39;plugin:import/warnings&#39;,
    &#39;plugin:prettier/recommended&#39;,
    &#39;plugin:@typescript-eslint/recommended&#39;,
    &#39;prettier&#39;,
  ],
  rules: {
    &#39;linebreak-style&#39;: 0,
    &#39;import/prefer-default-export&#39;: 0,
    &#39;prettier/prettier&#39;: 0,
    &#39;import/extensions&#39;: 0,
    &#39;no-use-before-define&#39;: 0,
    &#39;import/no-unresolved&#39;: 0,
    &#39;import/no-extraneous-dependencies&#39;: 0,
    &#39;no-shadow&#39;: 0,
    &#39;react/prop-types&#39;: 0,
    &#39;react/react-injsx-scope&#39;: &#39;off&#39;,
    &#39;react/jsx-filename-extension&#39;: [2, { extensions: [&#39;.js&#39;, &#39;.jsx&#39;, &#39;.ts&#39;, &#39;.tsx&#39;] }],
    &#39;prefer-destructuring&#39;: [&#39;error&#39;, { object: true, array: false }],
    &#39;jsx-a11y/no-noninteractive-element-interactions&#39;: 0,
    &#39;react/jsx-props-no-spreading&#39;: &#39;off&#39;,
    &#39;react/no-array-index-key&#39;: &#39;off&#39;,
    &#39;no-underscore-dangle&#39;: &#39;off&#39;,
    &#39;no-param-reassign&#39;: &#39;off&#39;,
    &#39;@typescript-eslint/no-empty-function&#39;: &#39;off&#39;,
  },
};
</code></pre>
<h1 id="prettier">Prettier</h1>
<p><strong>Prettier는 코드를 정렬해 주는 Code Formatter 중 하나이다. 코드 스타일을 깔끔하게 혹은 통일되도록 도와준다.</strong> 설정한 ESLint 룰에 따라 Prettier가 자동으로 format(줄 바꿈, 공백, 들여 쓰기 등)을 잡아준다.</p>
<h2 id="prettier-셋팅">Prettier 셋팅</h2>
<h3 id="1-vscode-extension-이용하기">1. VSCode extension 이용하기</h3>
<h3 id="2-prettier-플러그인-직접-설치-후-eslintrc에-셋팅하기">2. Prettier 플러그인 직접 설치 후 eslintrc에 셋팅하기</h3>
<hr>
<p><strong>reference</strong></p>
<ul>
<li><a href="https://helloinyong.tistory.com/325">https://helloinyong.tistory.com/325</a></li>
<li><a href="https://velog.io/@bab_bury/React-ESLint-Prettier-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95">https://velog.io/@bab_bury/React-ESLint-Prettier-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022년 2,3월 회고]]></title>
            <link>https://velog.io/@ouo_yoonk/2022%EB%85%84-23%EC%9B%94-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@ouo_yoonk/2022%EB%85%84-23%EC%9B%94-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Fri, 01 Apr 2022 06:17:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://media.vlpt.us/images/ouo_yoonk/post/be3ad1dd-67ea-433d-99b9-9333eb6c2143/%E1%84%83%E1%85%A1%E1%84%8B%E1%85%AE%E1%86%AB%E1%84%85%E1%85%A9%E1%84%83%E1%85%B3%20(3).png" alt=""></p>
<p>2월엔 별로 한게 없는 것 같아서 그 사실이 부끄러워 괜히 미루다 회고 쓸 타이밍을 놓쳤는데, 3월도 끝나버렸다. 한 달이 얼마나 빠른지, 한게 없는 와중에 한 것은 정리를 하고 싶어서, 한게 없다면 왜 없는지에 대해 생각하는게 회고이므로 늦은 2월 회고와 3월 회고를 쓴다.</p>
<h1 id="graphql-전환-작업2월">GraphQL 전환 작업(2월)</h1>
<p>2월의 가장 큰 일은 graphQL 전환 작업이었다. graphQL을 쓰면서 좋았던 것은 일일이 손으로 했던 타입 정의를 type gen을 이용해서 하기 때문에 작업 시간이 줄었고, 중복 타입을 선언하는 일이 없어졌다. 불러오는 데이터에 따라 API 생성 요청을 백엔드에 따로 하지 않아도 되고, apollo client가 제공하는 hook이 사용하기 편리했다. 그럼에도 불편했던 점은 초반에 타입이 계속 변경되는 일이 있었는데, 프로젝트를 시작하거나 커밋할 때 자동으로 gen을 하는 코드때문에 에러가 나서 에러가 고치기 전까지는 앱 실행이 안되는 경우가 있었다. 이건 graphQl의 문제라기 보다 우리의 스크립트와 소통의 문제라고 생각했다. </p>
<p>graphQl을 사용하기 전에 server쪽과 client쪽 모두 공부를 했다. schema와 resolver의 개념을 공부하고, REST API와의 차이점과 더 좋은 점을 알고, 사용법을 익혔다. </p>
<h1 id="nomad-coder-reactjs-마스터-코스-feat-react-router-dom-v6">Nomad coder ReactJS 마스터 코스 feat. react-router-dom v6</h1>
<p>회사 복지를 이용해 nomad coder에서 강의를 왕창 샀고, ReactJS 강의를 첫번째로 듣고있다. (30프로정도 들었다...) 클론코딩에 회의적인 사람도 있지만, 내가 몰랐던 걸 코드 스타일이든, 개념이든, 라이브러리든 하나라도 더 배우는 게 있다면 그건 가치있는 일이라고 생각한다. 그리고 나는 아직 모르는게 너무너무 많기 때문에 무조건적인 input이 필요하다고도 생각한다. 
styled-component의 다양한 사용법과 recoil, chartjs를 새로 알게 되었고, react-router-dom v6를 이용해 보았다.! 이건 나중에 따로 정리할 예정.
v6를 적용해볼 생각을 한 것과 직접 적용해본 것에 칭찬을 👏</p>
<h1 id="운영체제-강의-완강---이화여대-반효경-교수님">운영체제 강의 완강 - 이화여대 반효경 교수님</h1>
<p>&#39;한 권으로 읽는 컴퓨터 구조와 프로그래밍&#39;을 읽다가 막히는 부분에서 벗어나질 못해 이번 달은 운영체제 강의를 들었다. 뭔지는 안다, 들어는 봤다는 것들의 개념과 맥락을 정리할 수 있었다. 다 이해하고 흡수하는건 또 다른 얘기겠지만 우선은 이렇게 또 점을 찍은 것에 만족! </p>
<h1 id="외출난이도-리팩토링-논의">외출난이도 리팩토링 논의</h1>
<p>사랑스러운 토이프로젝트인 외출난이도를 드디어 리팩토링 하기로 했다. 절반정도는 지금 회사에서 해본 것들을 도입해보기로 했다. react-query나 presentational-container pattern, amplify. 해본 것을 왜하냐하면 같이하는 파트너는 안 써본 기술이기도 했고, 나도 처음부터 다시 셋팅을 하면서 사용을 해보고 싶기 때문이다. 그리고 test code를 작성하고, 리덕스가 아닌 다른 상태관리 라이브러리를 사용해 볼 예정이다. 매주 토요일 11시에 회의하고, 틈틈이 작업하기로 얘기를 마치고, 새로운 브랜치도 따놨다. </p>
<h1 id="약재-계산-앱-brave_hc">약재 계산 앱 Brave_HC</h1>
<p><img src="https://media.vlpt.us/images/ouo_yoonk/post/98ccf3a9-a4eb-470b-94e7-73555de8feb4/%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%202022-04-01%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.33.32.png" alt=""></p>
<p>가까운 지인의 요청으로 저렇게 약재 용량을 입력하면 총 금액을 계산하는 앱을 만들었다. 백엔드 서버를 굳이 써야할까 싶어서 엑셀 파일을 읽어와 state에 저장해 사용했고, electron으로 빌드해 데스크탑 앱으로 만들어서 친구한테 전달했다.!
결과는!! 컴퓨터가 너무 느려진다.....였다. 내가 인지하고 있던 문제는 키보드 onChange 이벤트에 따로 lodash 처리를 안해서 모든 입력마다 include 연산이 돌면서 렌더링이 된다는 것과 약재입력 -&gt; 탭 -&gt; 수량입력 -&gt; 엔터의 딱 한가지 액션만 가능하다는 것이었다. 금방 해결할 수 있는 문제라 우선 써보라고 하고 업데이트를 할 예정이었는데 컴퓨터가 너무 느려진다니. 모든 데이터를 state에 올리고, 키보드 액션 한번마다 length 1000정도의 array를 매번 연산하는 점에서 성능이 괜찮을까 싶긴했는데 안 괜찮았나보다... 앱을 켜자마자 메모리 사용량이 1gb가 확 늘어버리는데 기능이 단순하니까 성능도 가벼웠으면 했는데 어떻게 개선을 할 수 있을지 모르겠다. 엑셀을 db처럼 쓰고 싶어서, 프로그램을 켜면 엑셀을 읽어 json으로 만들고 그걸 바로 데이터로 이용할 수 있으면 좋겠다고 생각은 하고 잇는데 아직 방법을 못찾았다. 그래서 개선도 못해주고있다 ㅋㅋㅋㅋ.. 4월엔 꼭!</p>
<h1 id="프로덕트를-대하는-태도에-대해">프로덕트를 대하는 태도에 대해</h1>
<p>우리 파트너센터는 현재 antd를 사용하고 있다. 팀장님이 ag grid 도입에 대해 poc를 해보라고 하셔서 개발된 페이지에 똑같이 만들어봤고 낸 내가 결론은 &#39;지금 쓰는 것 보다 불편하다, 굳이 이걸 써야하나? 아직 사용자측에서 이런 기능을 요구한게 아닌데 사용할지 안할지도 모르는 기능 때문에 이걸 써야하나?&#39;였다. 그리고 회의를 하면서 엄청 부끄러웠다. sean이 poc를 요청한 이유는 다른 서비스의 파트너센터들에서는 기본적으로 제공되는 기능들이 우리 테이블에는 부족하고, 어떤 기능들이 있는지 보면서 우리가 어떻게 이걸 활용할 수 있을지, 이걸 이용해 어떻 편의를 먼저 제공해줄 수 있을지 등을 탐색해봤으면 해서였다. 띠용... 부끄러웠지만 프로덕트를 대하는 시야가 또 조금 더 넓어질 수 있었다. 기술은 결국 사용자에게 더 좋은 경험을 주기 위한 것이고, 요청이 있을때마다 개발을 해도되지만 더 나은 사용성을 어떻게 제공할 수 있을지를 고민해야 했다. 기술적 지식도 부족하지만 일을 대하는 태도도 아직 이렇게 부족하다 ㅠㅠ. 그래도 파트너센터를 좋은 프로덕트로 만들고싶은 욕심, 다른 서비스에서는 본적 없는 기능을 제공하고 싶다는 욕심이 생겼다. </p>
<h1 id="4월에-할-일">4월에 할 일</h1>
<ul>
<li>정산 검수 개선</li>
<li>노마드코더 리액트 강의 다 듣기</li>
<li>brave hc 개선해서 전달하기</li>
<li>외출난이도 리팩토링</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[나만의 리액트 라이브러리를 만들어보자]]></title>
            <link>https://velog.io/@ouo_yoonk/%EB%82%98%EB%A7%8C%EC%9D%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EB%A5%BC-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90-gjmokobr</link>
            <guid>https://velog.io/@ouo_yoonk/%EB%82%98%EB%A7%8C%EC%9D%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EB%A5%BC-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EC%9E%90-gjmokobr</guid>
            <pubDate>Tue, 22 Mar 2022 00:23:34 GMT</pubDate>
            <description><![CDATA[<p>출처 : <a href="https://pomb.us/build-your-own-react/">https://pomb.us/build-your-own-react/</a></p>
<ul>
<li>들어가기 전에<ul>
<li>ㅁ</li>
<li>결과물? 자바스크립트로 작성된 리액트 코드</li>
<li>어떻게 ? ‘Build your own React’를 보고 따라한다, 더 알고싶은 부분은 공부해서 정리한다</li>
</ul>
</li>
</ul>
<aside>
💡 목차
**Step I**: The `createElement` Function
**Step II**: The `render` Function
**Step III**: Concurrent Mode
**Step IV**: Fibers
**Step V**: Render and Commit Phases
**Step VI**: Reconciliation
**Step VII**: Function Components
**Step VIII**: Hooks

</aside>

<h1 id="1-createelement-function">1. createElement Function</h1>
<p>JSX —babel—&gt; React.createElement</p>
<p>React의 element는 React.createElement 함수를 이용해 만들어지지만 가독성이 떨어지기 때문에 JSX 문법을 사용한다. JSX 문법은 babel이 JS로 변환한다. </p>
<p><code>React.createElement</code> </p>
<pre><code class="language-jsx">function createElement(type,props,...children){ // (type,props,[children])
    return {
        type,// tag name
        props:{
            ...props,
            children
        }
    }
}</code></pre>
<pre><code class="language-jsx">// text를 처리하기 위한 함수
function createTextElement(text) {
  return {
    type: &#39;TEXT_ELEMENT&#39;,
    props: {
      nodeValue: text,
      children: []
    }
  };
}</code></pre>
<p>💡 뜬금없이 스프레드 연산자!</p>
<p>...children은 children이라는 배열의 요소를 스프레드 한 것. 따라서 type,props,[children]이 됨</p>
<h1 id="2-render-function">2. render Function</h1>
<p><code>ReactDOM.render</code> </p>
<ol>
<li>DOM node 생성</li>
<li>props 추가</li>
<li>생성한 node를 container에 추가</li>
</ol>
<pre><code class="language-jsx">function render(element,container){
    const dom = 
                element.type == &quot;TEXT_ELEMENT&quot;
                ? document.createTextNode(&quot;&quot;) // text node
                : document.createElement(element.type) // element type으로 DOM노드 생성하기

  const isProperty = key =&gt; key !== &#39;children&#39;
  // element에 props 추가
    Object.keys(element.props)
            .filter(isProperty)
            .forEach(name =&gt; {
                dom[name] = element.props[name]
            })                
    // children을 재귀적으로 
    element.props.children.forEach(child=&gt;
        render(child,dom)    
    )
    container.appendChild(dom) // 생성한 node container에 추가
}</code></pre>
<h1 id="3-concurrent-mode동시성-모드">3. Concurrent Mode(동시성 모드)</h1>
<p>render 함수에서 재귀호출부분의 경우 트리 끝까지 렌더가 될 때까지 동작을 멈출 수가 없다. 트리가 커지는 경우 긴 시간 동안 메인 스레드가 멈추게 된다. 브라우저가 높은 우선 순위의 작업을 처리하기 위해서 렌더를 멈출 필요가 있다.</p>
<pre><code class="language-jsx">function render(element, container) {
... 
 element.props.children.forEach((child) =&gt; render(child, dom)); 

  container.appendChild(dom);
}</code></pre>
<p>그래서 작업을 작은 단위로 나누고, 각 단위가 끝나면 브라우저가 렌더링을 중단하도록 한다.</p>
<pre><code class="language-jsx">let nextUnitOfWork = null

function workLoop(deadline) {
  let shouldYield = false
  while (nextUnitOfWork &amp;&amp; !shouldYield) {
    nextUnitOfWork = performUnitOfWork(
      nextUnitOfWork
    )
    shouldYield = deadline.timeRemaining() &lt; 1
  }
  requestIdleCallback(workLoop)
}

requestIdleCallback(workLoop)

function performUnitOfWork(nextUnitOfWork) {
  // TODO
}</code></pre>
<p>requestIdleCallback : loop를 만들기 위해 requestIdleCallback를 사용한다. 브라우저의 메인 스레드가 idle 상태가 되면 requestIdleCallback을 호출한다. </p>
<h1 id="4-fibers">4. Fibers</h1>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/36bd4f2d-98eb-49fa-b9b0-d059f6fe6446/image.png" alt=""></p>
<p>각 작업 단위를 구조화 하기 위해 <code>fiber tree</code> 라는 자료 구조가 필요하다.</p>
<p>위와 같은 작업을 수행한다고 할 때 render 함수 내부에서 root fiber를 만들고, 이것을 nextUnitOfWork로 설정한다. 나머지 작업들은 <code>performUnitOfWork</code> function에서 일어난다. </p>
<p>각 fiber는 세가지 일을 한다.</p>
<ol>
<li>element를 DOM에 추가한다</li>
<li>element의 children에 추가할 fiber를 만든다</li>
<li>다음 작업 단위를 선택한다.</li>
</ol>
<p>fiber - children - ling - parent </p>
<p>fiber tree의 목적은 다음 작업단위를 쉽게 찾는데에 있다.</p>
<p>한 fiber의 작업이 끝났을 때, children이 있을 경우에는 children이 없을 때는 sibling이 다음 작업 단위가 된다. </p>
<p>sibling도 없을 경우 parent로 올라가고, root로 갈때까지 render 된다.</p>
<h1 id="5-render-and-commit">5. Render and Commit</h1>
<p>지금의 코드에서 새로운 node가 DOM에 추가될 때 브라우저는 렌더 트리가 완성되기 전에 작업을 중단할 수 있다. 이 경우 우리는 미완성된 UI를 보게된다.</p>
<p>이를 위해 DOM을 제거하는 로직 대신, fiber 루트를 추적할 것이다. 이를 wipRoot(work in progress rot)라고 한다.</p>
<p> 일단 모든 작업이 끝나고 나면 (더 이상 다음 작업이 없는 경우), 전체 fiber 트리를 돔에 커밋한다.</p>
<p>이 과정은 commitRoot 함수에서 이루어집니다. 여기서 모든 노드를 재귀적으로 dom에 추가한다.</p>
<h1 id="6-reconciliation재조정">6. <strong>Reconciliation(재조정)</strong></h1>
<p>node의 update와 delete는 render 함수로 얻은 element를 마지막으로 커밋한 fiber 트리와 비교한다.</p>
<p>이를 위해 커밋을 한 후 마지막 fiber tree를 저장한다. 이를 current root라고 부른다.</p>
<p>그리고 모든 fiber 속성에 alternate를 추가한다. 이것은 이전의 단계에서 커밋한 fiber다. </p>
<p>그런 다음 performUnitOfWork에서 새로운 fiber를 생성하는 로직을 추출해 <code>reconcileChildren</code> 함수를 만든다. 예전 fiber의 자식들과 재조정해야하는 element를 비교하기 위에 동시에 순회한다.</p>
<p>element와 oldFiber의 <code>type</code> 비교</p>
<ul>
<li>타입이 같으면 props만 바꾼다 - update</li>
<li>타입이 다르면 새로운 element이므로 새로운 DOM node를 생성한다 - placement</li>
<li>타입이 다르고 예전 fiber가 있으면 예전 node를 지운다 - deletion</li>
</ul>
<p><code>effectTag</code> 속성을 추가한다.</p>
<p>제거하고 싶은 노드를 추적하기 위해 delection 배열을 만든다.</p>
<p>commitRoot에서 delection 배열을 순회해 commitWork를 실행한다. commitWork 함수에서 effectTag를 이용해 append하거나 removeChild하거나 update한다.</p>
<p>갱신을 위해 <code>updateDom</code> 함수를 만든다.</p>
<p>예전 fiber의 props를 새로운 fiber의 props와 비교해 사라진 props는 제거하고, 달라진 props를 설정한다. 이 때 on으로 시작하는 props(event)가 있다면 eventListner를 추가한다.</p>
<h1 id="7-함수형-컴포넌트">7. 함수형 컴포넌트</h1>
<p>함수형 컴포넌트로 만들어진 fiber에는 DOM node가 없다. 그래서 children을 props로 가져오는 대신 함수를 호출한다.</p>
<p>fiber 타입이 함수인지 체크한다음 <code>updateFunctionComponent</code> 에서 지금까지와 같은 역할을 하게끔 수정한다.</p>
<h1 id="8-hook">8. Hook</h1>
<p>함수형 컴포넌트에 상태를 추가하기.</p>
<pre><code class="language-jsx">const Myact = {
  createElement,
  render,
  useState,
}

/** @jsx Didact.createElement */
function Counter() {
  const [state, setState] = Didact.useState(1)
  return (
    &lt;h1 onClick={() =&gt; setState(c =&gt; c + 1)}&gt;
    Count: {state}
    &lt;/h1&gt;
    )
}
const element = &lt;Counter /&gt;
const container = document.getElementById(&quot;root&quot;)
Didact.render(element, container)</code></pre>
<pre><code class="language-jsx">let wipFiber = null
let hookIndex = null

function updateFunctionComponent(fiber) {
  wipFiber = fiber
  hookIndex = 0
  wipFiber.hooks = []
  const children = [fiber.type(fiber.props)]
  reconcileChildren(fiber, children)
}</code></pre>
<p>함수형 컴포넌트를 호출하기 전에 <code>useState</code> 함수의 내부에서 사용하기 위한 몇몇 전역 변수들을 초기화해야 한다.</p>
<p>먼저 작업중인 fiber를 설정한다. 또한 그 fiber에 <code>hook 배열</code>을 추가함으로서 동일한 컴포넌트에서 여러 번 useState 함수를 호출 할 수 있도록 한다.</p>
<pre><code class="language-jsx">function useState(initial) {
  const oldHook =
        wipFiber.alternate &amp;&amp;
        wipFiber.alternate.hooks &amp;&amp;
        wipFiber.alternate.hooks[hookIndex]
  const hook = {
    state: oldHook ? oldHook.state : initial,
  }

  wipFiber.hooks.push(hook)
  hookIndex++
  return [hook.state]
}</code></pre>
<p>함수형 컴포넌트가 useState를 호출할 때 이것이 오래된 hook인지를 체크하는데, 이때 훅 인덱스를 사용해 fiber의 alternate를 체크한다.</p>
<p>만약 우리가 가지고 있는 것이 오래된 hook이라면 상태를 초기화하지 않았을 경우 이 훅의 상태를 새로운 훅으로 복사한다.</p>
<p>그리고 새로운 훅을 fiber에 추가한 뒤 훅 인덱스 값을 증가시킨 다음 state를 반환한다.</p>
<pre><code class="language-jsx">const hook = {
  state: oldHook ? oldHook.state : initial,
  queue: [],
}

    const setState = action =&gt; {
      hook.queue.push(action)
      wipRoot = {
        dom: currentRoot.dom,
        props: currentRoot.props,
        alternate: currentRoot,
      }
      nextUnitOfWork = wipRoot
      deletions = []
    }

wipFiber.hooks.push(hook)
hookIndex++
return [hook.state, setState]
}</code></pre>
<p>또한 useState는 상태를 갱신하는 함수 역시 리턴해야 하므로, 액션을 받는 setState 함수를 정의한다. 이 액션을 우리가 훅에 추가한 큐에 넣는다.</p>
<p>그리고 렌더 함수에서 했던 것과 비슷한 작업을 하는데, 새로운 작업중(wip)인 루트를 다음 작업할 단위로 설정하여 반복문에서 새로운 렌더 단계를 시작할 수 있도록 한다.</p>
<pre><code class="language-jsx">const actions = oldHook ? oldHook.queue : []
actions.forEach(action =&gt; {
  hook.state = action(hook.state)
})</code></pre>
<p>아직 액션을 실행하지는 않았다. 이는 컴포넌트 렌더링 다음에 수행하는데, 오래된 훅의 큐에서 모든 액션을 가져온 다음 이를 새로운 훅 state에 하나씩 적용하면 갱신된 state를 얻을 수 있게 된다.</p>
<h1 id="새로-알게된-것">새로 알게된 것</h1>
<ul>
<li>React의 element는 React.createElement 함수를 이용해 만들어지지만 가독성이 떨어지기 때문에 JSX 문법을 사용한다. JSX 문법은 babel이 JS로 변환한다.</li>
<li>react는 작업단위를 구조화하기 위해 fiber tree 구조를 사용한다.</li>
<li>virture DOM은 render 함수로 얻은 element 즉, javascript 객체다. 리액트는 이전의 tree와 새로운 tree를 동시에 순회해 비교한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022년 1월]]></title>
            <link>https://velog.io/@ouo_yoonk/2022%EB%85%84-1%EC%9B%94</link>
            <guid>https://velog.io/@ouo_yoonk/2022%EB%85%84-1%EC%9B%94</guid>
            <pubDate>Tue, 22 Mar 2022 00:14:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>목표</p>
</blockquote>
<ul>
<li>&lt;한 권으로 읽는 컴퓨터 구조와 프로그래밍&gt;으로 공부</li>
<li>정산 검수 / 수수료입력 화면 개발</li>
</ul>
<h1 id="정산봇">정산봇</h1>
<p>정산은 우리팀의 큰 아젠다다. 정산 자동화를 위해 4달째 개발을 하고 있고, 개선을 하고 있다. 전체 목표에서 80프로 정도 진행된 상태다. 지금까지는 스프린트를 다 같이 진행하면서 일감을 나누어 개발했는데, 어쩌다보니 내가 정산 담당자가 되었다. 🙄  공식적?으로 담당자가 되니까 프로덕트에 대한 오너쉽이 생기고, 더 애착이 생긴다. 그래서 정산 플로우나 백엔드 로직에 대해 더 이해하려고 하고, 스스로 기획, 개발, UX에 대해 더 적극적으로 의견을 내게 되었다. 코드나 UI/UX에 미흡한 부분도 더 잘 보이고, 일도 더 밀도있게 하고 있다. 쉬지않고 계속 개발을 하는 느낌이라 지치기도 하지만 또 성장할 수 있는 기회라고 생각한다. 화이팅</p>
<h1 id="요즘-참-재밌네-출퇴근은-힘들지만">요즘 참 재밌네. 출퇴근은 힘들지만...</h1>
<p>출퇴근이 힘든 건 적응을 못하고있지만 그래도 요즘 공부가 순조롭고 재밌다. 회사 생활도 참 재밌다. 팀데이도 진행했고, 한달에 한 번 비타민카드 쓰기라는 팀 문화도 만들었다. 아래는 팀원들의 메시지. 요즘은 문서화에 신경을 많이 쓰고 있다. 프로젝트, QA, 배포 관련 문서화나 프로세스가 없는 상태여서 부족하지만 하나씩 만들어가는 중이다.</p>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/c7df6d7c-d9ce-4842-8975-e4b2709ec9bd/image.png" alt=""></p>
<h1 id="네-주니어같지-않다고요">네?! 주니어같지 않다고요!?</h1>
<p>이번달 면담 시간에 팀장님한테 주니어 같지 않다는 얘기를 들었다. 저번달 면담하면서 ‘문제 해결능력이 부족하다’라는 피드백을 들었다고 남겼는데 사실 그 뒤에 시간을 더 드릴걸그랬다는 말씀을 하셨다. 사수님의 의도는 뒷 부분이었기때문에 그런 말을 제가 했나요?!라고 놀라셨지만 나는 앞부분에 충격을 받았고, 그게 좋은 자극이 되었다고 말씀드렸다. 그랬더니 내 행동이 주니어 같지는 않아서 마냥 주니어 대하듯 대하지 않고 있나보다 라고 하셨다. <del>연륜때문인가</del> 사수님도 칭찬으로 말씀하셨고 나에게도 지금 시점에서 가장 의미있는 칭찬이라 기대치 팍팍 올려서 팍팍 키워달라고 했다. 팍팍크자 <del>연륜의</del> 주니어</p>
<h1 id="한-권으로-읽는-컴퓨터-구조와-프로그래밍">한 권으로 읽는 컴퓨터 구조와 프로그래밍</h1>
<p>을 읽고있다. 해야할 건 언제나 많지만 CS 지식을 쌓고싶다는 욕구와 동작원리를 알아가면 재밌으니까! 이번달에 다 읽고 정리하는게 목표였는데.... 몹시 더디다. 챕터당 블로그 글 한개씩 쓰는게 목표였는데 중간에 이해를 못하는 것도 있고, 글로 어떻게 정리를 할지, 어떤 내용을 엮어야할지 몰라 이것도 더디게 더디게 하고 있다. 그래도 조금 더 기계적인 관점으로 컴퓨터를 생각할 수 있게 되었다. 책만 쭉 읽고 정리하는건 능동적이지 못한 공부 방법이라고 해 우선 내용을 넣고, 필요한 부분은 더 찾아보고, 면접질문도 같이 보면서 공부를 하고 있다. </p>
<h1 id="그럼에도-공부를-많이-못해-아쉽다">그럼에도 공부를 많이 못해 아쉽다</h1>
<p>퇴근하면 체력이 없다는 핑계로 몰입해서 공부를 한지가 꽤 된 것 같다. 그래서 우선 가볍게, 재미있는것부터 하자는 생각으로 저 책을 읽고 있다. 그리고 정리하는 방법을 조금 바꿨는데, 노션을 적극적으로 활용하고 있다. 가볍게 읽은 블로그 글이나 몰라서 찾아본 것들을 노션에 우선 아무렇게나 적고, 미완이면 미완인 상태로 남겨둔다. 부족한 내용은 더 보충하고, 까먹으면 그냥 넘어가기도 하고, 정리를 다한 완성된 글은 블로그에 올리는 식으로 하고 있다. 메모처럼 쉽게 접근할 수 있어 오히려 더 많은 것을 남길 수 있게 되었다. 도구를 바꾸니 공부하는 방식이나 효율이 달라지는게 신기하다. </p>
<p>하지만 역시 몰입이나 밀도가 부족하니까 어딘가 허하고 부족한 기분이다. 이런 기분은 해서 채우는 수 밖에 없지 뭐...</p>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/68d2179f-c52e-4034-9b4b-4470dad4abff/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2021년 회고]]></title>
            <link>https://velog.io/@ouo_yoonk/2021%EB%85%84-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@ouo_yoonk/2021%EB%85%84-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sat, 01 Jan 2022 08:42:23 GMT</pubDate>
            <description><![CDATA[<h2 id="모든-것은-이직으로부터">모든 것은 이직으로부터</h2>
<p>올 2월 SI 프리랜서로 두 번째 프로젝트를 마치고 이직 준비를 시작했다. 이직을 결심한 이유는 역시 개발자로서의 성장이었다. 프로젝트를 진행하면서 내가 느낀 것은 ‘좋은 코드’를 짜기보다는 돌아가기만 하는 코드를 짜면 된다는 것이었다. 처음에는 내가 개발자라는 것 자체가 너무 좋았는데 점점 내가 그 정체성에 부끄러워지기 시작했다. 성장할 수 있는 많은 방법 중 내가 선택한 방법은 <code>환경을 바꾸는 것</code>이었고, 동료가 있고, 프로덕트의 지속적인 개선이 필요한 환경을 원했기 때문에 이직 준비를 시작했다.</p>
<p>이직을 위해 내가 필요한 것과 부족한 것, 채워야 할 것이 혼자서는 명확하지 않아 항해99 과정을 시작했고, 포트폴리오와 면접을 준비해 지금 우리 회사로 왔다. 지원한 회사중 가장 오고 싶었던 회사였기 때문에, 합격 연락이 오기 전에 다른 최종 합격한 다른 회사에 거절 의사를 전달도 했는데 다행히 곰이 그려진 명함을 받게 되었다.</p>
<p>우리팀은 올초부터 무에서 유를 창조하고 있는 중이었고, 프론트엔드 개발자로 그 과정을 함께하고 있다. GPM으로 시작해 파트너센터 스팩을 변경한 2.0을 오픈했고, 정산검수, 정산내역까지 쉬지않고 개발했다.</p>
<h2 id="회사에서-개발자로-일하기">회사에서 개발자로 일하기</h2>
<p>고미는 내 인생 첫 회사다. 그래서 개발자로서도 회사원으로서도 많은 것을 배우고 있다. 우선 개발자로서 회사의 도메인이나 프로덕트 구조나 구성에 대한 파악을 해야했고, 우리팀의 업무 흐름과 업무 방식(JIRA, 스프린트, slack,...)에 익숙해져야했다. 그리고 회사내의 개발팀으로서 우리가 해야할 일에 대한 고민을 함께 했고, 그 안에서 해야할 일을 찾고, 그것을 전달하고, 소통하는 방법도 배우고 있다. </p>
<p>지금까지는 막연하게 프로그래밍에 대해 많이 아는 사람이 실력 좋은 개발자라고 생각했는데, 지금은 회사에서 당면한 문제를 좋은 코드로 풀어 해결하는 사람이 좋은 개발자라고 생각한다. 일 잘하는 개발자에 대한 나름대로의 정의가 생겼고, lv1부터 한개씩 스테이지를 깨가고 있다고 생각한다. </p>
<h2 id="좋은-동료">좋은 동료</h2>
<p>그 과정에서 동료들의 도움을 정말정말 많이 받고 있다. 그리고 같이 일하는 게 즐겁다. 같이 프로덕트를 만들어 가는 것도, 문제를 해결하는 것도, 좋은 코드를 위해 의견을 나누는 것도 다 즐겁다!  물음표로 이어지는 대화를 하고 퇴근을 할 때의 들뜸이란. 좋아하는 그들에게 나 역시 신뢰할 수 있고, 즐거운 동료가 되고 싶다. 그러기 위해 지금 부족한건 역시 <del>실력</del>일까..</p>
<h2 id="뭘-공부해야하지">뭘 공부해야하지?</h2>
<p>항상 공부할게 명확했는데 올 4분기에는 길을 잃었다. 내가 뭘 알아야하는지, 어느 정도 알아야하는지를 몰라서 공부 방향을 잡기 위해 사수님에게도 물어보고, 유튜브도 보고, 블로그 글도 보고 했다. 해야할 게 많은데도 뭘 해야할지 모른다면 그건 모든 걸 한 번에 다 해야한다는 부담감때문인 것 같다. 나는 점을 찍는다는 말을 좋아한다. 한 번에 딥 다이브하는 것도 좋지만 그럼 맥락을 놓치기 쉽다. 간단히 훑고 지나가는 것만으로도 뇌에는 점이 찍히고, 그 점을 다른 상황에서 다시 만났을 때 다른 점과 엮이거나 더 잘 이해할 수 있다고 생각한다. 이론과 실전이 항상 상호작용해야한다는 것을 잊지말고, 당장 눈에 보이지 않는 지리멸렬한 시간을 인내심을 갖고 계속 쌓아가야하지 않을까. 우선 공부에 흥미를 붙이는게 우선인것 같아 내년에는 내가 제일 궁금한 걸 제일 보람있는 방식으로 시작할 예정이다. 1년치 목표를 월별, 분기별로 정해놓을까도 했는데, 언제든 바뀔 수 있기 때문에 처음 한 두달치만 정해서 시작!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2021년 12월 회고]]></title>
            <link>https://velog.io/@ouo_yoonk/2021%EB%85%84-12%EC%9B%94</link>
            <guid>https://velog.io/@ouo_yoonk/2021%EB%85%84-12%EC%9B%94</guid>
            <pubDate>Sat, 01 Jan 2022 07:08:54 GMT</pubDate>
            <description><![CDATA[<h2 id="정산-검수-개발-완료-및-정산-내역-개발">정산 검수 개발 완료 및 정산 내역 개발</h2>
<p>정산 알파는 감마와 델타를 지나 ‘정산 검수’로 기획이 바뀌었고, 드디어 개발을 완료하고 배포를 했다. 파일 업로드와 파일 다운로드 기능을 구현하면서 polling 요청과 blob, 파일 다운로드 로직에 대해 공부도 했다. 그리고 현업 사용자에게 이런 피드백을 받았다. 이후로 계속 개선중이다.</p>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2a9ae49f-1267-4043-adc5-37eeb5063373/%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-12-13_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_5.31.34.png" alt="스크린샷 2021-12-13 오후 5.31.34.png"></p>
<p>그리고 이어서 정산 내역을 개발했다. 내가 담당한 페이지는 정산 과정상 복잡한 과정이 없어서 비교적 빨리 스프린트를 마칠 수 있었다. 개발하는 속도가 빨라졌다는 걸 느낀다. 개발이 순조롭게 끝나서 배포를 마치고, 현업 피드백을 받아 반영하고 있다. </p>
<p>그 와중에 부끄러운 일이 있었는데 파일 업로드 상태를 받아오는 로직에서 polling을 처리해야 했는데 react-query에 기능을 제공하는데도 불구하고 useEffect를 이용해 처리를 했다. 문서를 보고했는데 폴링 요청이 제대로 안들어가 나름대로는 해결한거였는데 연차와 주말이 지나고 돌아오니 다른 분이 다시 손 본 다음 머지가 되어있었다. 일을 두 번하게 하는 팀원이라니 ㅠㅠ 시간에 맞추기 위해 우선 하고 넘기자는 생각이었는데, 시간이 조금 더 들더라도 제대로 처리해야겠다고 다짐했다. </p>
<p>그리고 QA 과정에서 꼼꼼하지 못했던 것도 반성... 꺼진 커밋도 다시보자.</p>
<h2 id="회사일을-잘하는-건-어떤건가요-지금-저에게-부족한건-어떤건가요">회사일을 잘하는 건 어떤건가요? 지금 저에게 부족한건 어떤건가요?</h2>
<p>라고 사수님께 여쭤봤다. 코딩 실력도 회사 일을 처리하는 능력도 늘지 않는 것 같아서 계속 고민을 했기 때문이다. 사수님의 답변은 회사 프로덕트에 관심을 갖고 끊임없이 개선을 해야한다. 개선점을 알아보려면 결국 input이 많아야한다. 자바스크립트 코어를 잡고 가려는 지금의 공부방향도 좋지만, 그것과는 별개로 변두리의 것들도 챙겼으면 좋겠다고 하셨다. 그리고 문제 해결 능력이 조금 아쉽다고 했다. 우선 돌아가는 것도 중요하지만 Best practice가 뭔지 많이 찾아보고 적용해봤으면 좋겠다고 하셨다. </p>
<p> 스스로를 돌아보니 나는 회사에서 일정이 널널할 때는 회사 프로젝트를 보기보다는 리액트나 CS의 이론들을 찾아본다.  코어도 중요하지만 당장 코드에 적용할 수 있는 무언가는 아닐 수도 있다. 진짜 문제는 회사 태도를 대하는 자세라고 생각했다. 그래서 하루에 한개씩 개발이외에 수정사항 찾기를 하기로했다.</p>
<h2 id="그래서-하루-한개-회사-코드-개선하기">그래서 하루 한개 회사 코드 개선하기</h2>
<p>면담 이후 하루 한개씩 회사 코드를 수정하기로 하고 실천했다. 변수 이름 변경, 예외 케이스 찾아서 처리하기, 코드에서 중복되는 부분을 없애기 위해 컴포넌트를 수정하거나, hook 만들어서 처리하기, strict mode에서 warning 나던 코드 개선하기, 스택 변경하고 나서 손길이 닿지 않은 부분 코드 수정하기 등등을 했다. </p>
<p>물론 할당량을 못 채운 날도 있었지만 회사 프로덕트를 대하는 자세가 달라졌다. 개발이 완료된 소스를 안중에 없었는데(<del>반성</del>) 프로젝트 전체를 과거가 아닌 현재로 인식을 하게 되었고, 이게 정말 최선일까? 더 좋은 방법이 없을까를 계속 고민하고 찾고 있다. 그리고 실질적으로 적용하고 개선할 수 있는 것들을 공부하니 공부에 좀 더 재미가 붙었다. 지금까지 했던 공부들은 물론 다 필요한 것이지만 눈에 보이는 결과물이 없어 재미도 없고, 보람도 없었기 때문이다. 눈을 크게 뜨고 프로젝트를 대하니 전보다 더 애정도 생긴다. 이 태도가 사실은 당연한 건데 이제라도 배운 것을 다행으로 생각하고 앞으로도 계속 유지할 것이다.</p>
<ul>
<li>하루 한개 회사 코드 개선하기<ul>
<li>settlement_index 없을 때 검증관련 버튼들 비활성화</li>
<li>컴포넌트 내부에 있던 object 컴포넌트 밖으로 뺌 - 컴포넌트가 렌더링 될때마다 불필요한 객체 생성을 피하기위해</li>
<li>업로드 취소 기능이 추가됐을 때, api에 반영되지 않은 예외케이스를 찾아서 추가했다</li>
<li>Layout의 Header 컴포넌트 extra 부분에 여러 컴포넌트가 들어갈 경우 간격 유지를 Row, Col 컴포넌트로 일일이 해줘야하는 것을 Header 내부에 Space 컴포넌트와 간격을 추가했다. → 모든 컴포넌트에서 헤더 내부 버튼 간격이 동일해지고, 레이아웃을 사용하는 곳에서 Row,Col을 줄필요가 없음</li>
<li>useTableData → useFetchPayload로 변경, keyword포함</li>
<li>React has detected a change in the order of Hooks called by VerificationTable. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks:  에러 해결<ul>
<li>원인 : 조건문 안에서 hook을 쓰고 있었음. isLoading?<Skeloton>:&lt;Table column={tableCoumn(내부에서 useTranslation훅)}&gt;</li>
</ul>
</li>
<li>i18next로 변경하면서 관련 파일 수정</li>
<li>GPM table key 에러 수정</li>
<li>권한 라우터 수정</li>
</ul>
</li>
</ul>
<h2 id="조직-개편과-개발실-이사-그리고-삶의-질-개선">조직 개편과 개발실 이사, 그리고 삶의 질 개선</h2>
<p>회사가 시리즈B 투자를 받으면서 본격적으로 성장할 준비를 하고있다. C Level 임원진을 구성하고, 지금 업무를 좀 더 체계적으로 하기 위해 조직 개편도 했다. 채용도 더 많이 늘릴 예정이라고 한다. 개발실은 당장 개편의 영향을 받은 것은 아니라서 하는 일에서 달라진건 없다. 다만 사수팀이 팀장님!이 되셨고, 우리 파트너센터의 R&amp;R을 선명하게 하고, 회사 안에서 해야할 일을 적극적으로 찾고 있다. 회사 입사할 때 해야할 일이 많아 보였고, 회사의 가능성이 너무 좋았는데 기대했던대로 회사가 성장하고 있다. 시스템을 잡아가는 과정, 조직이 성장하는 과정에서 많이 배우고, 그 안에서 내가 해야할 일도 적극적으로 찾아서 해야지. </p>
<p>인원이 늘어나면서 사무실이 좁아져 내년 초 이사전까지 개발실은 위워크로 이사를 했다. 회사 여기저기서 들리던 웃음소리가 사라져 적막한데 차분한 분위기나 시설은 좋다. 강남역으로 이사한 덕에 출퇴근 시간이 십분정도 줄었는데 아침저녁으로 큰 차이다. 퇴근 후에 조금 더 여유가 생겼다. 계속 여기 있으면 좋겠는데 과연...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Udemy 프론트엔드 개발자 성장 가이드를 보고 뼈맞은 후기]]></title>
            <link>https://velog.io/@ouo_yoonk/Udemy-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%84%B1%EC%9E%A5-%EA%B0%80%EC%9D%B4%EB%93%9C%EB%A5%BC-%EB%B3%B4%EA%B3%A0-%EB%BC%88%EB%A7%9E%EC%9D%80-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@ouo_yoonk/Udemy-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%84%B1%EC%9E%A5-%EA%B0%80%EC%9D%B4%EB%93%9C%EB%A5%BC-%EB%B3%B4%EA%B3%A0-%EB%BC%88%EB%A7%9E%EC%9D%80-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Wed, 22 Dec 2021 12:48:00 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/ouo_yoonk/post/bbc4c64e-f4d7-4719-8f88-89f3fbb2e2fa/9995DA4D5E097E7C2D.gif" alt=""></p>
<h1 id="📄-내용-정리">📄 내용 정리</h1>
<h2 id="프론트엔드-개발자란">프론트엔드 개발자란?</h2>
<ul>
<li><code>HTML</code>은 <code>WEB</code>으로 연결된 <code>문서</code>로 <code>브라우저</code> 위에서 동작하는 문서이며, <code>다양한 기기</code> 위에서 동작한다.</li>
<li>FE 개발 범위 : Backend For Fronted, 백오피스, 대시보드, 차트, UI 개발, 퍼블리셔, 코어, 모듈, 라이브러리, 디자인 시스템, 모바일</li>
<li>필수 역량 : 1. HTML  2.javascript  3.CSS  4.브라우저  5.네트워크</li>
</ul>
<h2 id="학습의-주도권-가져오기">학습의 주도권 가져오기</h2>
<ul>
<li>오늘은 JavaScript 호이스팅에 대해서 공부해야지 vs 오늘은 땡땡 자바스크립트 책 1페이지부터 400페이지까지 읽어야지</li>
<li>책이나 강의를 끝내는 것을 목적으로 공부하는 것은 학습의 주도권을 뺏긴 것이다</li>
<li>책이나 강의 : 블로그 글을 쭈루룩.. → 남이 공부한 것을 따라치고, 나열한 것이 진짜 공부인가?</li>
<li>내가 무엇을 공부해야 할지 모르는 상태 ㅠ</li>
<li>채용공고, 책 목차, 강의 목차 등 <code>키워드 단위</code>로 공부하기!<ul>
<li>추천하는 방법 온갖 책의 목차를 긁어보기 → 나만의 학습목차를 만들기 → 각 책에서 그 부분들을 읽고 정리하기</li>
</ul>
</li>
</ul>
<h2 id="input-vs-output의-혼합">Input vs Output의 혼합</h2>
<ul>
<li>오늘은 Debounce, Throttle에 대해서 공부해야지 vs JavaScript를 이용해서 무한 스크롤을 만들어야지! 거기서 Debounce, throttle을 이용해봐야지.</li>
<li>클론코딩은 서비스, 디자인을 클론하는거지 코드를 클론하는 것이 아님</li>
<li>Input + Output + Feedback</li>
</ul>
<h2 id="자기-객관화">자기 객관화</h2>
<ul>
<li>함께 자라기 : 스터디, 해커톤, 학습 내역 체계화해서 정리하기</li>
<li>회사 지원하기<ul>
<li>Top → Down</li>
<li>Botton → Up</li>
<li>내가 공부할 키워드!</li>
</ul>
</li>
</ul>
<h2 id="피드백-주도-성장">피드백 주도 성장</h2>
<ul>
<li>내 경험치와 점수를 어디서 얻지?</li>
<li><code>더 자주</code>, <code>더 빨리</code>, <code>더 꾸준히</code> 받을 수 있는 피드백!!</li>
<li>테스트코드, 코드리뷰, 짝 프로그래밍, 커뮤니티 참여</li>
<li>문제 바깥에 있는 사람을 맥락을 파악해 문제를 빨리 해결할 수 있다</li>
<li>써먹을 수 있는 환경에서 공부해야</li>
</ul>
<h2 id="qa">Q&amp;A</h2>
<ul>
<li>좋은 개발자란? 문제를 정의하고, 문제를 해결하고, 해결을 공유</li>
<li>어떻게와 왜를 방황도 하면서 직접 찾아보기</li>
<li>업무와 연결되어 있는 공부를 하면 업무와 공부 실력이 함께 늘어갈거예요</li>
<li><code>작은 문제를 얼마나 탁월하게 해결하려 했는지</code>가 중요</li>
</ul>
<h1 id="👩🏻🔧-어떻게-나에게-녹일까">👩🏻‍🔧 어떻게 나에게 녹일까?</h1>
<p> 강의를 듣고 현재 학습 방식의 문제를 파악해봤다.</p>
<ul>
<li>완강, 완독 목적의 공부</li>
<li>피드백이 부족</li>
<li>input의 부족</li>
</ul>
<p>지금 채우고 싶은 부분은 CS와 JavasScript에 대한 깊은 이해,  그리고 당장 회사일을 잘하고 싶다는 것이다. 사실 계속 방황하는 지점은 이 지점인데, 내가 채우고 싶은 부분과 회사 일의 접점이 지금 당장 눈에 보이지 않는다.  그래도 우선은 input을 늘리고 접점이 생길 때까지 해나갈 수 밖에 없다고 생각한다. 다만 일을 할 때 만난 <code>문제를 탁월하게 해결하는 습관</code>을 꾸준히 길러야겠다고 생각했다.  </p>
<p>공부 방법에도 변화를 주려고 한다. 지금까지는 책을 처음부터 끝까지 읽거나, 강의를 끝까지 보는 형식이었는데 꾸역꾸역 해왔다. 하지만 남는게 끝냈을 때의 약간의 성취감뿐이라는 것을 인정해야겠다. 가고 싶은 회사의 채용공고와 면접 문제들을 자주 보면서 <code>가장 궁금한 키워드</code>들 위주로 공부를 하려한다. 목차가 흥미로운 책을 발견했는데, 한 번에 여러 주제가 아닌 하나의 주제로 다양한 자료들을 보면서 공부하고, 내용을 옮겨적는 포스팅이 아닌 공부한 것을 다시 정리하는 포스팅을 하려한다. </p>
<p>그래도 나는 피드백을 받기 좋은 환경에 있는데, 사수님에게 한달에 한번 면담을 하면서 조언을 구하거나 일에 대한 피드백을 받고 있고, 코드리뷰도 하고 있다. 내년에는 내가 해온 공부의 결과와 방향성에 대한 피드백을 주기적으로 인프런 멘토링을 통해 받으려고 한다. 그러려면 해온 결과물이 있어야하기 때문에 그 자체로도 동기부여가 될것이라고 생각한다. </p>
<p>그래서 지금 머리속의 계획을 정리하자면</p>
<ol>
<li>‘한 권으로 읽는 컴퓨터 구조와 프로그래밍’ 목차별로 공부하기</li>
<li>일할 때 만난 문제를 탁월하게 해결하는 습관 기르기 </li>
<li>사수님에게 피드백 적극적으로 요청하기</li>
<li>한달에 한번 멘토링 받기 - 멘토링을 위한 준비 열심히 하기</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[주니어개발자로 성장하는 우아한 방법들]]></title>
            <link>https://velog.io/@ouo_yoonk/%EC%A3%BC%EB%8B%88%EC%96%B4%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A1%9C-%EC%84%B1%EC%9E%A5%ED%95%98%EB%8A%94-%EC%9A%B0%EC%95%84%ED%95%9C-%EB%B0%A9%EB%B2%95%EB%93%A4</link>
            <guid>https://velog.io/@ouo_yoonk/%EC%A3%BC%EB%8B%88%EC%96%B4%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A1%9C-%EC%84%B1%EC%9E%A5%ED%95%98%EB%8A%94-%EC%9A%B0%EC%95%84%ED%95%9C-%EB%B0%A9%EB%B2%95%EB%93%A4</guid>
            <pubDate>Wed, 01 Dec 2021 13:40:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://www.youtube.com/watch?v=Qtg5xe6B_vA&amp;t=3s">2020년 7월 우아한테크세미나 주니어 개발자로 성장하는 우아한 방법들</a>을 보고 정리한 내용입니다 </p>
</blockquote>
<h1 id="들어가기-전에">들어가기 전에</h1>
<h3 id="왜">왜?</h3>
<p>일 잘하는 주니어 개발자로 성장하고 싶은데 어떤 공부를 어떻게 해야할지 모르겠다. 그래서 그것에 대한 힌트를 얻고 싶다.</p>
<h3 id="얻는것">얻는것?</h3>
<ul>
<li>앞으로 공부 방향</li>
<li>성장에 관해 내가 몰랐거나 간과했던 것드</li>
</ul>
<h3 id="어떻게">어떻게?</h3>
<ul>
<li>영상을 보고 블로그에 내용과 감상을 정리한다</li>
<li>그것을 바탕으로 구체적인 공부 방향을 정해 실행한다</li>
</ul>
<h1 id="fe-개발자가-되기위한-10가지-방법">FE 개발자가 되기위한 10가지 방법</h1>
<h2 id="1-현장-이해하고-대응하기">1. 현장 이해하고 대응하기</h2>
<ul>
<li>함께 일하기</li>
<li>분석,개발,테스트,배포,피드백,개선</li>
<li>제품과 서비스의 지속적인 운영</li>
</ul>
<h4 id="현장에서의-어려움">현장에서의 어려움:</h4>
<ul>
<li>커뮤니케이션의 어려움</li>
<li>일의 순서 정리</li>
<li>다양한 제약사항</li>
<li>품질과 일정</li>
<li>형상관리</li>
<li>테스팅의 어려움</li>
</ul>
<h4 id="어떻게-1">어떻게?</h4>
<ul>
<li>프로젝트하기!<ul>
<li>작은 프로젝트 -&gt; 혼자하는 프로젝트 -&gt; 같이하는 프로젝트</li>
</ul>
</li>
<li>문제 해결 능력 향상!<ul>
<li>다양한 방법으로 문제를 해결하고, 회고하고</li>
<li>디버깅 잘 이해하기</li>
<li>다른 사람의 방법 배운기</li>
</ul>
</li>
</ul>
<h2 id="2-프로젝트를-통해서-js-익히기">2. 프로젝트를 통해서 JS 익히기</h2>
<ul>
<li>JavaScript 익히는 것이 가장 중요하다!</li>
<li>책과 프로젝트의 거리감 줄이기</li>
<li>프로젝트를 하면서 언어 책을 함께 공부하자.</li>
<li>면접에서 이론을 묻는 이유는 <strong>기초지식을 충분히 알고 대응하는 개발자인지</strong>.</li>
</ul>
<h2 id="3-향상된-ux를-목표로-개발하기">3. 향상된 UX를 목표로 개발하기</h2>
<ul>
<li>FE 개발자는 사용자와 접점</li>
<li>완벽한 코드보다, 사용성 향상(성능, 매끄러움)</li>
<li>사용자가 쓰기에 불편함 없는 인터랙션 개발</li>
<li><em>CSS3, COM, Event, Animation 제어, 비동기에 대한 이해</em></li>
</ul>
<h2 id="4-함께-자라기">4. 함께 자라기</h2>
<ul>
<li>함께 학습하기, 간단한 프로젝트 함께 만들어보기, 피드백</li>
<li>업무 분석, github issue/project로 일감 만듫어서 관리하기, PR보내기, git branch 전략 세워보기</li>
</ul>
<h2 id="5-기초는-튼튼하게">5. 기초는 튼튼하게</h2>
<ul>
<li>자료구조와 알고리즘</li>
<li>네트워크 : HTTP 중심으로 공부하자, __개발자도구의 네트워크 탭_을 다 이해해보자, 개발하면서 알게 되는 용어를 찾아서 공부하기</li>
</ul>
<h2 id="6-html-css에서-fe엔지니어로">6. HTML, CSS에서 FE엔지니어로!</h2>
<ul>
<li>프로그래밍 사고 늘리기</li>
<li>JS 책만 보지 말고, 무언가 만들어보기</li>
<li>조건, 반복, 함수를 활용한 호출관계, 모듈을 만드는 방법</li>
<li>알고리즘 문제, 쉬운문제를 많이 꾸준히 푸는 연습</li>
<li>자바스크립트 프로그래밍 연습</li>
</ul>
<h2 id="7-양--질">7. 양 &lt; 질!</h2>
<ul>
<li>로드맵 다하려면 10년 지남 </li>
<li>주니어는 다 아는가?가 아니고 <strong>무언가를 찐하게 대할 수 있는지가 더 중요</strong></li>
<li>내가 하고 잇는 분야를 깊게 파고 있는가?<ul>
<li>웹 사이트를 꾸준히 만들고 피드백을 받으며 개선했나/</li>
<li>좀 더 사용성을 올리기 위해서 다양한 방법을 찾아보고 시도했는가?</li>
<li>좀더 나은 코드품질을 위해서 다양한 리패토링을 시도하고 실험했는가?</li>
<li>내가 얻어낸 코드를 공유하고, 노하우를 ㅇ알리거나, 오픈소스화 햇는가</li>
</ul>
</li>
</ul>
<h2 id="8-react-vue-angular-익히기겉-보다는-속">8. React, Vue, Angular 익히기(겉 보다는 속)</h2>
<ul>
<li>모두 다 할 필요는 없다</li>
<li>바닐라도 훈련이 충분이 된 상태에서 접근</li>
<li><strong>이미 프레임워크를 다뤘다면 바닐라도 프레임워크 흉내내보기</strong></li>
<li>특정 프레임워크 비의존적인 부분에 대한 학습<ul>
<li>각 프레임워크의 동작 방식</li>
<li>렌더링 원리</li>
<li>상태 관리</li>
<li>모듈 관리</li>
<li>성능에 유리한 코딩방식</li>
</ul>
</li>
</ul>
<h2 id="9-내가-선택한-길이-맞을까">9. 내가 선택한 길이 맞을까?</h2>
<ul>
<li>열심히 그리고 즐겁게 개발하고 있다면 틀린 길은 없음</li>
<li>나를 믿고 전진하기</li>
<li><strong>이것저것 왔다갔다 안하기</strong></li>
<li>피드백을 받을 수 있는 방법을 찾아보기</li>
<li>다양한 멘토와 코드리뷰</li>
</ul>
<h2 id="10-나를-알아봅시다">10. 나를 알아봅시다</h2>
<ul>
<li>이력서 써보기</li>
<li>면접 기회 얻어보기</li>
</ul>
<h1 id="그래서-앞으로">그래서 앞으로?</h1>
<p>지금 정도의 수준에서 뭘 어떻게 해야할지 감이 안잡혔는데 강의를 듣고 <strong>어쨌든 바닐라 자바스크립트</strong>!라는 해답을 얻었다. 끊어둔 자바스크립트 강의가 있었는데 들었던 것과 비슷한 수준의 강의고, 이미 다 할 줄 안다고 생각해 흥미가 안 붙었는데 마음을 고쳐먹기로 했다. </p>
<ul>
<li>사놓은 강의는 들으면서 몰랐던 것을 하나라도 더 익히기</li>
<li>리액트의 라우터, 렌더링, 상태 관리 등을 직접 구현해보기</li>
<li>알고리즘 문제 꾸준히 풀기
를 하기로 했다. 이것을 완료하면 인프런 멘토링을 통해 결과물에 대한 피드백을 받고, 그 다음 스텝에 대한 조언을 듣는 걸로 목표를 잡았다.</li>
</ul>
<p>그리고 당연한지만 회사 코드를 좀 더 뚫어져라 보고, 회사에서 쓰는 기술, 웹팩 설정, CI/CD, AWS 리소스 관리, git 정책 등을 최대한 내걸로 만들어야겠다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[요즘 고민]]></title>
            <link>https://velog.io/@ouo_yoonk/%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%84%B1%EC%9E%A5%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@ouo_yoonk/%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%84%B1%EC%9E%A5%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Wed, 01 Dec 2021 11:58:34 GMT</pubDate>
            <description><![CDATA[<p>벌써 1년 6개월정도 경력의 2년차 개발자다. 두둥. 1년은 SI 개발을 했고, 지금은 스타트업에서 프론트엔트 개발자로 일하고 있다. 29살에 경력을 시작해, 첫 5개월은 개발을 한다는 자체가 기뻤다. 학원 출신이라 CS에 대한 갈증이 있었기 때문에 강의와 책을 통해 CS 공부를 조금씩했다. 그 때까지도 지금 개발 세계에서 펼쳐지고 있는 일들을 거의 몰랐다. 첫 프로젝트는 나름 만족스러웠다. SI는 악명이 높다지만 나는 운이 좋았는지 적당한 업무 강도, 적당한 환경이었기 때문이다. </p>
<p>그 다음으로 알만한 IT 회사의 외주 프로젝트를 했다. 그 때 엄청난 자극을 받았다. 나는 들어본적도 없는 들어본 도커나 AWS를 쓰고, 좋은 코드를 짜기 위해 노력하고, 스프린트를 진행하고, 회사 사옥도 좋았다. 지나다니는 사람들도 다 개발자고, 누군가의 책상 위에 GoLang 책이 놓여있었다. 내가 얼마나 뒤처져있는지 알았고, 내가 원하던 개발자는 이런거였구나 싶었다. 그래서 &#39;프론트엔드 개발자로 회사에 취직하기&#39;를 목표로 했다. 그 때부터 깃헙을 관리하고, 포트폴리오를 만들고, 프론트엔드 관련된 강의를 들으며 준비했고 다행히 지금 회사에 취직을 하게 되었다. </p>
<p>처음 3달동안은 팍팍 성장하는게 느껴졌다. 좋은 개발문화를 만들기 위해 노력하고, 코드리뷰도 받고, 회사 도메인에 대해 이해도 하고, 프로젝트 구조에 대해 고민하고, 어떤 기술 스택을 쓸지 공부하고... 그러면서 취업준비+재택근무 오래하다가 오랜만에 출근+통근 왕복 3시간+회사적응등등의 이유로 착실히 쌓인 피로가 터지면서 퍼져버렸다. 공부를 하지 않으면 불안한 마음이 늘 있었는데 그럴 마음 조차들지 않을만큼 피곤에 절어서 퇴근하면 자기 바빴고, 성장이라는 단어에 신물이 났다. 그렇게 일만하면서 회복하기를 또 한 두달.</p>
<p>그리고 지금이다. 퇴근하고 컴퓨터 앞에 앉을만큼 체력과 정신력이 생겼고, 성장하고 싶다는 욕구가 퐁퐁 솟아오르는데 이제 어떻게 해야할지 모르겠다. 
지금 모르는 것을 나열해보면</p>
<ul>
<li>성장이 뭐지</li>
<li>어떻게 해야하지</li>
<li>나는 지금 회사에서 일을 잘하고 있나</li>
<li>일을 잘한다는건 뭐지</li>
<li>일을 잘하려면 뭘해야하나</li>
<li>곧 3년차가 되는데 이대로 괜찮나</li>
<li>약속의 3년에 쓸만한 개발자는 뭘 어떻게 해야하지</li>
<li>연봉협상을 위해서 어필할게 지금 있나</li>
<li>이직을 한다고 했을 때 과연 가능한 수준인가
....이다. </li>
</ul>
<p><img src="https://media4.giphy.com/media/ZaJtnTY8tFZz0PvmW9/giphy.gif?cid=ecf05e47prwqci47g83iln2916x3y0968mm6vflpp67gxt1n&rid=giphy.gif&ct=g" alt="">
<del>뭐이렇게 모르는게 많아?</del></p>
<p>다시 정리해보면 나의 핵심 욕구는 성장하고 싶다와 일을 잘하고 싶다.
그런데 개발자로 성장하는 것과 일을 잘하는 것이 어떤 건지 모르겠다.
그래서 어떻게할지 모르겠다.</p>
<p>자바스크립트 강의를 쭉 들었고, 또 강의를 끊어는 놨는데 이게 맞나 싶어서 집중을 못하고 있다. 그래서 우선을 방향과 목표를 구체적으로 정하는게 좋을 것 같아 개발자 성장에 대한 글이나 영상을 보고 정리하려고 시작한 글이었는데...이렇게 글이 길어졌네.
<img src="https://pbs.twimg.com/profile_images/1015466287143632896/6TzAgBor_400x400.jpg" alt=""></p>
<p>덕분에 생각을 정리했으니 이제 몸을 움직일 차례다. 무브무브.</p>
<p><img src="https://s3.ap-northeast-2.amazonaws.com/univ-careet/FileData/Picture/202106/4f569b69-5bae-4489-ae66-294a3fc20903_770x426.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[This]]></title>
            <link>https://velog.io/@ouo_yoonk/This</link>
            <guid>https://velog.io/@ouo_yoonk/This</guid>
            <pubDate>Sun, 28 Nov 2021 07:05:20 GMT</pubDate>
            <description><![CDATA[<p>당근 게임을 만들면서 아래의 코드를 실행했을 때 <code>undefined</code>가 출력되었다.</p>
<pre><code class="language-javascript">this.field.addEventListener(&#39;click&#39;,this.onClick)</code></pre>
<p>this.onClick은 할당이 되어있는 상태였는데 왜..? 
이유는 click의 콜백으로 전달할 때 클래스의 정보는 안 넘어가기 때문이었다.</p>
<p>그래서 this를 공부하고 정리했다.</p>
<blockquote>
<p>this는 함수가 호출될 때 결정된다.</p>
</blockquote>
<h3 id="전역공간에서-this는-전역-객체를-가리킨다">전역공간에서 this는 전역 객체를 가리킨다</h3>
<h3 id="함수-호출시--전역객체">함수 호출시 : 전역객체</h3>
<ul>
<li><p>함수를 호출하는 순간 함수를 실행하는 주체는 전역객체이기 때문</p>
<pre><code class="language-javascript">function a(){
      console.log(this); //window
}

  a()
</code></pre>
</li>
</ul>
<p>function b(){
    function c(){
            console.log(this) 
    }</p>
<p>   c()<br>}</p>
<p>b();// window  </p>
<pre><code>### 메서드 호출시 : 호출한 대상 객체가 바인딩 됨

``` javascript
var d = {
    e:function(){
        function f(){
            console.log(this);
        }
      f(); // window
    }
}

d.e() // d
</code></pre><h3 id="callback-호출시-기본적으로는-함수내부에서와-동일하다">callback 호출시 기본적으로는 함수내부에서와 동일하다.</h3>
<ul>
<li>기본적으로 함수의 this과 같다.</li>
<li>제어권을 가진 함수가 콜백의 this를 지정해둔 경우도 있다.(call, apply,bind)</li>
</ul>
<h3 id="callapplybind">call,apply,bind</h3>
<pre><code class="language-javascript">function a(x,y,z){
    console.log(this,x,y,z);
}

var b = {
    bb:&#39;bbb&#39;
};

a.call(b,1,2,3) // {bb:&quot;bbb&quot;} 1 2 3

a.apply(b,[1,2,3]); // // {bb:&quot;bbb&quot;} 1 2 3

var c= a.bind(b);
c(1,2,3); // {bb:&quot;bbb&quot;} 1 2 3

var d = a.bind(b,1,2);
d(3) //{bb:&quot;bbb&quot;} 1 2 3</code></pre>
<h3 id="생성자함수-호출시">생성자함수 호출시</h3>
<pre><code class="language-javascript">function Person(n,a){
    this.name = n;
      this.age = a;
}

var gisele1 = Person(&#39;나다&#39;,30);
console.log(window.name, window.age); // 나다, 30

var gisele2 = new Person(&#39;나다&#39;,30);
console.log(gisele); //Person{age:30,name:&#39;나다&#39;}</code></pre>
<h3 id="es6의-arrow-function은-this를-바인딩하지-않고-스코프-체인상의-this에-바로-접근할-수-있다">ES6의 Arrow function은 this를 바인딩하지 않고, 스코프 체인상의 this에 바로 접근할 수 있다.</h3>
<p>다시 맨 처음 문제로 돌아와서 콜백 함수에 class를 바인딩하기 위해서는 세 가지 방법이 있다.</p>
<pre><code class="language-javascript">// 1. bind 함수로 this바인딩 해주기
this.onClick = this.onClick.bind(this);

// 2. 콜백함수 내부에서 화살표 함수로 event 넘겨주기
this.field.addEventListener(&#39;click&#39;, (event) =&gt; this.onClick(event));

// 3. onClick 함수를 화살표 함수로 선언하기

this.field.addEventListener(&#39;click&#39;, this.onClick);
...
onClick = (event) =&gt; {
    // arrow function은 자동으로 this바인딩
    const target = event.target;
    if (target.matches(&#39;.carrot&#39;)) {
      target.remove();
      playCarrot();
      this.onItemClick &amp;&amp; this.onItemClick(&#39;carrot&#39;);
    } else if (target.matches(&#39;.bug&#39;)) {
      this.onItemClick &amp;&amp; this.onItemClick(&#39;bug&#39;);
      playBug();
    }
  };</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[실행 컨텍스트]]></title>
            <link>https://velog.io/@ouo_yoonk/%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@ouo_yoonk/%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Sun, 28 Nov 2021 06:32:33 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/ouo_yoonk/post/da1bc9b1-3acb-4588-805f-20575a397b8d/image.png" alt=""></p>
<h1 id="실행컨텍스트란">실행컨텍스트란?</h1>
<ul>
<li>context : 이 자리에서 어떤 역할을 수행하는지 알기 위해서는 그 코드에 영향을 주는 주변 코드나 변수들을 파악해야 한다. 그렇게 영향을 주는 환경을 일컬어 context라고 한다. 해당 코드의 배경이 되는 조건/환경</li>
<li>실행 컨텍스트 : 어떤 조건/환경을 지니는 코드 뭉치를 실행할 때 필요한 조건/환경정보</li>
<li>자바스크립트에서 동일한 조건을 지닐 수 있는 조건 : 전역공간, 함수, eval, module</li>
<li>자바스크립트는 함수에 의해서만 context를 구분할 수 있다. 즉, 함수를 실행할 때 필요한 조건, 환경정보를 담은 객체</li>
</ul>
<h1 id="실행-컨텍스트의-내부의-환경정보">실행 컨텍스트의 내부의 환경정보</h1>
<ul>
<li>현재 환경과 관련된 식별자 정보들</li>
<li>VariableEnvironment : 식별자 정보 수집, 변화 반영x</li>
<li>LexicalEnvironment(Environment Record, outer Environment Reference)<ul>
<li>각 식별자의 데이터 추적, 변화 반영ㅇ</li>
<li>어떤 실행 컨텍스트의 환경정보가 담긴 사전이라고 생각하면 됨</li>
<li>실행컨텍스트를 구성하는 환경정보들을 모아 사전처럼 구성한 객체를 의미한다</li>
<li>Environment Record: 현재 문맥의 식별자 정보, 실행 컨텍스트가 최초 실행될 때 하는 일, <ul>
<li>hoising : 현재 컨텍스트의 식별자 정보들을 수집해서 environmentRecord에 담는 과정, 실제 일어나는 현상이 아니고 environment Record 과정을 좀 더 쉽게 이해하기 위해서 만든 허구의 개념 </li>
</ul>
</li>
<li>outer Environment Reference : 외부 환경에 대한 참조, scope chain 현상, 실행 컨텍스트가 수집해 놓은 정보만 접근을 할 수가 있음 -&gt; 변수의 유효범위(스코프)</li>
</ul>
</li>
</ul>
<ul>
<li>ThisBinding</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[브라우저101]]></title>
            <link>https://velog.io/@ouo_yoonk/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80101</link>
            <guid>https://velog.io/@ouo_yoonk/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80101</guid>
            <pubDate>Sun, 28 Nov 2021 05:50:40 GMT</pubDate>
            <description><![CDATA[<h1 id="browser101">browser101</h1>
<p><a href="https://academy.dream-coding.com/courses/browser101">드림코딩 - 프론트엔드 필수 브라우저101</a> 강의를 보고 공부한 내용입니다.</p>
<h1 id="새로-알게-된-것">새로 알게 된 것</h1>
<ul>
<li><a href="https://gisele-dev.tistory.com/15?category=963625">DOM</a></li>
<li><a href="https://gisele-dev.tistory.com/16?category=963625">Web APIs</a></li>
<li><a href="https://gisele-dev.tistory.com/18?category=963625">브라우저 렌더링 순서</a></li>
<li><a href="https://gisele-dev.tistory.com/21?category=963625">Event</a></li>
<li><a href="https://gisele-dev.tistory.com/24?category=963625">자바스크립트 런타임 환경</a></li>
<li><a href="https://velog.io/@ouo_yoonk/This">this</a></li>
<li>class, construct 실습</li>
<li>builder 패턴</li>
</ul>
<h1 id="당근게임">당근게임</h1>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/0ec675a9-c093-4afd-bef7-376fa589c9f1/image.png" alt=""></p>
<h2 id="요구사항-정의">요구사항 정의</h2>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/458626fd-3b67-43bf-bd7d-f6b6b3f9a3bb/image.png" alt=""></p>
<ul>
<li><p>타이머
[v] 타이머 시작
[v] 타이머 종료
[v] 타이머 시간 표시</p>
</li>
<li><p>음악
[v] 음악 시작, 정지
[v] 당근 소리
[v] 벌레 소리</p>
</li>
<li><p>필드
[v] 당근 배치
[v] 벌레 배치</p>
</li>
<li><p>팝업
[v] 팝업
[x] 리플레이</p>
</li>
<li><p>게임
[v] 게임 시작
[v] 게임 종료</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[CRP 브라우저 렌더링 순서]]></title>
            <link>https://velog.io/@ouo_yoonk/CRP-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%88%9C%EC%84%9C</link>
            <guid>https://velog.io/@ouo_yoonk/CRP-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%88%9C%EC%84%9C</guid>
            <pubDate>Sun, 28 Nov 2021 05:47:45 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/ouo_yoonk/post/2ca44d51-202c-4234-ad69-66742c59dcd0/image.png" alt=""></p>
<h1 id="critical-rendering-path">Critical rendering path</h1>
<blockquote>
<p>브라우저가 HTML, CSS, Javascdript를 화면에 픽셀로 변환하는 일련의 단계</p>
</blockquote>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/ae3a0158-0a4e-44f2-842b-f30712c612cf/image.png" alt=""></p>
<p>CRP는 DOM, CSSOM, Render tree, Layout, Paint를 포함한다. CRP를 이해하고 최적화하는 것은 뛰어난 사용자 상호작용을 보장하며 버벅거림을 피할 수 있도록 하고, <code>1초당 60프레임</code>에 리플로우와 리페인트가 발생할 수 있도록 하는데 중요하다.</p>
<p>브라우저는 요청 후에 서버로부터 HTML을 응답받는다. 응답받은 HTML을 분석하고, 수신된 바이트를 <code>DOM트리</code>로 변환한다. 브라우저는 스타일시트, 스크립트 또는 외부 자원(이미지 참조 등)에 대한 링크를 찾을때마다 요청을 한다.(불러온 에셋을 다룰 때까지 나머지 HTML을 분석 작업은 중단됨) CSSOM이 완료되면 브라우저는 렌더 트리를 생성하고 보여지는 컨텐츠를 위해 스타일을 계산한다. 렌더트리가 완료된 후 모든 렌더 트리 요소들에 대한 위치와 크기가 정의된 레이아웃이 만들어진다.</p>
<table>
<thead>
<tr>
<th><strong>Construction</strong></th>
<th><strong>Construction</strong></th>
</tr>
</thead>
<tbody><tr>
<td>DOM-&gt;CSSOM -&gt; Render Tree</td>
<td>Layout -&gt; Paint -&gt; Composition</td>
</tr>
</tbody></table>
<h2 id="1-dom">1. DOM</h2>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/dcd7a4de-afce-41b9-a5ee-63e28411e90a/image.png" alt=""></p>
<ul>
<li>DOM Tree는 완전하게 파싱된 HTML 페이지의 Object 표현이다.</li>
<li>html로부터 시작해 각 element, text에 대한 <code>node</code>가 만들어진다.</li>
<li>DOM Tree 생성은 점진적으로 진행된다. 페이지에 내용을 표시하기 위해 문서 전체를 로드할 필요가 없다.</li>
<li>CSS와 javascript 로드시에 페이지의 렌더링을 차단할 수 있다.</li>
<li>불필요한 tag나 wrapper는 사용하지 않고, div를 남용하는 대신 sementic 태그를 이용하는 것이 성능에 좋다.</li>
</ul>
<h2 id="2-cssom">2. CSSOM</h2>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/0b680a5d-793c-4708-9fc9-7f4912730e1e/image.png" alt=""></p>
<ul>
<li>DOM을 스타일링 하기 위한 페이지의 모든 스타일 정보</li>
<li>CSS는 상속과 덮어쓰기가 가능하기 때문에 CSS 전체가 파싱되기 전에 사용하면 잘못된 스타일이 적용될 수 있다.</li>
<li>따라서 브라우저는 모든 CSS를 수신하고 처리할 때까지 페이지 렌더링을 막는다.</li>
<li>구체적인 선택자보다 덜 구체적인 선택자가 더 빠르지만 성능차이가 크지는 않다.</li>
</ul>
<h2 id="3-render-tree-만들기">3. Render Tree 만들기</h2>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/6c84be23-6eea-40a4-88ee-1999198133a9/image.png" alt=""></p>
<ul>
<li>DOM과 CSSOM이 합쳐진 것으로 페이지에서 최종적으로 렌더링할 내용을 나타내는 Tree</li>
<li>Render Tree는 오직 보여지는 node만 캡쳐한다.<ul>
<li>예] display:none 속성은 렌더트리에 포함되지 않는다.(visible:hidden은 포함됨)</li>
</ul>
</li>
</ul>
<h2 id="4-layout">4. Layout</h2>
<ul>
<li>요소들이 페이지에서 배치되는 위치와 방법, 각 요소의 <code>너비</code>와 <code>높이</code> 그리고 서로 관련된 <code>위치</code>를 결정한다.</li>
<li>뷰포트 메타 태그는 레이아웃의 너비를 정의한다.</li>
</ul>
<pre><code>
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;</code></pre><ul>
<li>레이아웃은 디바이스가 회전하거나 브라우저의 사이즈가 조정될 때마다 발생한다.</li>
<li>레아아웃의 성능은 DOM의 영향을 받는다. 노드의 수가 많을수록 레이아웃은 더 길어지며, 스크롤링 또는 다른 애니메이션이 있으면 레이아웃에 jank를 일으키는 병목현상이 발생할 수 있다.</li>
<li>레아아웃 이벤트의 반복과 형성시간을 줄이기 위해서 일괄 업데이트를 해야하고, 박스 모델 속성을 애니메이션화 하지 말아야 한다.</li>
<li>박스모델 : HTML 요소를 padding, border, margin, content 으로 구분하는 것.</li>
</ul>
<h2 id="5-paint">5. Paint</h2>
<ul>
<li>화면에 픽셀을 그리는 단계.</li>
<li>렌더 트리가 생성되고 레이아웃이 나타나기 시작하면 화면에 픽셀을 그릴 수 있다.</li>
<li>load시에 전체 화면을 그리고, 이후에는 브라우저가 필요한 최소 영역만 다시 그리도록 최적화 되어있다</li>
<li><code>Layer 단위</code>로 준비(z-index 등). 변화가 있을 때 해당 레이어만 수정하도록 최적화되어 있다.</li>
</ul>
<h2 id="6-composition">6. Composition</h2>
<ul>
<li>준비한 레이어를 화면에 놓는 과정</li>
</ul>
<h2 id="crp-최적화-방법">CRP 최적화 방법</h2>
<ul>
<li>자원 로드 순서 관리</li>
<li>파일 사이즈 줄이기</li>
<li>자원 다운로드를 연기함으로써 중요 자원들의 수를 최소화</li>
<li>Operation 과정에서 layout, paint는 되도록 적게 발생해야 함</li>
<li>ex] element를 이동할 때 top, left 속성을 변경하면 layout+paint+composition이 다 발생하지만 translate를 이용할 경우 composition만 발생한다.</li>
</ul>
<p><a href="https://csstriggers.com/">https://csstriggers.com/</a> 참고</p>
<hr>
<p><strong>📑 referece</strong></p>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Web/Performance/Critical_rendering_path">MDN - 중요 렌더링 경로</a></li>
<li><a href="https://bitsofco.de/understanding-the-critical-rendering-path/">https://bitsofco.de/understanding-the-critical-rendering-path/</a></li>
<li><a href="https://wonism.github.io/critical-rendering-path/">Woonism Ciritical Rendering Path란</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[파일 업로드와 다운로드]]></title>
            <link>https://velog.io/@ouo_yoonk/%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EC%99%80-Blob</link>
            <guid>https://velog.io/@ouo_yoonk/%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%EC%99%80-Blob</guid>
            <pubDate>Tue, 23 Nov 2021 08:41:44 GMT</pubDate>
            <description><![CDATA[<p>프로젝트에서 파일 업로드와 다운로드를 구현하면서 사용한 코드들의 의미를 알아보기 위해 정리한 글입니다.</p>
<h1 id="파일-업로드">파일 업로드</h1>
<blockquote>
<p>flow : 
file input -&gt; 파일 선택 -&gt; formdata 객체 만들기 -&gt; multipart로 전송 </p>
</blockquote>
<h2 id="input-typefile">Input type=file</h2>
<pre><code class="language-javascript">&lt;input type=&quot;file&quot;
       id=&quot;avatar&quot; name=&quot;avatar&quot;
       accept=&quot;image/png, image/jpeg&quot;&gt;</code></pre>
<p>file type은 input 요소의 공용 특성 외에도 추가적인 특성을 지원한다.</p>
<p><code>accept</code>는 선택할 수 있는 파일 유형을 지정할 수 있다. 확장자(.jpg,.pdf,.doc)나 MIME 유형의 문자열을 값으로 입력하면 해당 유형의 파일만 선택할 수 있다.</p>
<p>아래처럼 입력하면 엑셀파일만 선택할 수 있다.
<code>accept=&#39;application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet&#39;</code></p>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/51aeb163-982a-41e4-b62a-ea2755b1c2cf/%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-11-30%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2010.07.45.png" alt=""></p>
<p><code>capture</code>는 이미지 또는 비디오 데이터를 캡처할 때 사용할 방법이고, <code>files</code>는 선택한 파일을 나열하는 FileList다. <code>multiple</code>을 <code>true</code>로 지정하면 여러 개의 파일을 선택할 수 있다.</p>
<p>선택한 파일에는 아래와 같이 접근할 수 있고,</p>
<pre><code class="language-javascript">const selectedFile = document.getElementById(&#39;input&#39;).files[0];</code></pre>
<p>onChange event를 이용해  <code>e.target.files[0]</code>로도 접근할 수 있다.</p>
<h2 id="formdata">Formdata</h2>
<p>FormData 인터페이스는 form 필드와 그 값을 나타내는 일련의 key/value 쌍을 쉽게 생성할 수 있는 방법을 제공한다. 또한 XMLHttpRequest.send() 메서드를 사용해 쉽게 전송할 수 있다.</p>
<p>리액트에서 formData로 파일을 보내는 코드를 아래처럼 짰다.</p>
<pre><code class="language-javascript">  const [targetFile, setTargetFile] = useState&lt;RcFile[]&gt;([]);

const handleFileChange = (e)=&gt;{
    const file = e.target.files[0]
    setTargetFile(file)
}

const onSubmit = ()=&gt;{
    const formData = new FormData();

      formData.append(&#39;fileKey&#39;,targetFile[0])

      axios.post(&quot;api/uploadfile&quot;, formData)
              .then(res =&gt; {
                console.log(res);
              })
              .catch(err =&gt; {
                console.log(err);
              });
}


...
&lt;input type=&quot;file&quot; name=&quot;file&quot; onChange={e =&gt; this.handleFileChange(e)}/&gt;

</code></pre>
<h2 id="multipart">Multipart</h2>
<p>Multipart란 웹 클라이언트가 요청을 보낼 때 http 프로토콜의 바디 부분에 데이터를 여러 부분으로 나눠서 보내는 것을 의미한다. 여러 부분을 하나의 복합 메세지로 보낸다.</p>
<p>form을 통해서 파일을 등록하고 전송할 때 웹 브라우저가 보내는 HTTP 메시지는 Content-Type 속성이 <code>multipart/form-data</code>로 지정되며, 정해진 형식에 따라 메시지를 인코딩하여 전송한다. 이를 처리하기 위해 서버는 멀티파트 메시지에 대해서 각 파트별로 분리하여 개별 파일의 정보를 얻게 된다. </p>
<p><img src="https://images.velog.io/images/ouo_yoonk/post/c56654a8-effe-4cad-b628-1866323a6a22/%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-11-30%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2012.22.46.png" alt=""></p>
<p>HTTP는 위 이미지와 같이 4개의 파트로 나뉘고
Header의 Content-Type 필드로 message body에 들어가는 데이터 타입을 명시한다.</p>
<p>multipart 메시지는</p>
<ul>
<li>서로 붙어있는 여러개의 메시지를 포함하여 하나의 복합 메시지로 보내진다.</li>
<li>MIME multipart 메세지는 <code>Content-type</code> 헤더에 <code>boundary</code> 파라미터를 포함한다</li>
<li><code>boundary</code>는 메시지 파트를 구분하는 역할을 하며, 메세지의 시작과 끝 부분도 나타낸다</li>
<li>boundary를 선택하는 것은 클라이언트의 몫이다. 보통 무작위의 문자(UUID등)을 선택해 메세지의 본문과 충돌을 피한다</li>
<li>HTTP form을 채워서 제출하면, 가변 길이 텍스트 필드와 업로드 될 객체는 각각 멀티파트 본문을 구성하는 하나의 파트가 되어 보내진다. 멀티 파트 본문은 여러 다른 종류의 길이의 값으로 채워진 form을 허용한다.</li>
<li>multipart/form-data : 사용자가 양식을 작성한 결과 값의 집합을 번들로 만드는데 사용한다.</li>
</ul>
<h2 id="대용량-업로드에서-난-에러처리">대용량 업로드에서 난 에러처리</h2>
<p>파일 업로드를 진행하던 중</p>
<blockquote>
<p>504 Gateway Time-out </p>
</blockquote>
<p>에러가 발생했다. 파일 용량이 커서 nginx의 기본 timeout을 넘어갔기 때문에 발생하는 에러였다. 서버와 클라이언트간 connection을 유지하는 시간이 길기 때문이고 nginx의 기본 timout 시간을 바꿔주면 에러는 해결된다.</p>
<p>우리는 서버에서 bull library를 이용해 queue를 만들어 파일부터 전송하고, 업로드 과정은 클라이언트와의 connection과는 상관없이 처리하는 방식으로 구현했다. </p>
<h1 id="파일-다운로드">파일 다운로드</h1>
<blockquote>
<p>flow : 
api 요청 -&gt; response를 blob으로 받아옴 -&gt; 브라우저에서 다운로드</p>
</blockquote>
<h1 id="blob">Blob</h1>
<p>Blob객체는 파일류의 불변하는 미가공 데이터를 나타낸다. 텍스트와 이진 데이터의 형태로 읽을 수 있으며, ReadableStream 으로 변환한 후 그 메서드를 사용해 데이터를 처리할 수도 있다.</p>
<p>Javascript에서 File 인터페이스는 사용자 시스템의 파일을 지원하기 위해 Blob 인터페이스를 확장한 것이므로, 모든 Blob 기능을 상속한다.</p>
<h2 id="생성">생성</h2>
<pre><code class="language-javascript">const blob = new Blob(array,options)

const blob = new Blob([response.data], { type: response.headers[&#39;content-type&#39;] });</code></pre>
<ul>
<li><p>array : ArrayBuffer, ArayBufferView,Blob(file),DOMString 객체 또는 이러한 객체가 혼합된 Array를 사용할 수 있다.</p>
</li>
<li><p>option</p>
<ul>
<li>type : 데이터의 MIME 타입, 기본값은 &quot;&quot;<ul>
<li>endings : \n을 포함하는 문자열 처리를 &#39;transparent&#39;와 &#39;native&#39;로 지정. 기본값은 transparent</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Property는 <code>size</code> <code>type</code>를 가지고, <code>slice(start,end,type)</code>를 사용해 지정된 바이트 범위의 데이터를 포함하는 새로운 Blob 객체를 만들 수 있다. 이를 <code>Chunks 객체</code>라고 한다.</p>
<h2 id="blob-url">Blob URL</h2>
<ul>
<li>Blob 객체를 가리키는 URL 생성 </li>
<li><code>createObjectURL</code> : Blob 객체를 나타내는 URL을 포함한 다음과 같은 DOMString을 생성. 이 Blob URL은 생성된 window의 document(브라우저)에서만 유요하다</li>
<li><code>revokeObjectURL()</code> : URL.createObjectURL()을 통해 생성한 기존 URL을 폐기한다. revokeObjectURL을 통해 해제하지 않으면 자바스크립트 엔진에서 GC되지 않는다. 메모리 누수를 방지하기 위해 생성된 URL을 DOM과 바인딩한 후에는 해제하는 것이 좋다.</li>
<li><code>revokeObjectURL</code></li>
</ul>
<pre><code class="language-javascript">export const downloadFile = (response: AxiosResponse&lt;Blob&gt;) =&gt; {
  const blob = new Blob([response.data], { type: response.headers[&#39;content-type&#39;] });
  const url = window.URL.createObjectURL(blob);
  const link = document.createElement(&#39;a&#39;);
  link.href = url;

  const filename = response.headers[&#39;content-disposition&#39;]
    .split(&#39;filename=&#39;)[1]
    .split(&#39;;&#39;)[0];

  link.setAttribute(&#39;download&#39;, filename);
  document.body.appendChild(link);
  link.click();
  link.remove();
  window.URL.revokeObjectURL(url);
};
</code></pre>
<p><strong>reference</strong></p>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Web/API/Blob">MDN - Blob</a></li>
<li><a href="https://developer.mozilla.org/ko/docs/Web/HTML/Element/Input/file">MDN - input type=file</a></li>
<li><a href="https://heropy.blog/2019/02/28/blob">HEROPY Tech - Blob 이해하기</a></li>
<li><a href="https://lena-chamna.netlify.app/post/http_multipart_form-data">레나참나 - HTTP multipart/form-data이해하기</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>