<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>taek_jini.log</title>
        <link>https://velog.io/</link>
        <description>필요한 것은 노력과 선택과 치킨</description>
        <lastBuildDate>Tue, 05 Nov 2024 14:11:15 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>taek_jini.log</title>
            <url>https://velog.velcdn.com/images/taek_jini/profile/f228ad78-b80b-4d5b-be84-73e324e3f5e1/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. taek_jini.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/taek_jini" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[2024 인프콘/FECONF 후기]]></title>
            <link>https://velog.io/@taek_jini/2024-%EC%9D%B8%ED%94%84%EC%BD%98FECONF-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@taek_jini/2024-%EC%9D%B8%ED%94%84%EC%BD%98FECONF-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Tue, 05 Nov 2024 14:11:15 GMT</pubDate>
            <description><![CDATA[<p>올해 처음으로 컨퍼런스를 다녀왔다.</p>
<h1 id="인프콘">인프콘</h1>
<p>사실 인프콘은.. 추첨 실패했는데 당시 회사 선임님이 많이 배우고 오라고 양보해주셨다 ㅠㅠㅠㅠㅠㅠㅠㅠ
양보해 준 만큼 진짜 많이 보고 많이 느끼고 올 생각으로 열~~심히 듣고 굿즈도 많이 챙겼다 ㅋ</p>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/2d2eea44-f5a8-447b-b695-004d5f499d5f/image.jpg" alt="">
사람 진~짜 많더라 굿즈 줄이 너무 길어서 듣고 싶은 세션들 듣다보면 빠지겠거니.. 싶어서 일단은 세션 듣기로 .. </p>
<p>발표 세션을 들으면서 남들처럼 메모나 기록을 할까 고민했지만, 온전히 발표에만 집중하고 유튜브에 올라온 영상보면서 복기할 생각으로 발표만 열심히 들었다.</p>
<p>발표를 들으면서 주제를 어떻게 전달하고 풀어나가는지에 집중했고 인상 깊었던 내용들을 적어보려고 한다.</p>
<h2 id="목적-조직-구조-안에서-개발팀이-일하는-법">목적 조직 구조 안에서 개발팀이 일하는 법</h2>
<p>패널 토크 형식으로 진행된 세션이였고 목적 조직 구조로 일을 하는 팀이 어떻게 일하는지, 그리고 각 개발자들이 어떤 생각으로 일을 하는지를 들을 수 있었다.</p>
<p>가장 강조했던건 <strong>꾸준한 문서화와 소통</strong> 이였다.</p>
<h2 id="처음으로-기술-리더가-된-개발자를-위한-안내서">처음으로 기술 리더가 된 개발자를 위한 안내서</h2>
<p>&quot;아직 1년차인 내가 들으면서 공감할 수 있을까?&quot; 라는 생각에 들을지 말 지 많이 고민했는데 굉~장히 유익했다. 리더가 아니더라도 다른 팀원에게 좋은 팀원이 될 수 있으니.</p>
<p>팀원이 나를 신뢰하는 습관 7가지</p>
<ul>
<li>유능, 소통, 이익 공유, 유사, 안전, 관심, 예측</li>
</ul>
<p>좋은 얘기들이 많았지만 가장 기억에 남는 키워드였다.
리더가 팀원에게 관심을 갖고 소통하며 안전성을 준다면, 좋은 내용들을 공유하고 성장을 도와준다면 사람 대 사람으로 어느 누가 싫어할 수 있을까. 라는 생각이 들었다.</p>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/bf31cf70-44df-4ed8-a850-1daaf20a51b8/image.jpg" alt=""></p>
<p>이건 열심히 파밍해온 굿즈들... ㅎ 많이도 얻었따ㅏ</p>
<p>마지막으로 네트워킹 시간이 있었는데, 주니어 프론트엔드 개발자들과 만나 대화를 나눌 수 있었다.
각자의 경험과 기술에 대해 이야기하고, 최신 트렌드 라이브러리 및 도구들에 대해 의견을 나누며 많은 인사이트를 얻었다. 대화를 하면서 정말 똑똑하신 분들도 많아서 큰 자극이 됐고, 너~무 유익한 시간이였다.</p>
<h1 id="feconf">FECONF</h1>
<p>FECONF는 선착순이길래 일단 냅다 구매하긴 했는데, 맘에 드는 세션이 많이 없어서,, 라이트닝톡에서 작은 발표들, 가벼운 주제를 다루는 주니어 개발자분들의 발표를 많이 들었다.</p>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/5f7de81b-48ac-4514-a20a-46fb3f1cae0c/image.jpg" alt=""></p>
<h2 id="바퀴-대신-로켓-만들기">바퀴 대신 로켓 만들기</h2>
<p>6개월동안 레거시 코드를 변경하는 업무를 4명의 FE개발자이서 2달 만에 해결했던 과정에 대한 발표다. 가장 흥미롭게 들었던 발표기도 하고, 문제 접근 방식부터 해결점 도달까지 발표를 너무 잘 해주셔서 큰 도움이 됐다.</p>
<p>프론트엔드 개발을 할 때 늦어지는 이유</p>
<ol>
<li>디자인 프로토타입</li>
<li>서버 API</li>
<li>요구사항 분석</li>
</ol>
<h3 id="디자인-프로토타입">디자인 프로토타입</h3>
<ul>
<li>기존 토스에서 사용하는 디자인 시스템의 단점을 제거하고 따로 토스페이먼츠의 제품에 맞는 디자인을 구성해서 유지보수, 확장성, 비효율을 없애나감</li>
</ul>
<h3 id="서버-api">서버 API</h3>
<ul>
<li><p>서버 API는 API SPEC이 아닌 OpenAPI Codegen을 커스텀하여 스펙에 집중하면서 싱크는 자동화</p>
<ul>
<li>OpenAPI Spec(JSON)을 집어넣으면 TypeScript &amp; Zod의 형식으로 타입을 정의해줌으로서 Swagger를 보고 하나하나 인터페이스를 정의할 필요가 없음</li>
</ul>
</li>
</ul>
<h3 id="요구사항-분석">요구사항 분석</h3>
<p>비효율은 개발 생태계가 너무 풍부해서 발생한다. -&gt; 새로운 바퀴들(next,react-query 등)이 계속 생겨난다.</p>
<p>FE 개발자가 어드민 제품을 만들 때 가장 많이 하는 행위</p>
<ul>
<li>Data Fetching</li>
<li>Form control</li>
<li>Table</li>
<li>Logging</li>
</ul>
<p>토스페이먼츠는 오픈소스 React 어드민 툴인 프레임워크를 만듦</p>
<ul>
<li>특징<ul>
<li>Headless : 특정 UI와 결합되어있지 않음</li>
<li>Resource : API 명세를 한 곳에서 객체로 관리</li>
<li>Provider : 필요한 기능들을 provider에 주입 가능</li>
</ul>
</li>
</ul>
<p>이 프레임워크를 통해 반복적인 코드를 줄이고, 개발자가 비즈니스 로직에 더 집중할 수 있게 해준다. 
자주 사용되는 행위들을 미리 구현해 두고, 쉽게 가져다 쓸 수 있으므로 개발 시간이 단축되면서도 코드의 일관성을 유지할 수 있었음.</p>
<p>개발자 모두가 반복적으로 작업하는 업무를 최대한 빠르고 쉽게 사용해서 팀원 모두가 공통된 코드로 개발할 수 있게 구현하는 것이 중요하다는 것을 발표를 통해 크게 깨닫게 되는 시간이였다.</p>
<h2 id="마무리">마무리</h2>
<p>FECONF는 너무 더워서 집 가자마자 뻗는 바람에 굿즈 받은 것들 사진도 못찍었다 ㅋㅋㅋ
컨퍼런스를 올해 처음 와봤는데 현장에서 듣는 게 확실히 더 와닿고 재밌었다.
기회가 된다면 내년에도 꼭 다시 가보고 싶다 ! 그 땐 발표가 지금보다 더 이해가 잘 되기를 빌며..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript의 this 키워드 작동 방식]]></title>
            <link>https://velog.io/@taek_jini/JavaScript%EC%9D%98-this-%ED%82%A4%EC%9B%8C%EB%93%9C-%EC%9E%91%EB%8F%99-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@taek_jini/JavaScript%EC%9D%98-this-%ED%82%A4%EC%9B%8C%EB%93%9C-%EC%9E%91%EB%8F%99-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Wed, 07 Aug 2024 05:50:14 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/taek_jini/post/29251e3d-d53d-477e-964a-74fd6d253239/image.png" alt="">
사내 레거시 코드를 개선하던 중 발생한 트러블 슈팅 내용입니다.</p>
<h1 id="문제-발생-원인">문제 발생 원인</h1>
<p>B2B 페이지의 상품 등록 및 수정 페이지를 리팩토링하면서 발생.</p>
<p><code>ProductForm</code>의 <strong>로직과</strong> <strong>UI</strong>를 분리하기 위해 UI를 <strong>컴포넌트</strong> 단위로 수정했습니다.
UI 컴포넌트에 <code>props</code>로 <code>state</code>를 변경하는 <code>setProduct</code>라는 함수를 <code>Container</code>에서 넘겨줍니다.</p>
<p>UI 컴포넌트에서는 받은 <code>props</code>를 사용할 때  아래 사진과 같은 에러 발생 !!
<img src="https://velog.velcdn.com/images/taek_jini/post/99e59e0f-0f30-449c-b3f3-5b65c8dd01a6/image.png" alt=""></p>
<h1 id="원인-추론">원인 추론</h1>
<h2 id="js에서의-this">JS에서의 this</h2>
<p>JS에서 <code>this</code>는 함수를 호출하는 방식에 따라 그 값이 결정됩니다. 객체의 메서드로 함수를 호출하면, <code>this</code>는 그 객체를 가리킵니다. 하지만, 단순히 함수로 호출하면, <code>this</code>는 전역 객체(브라우저에서는 <code>window</code>, <code>Node.js</code>에서는 <code>global</code>)를 가리키거나, 엄격 모드(&#39;<code>use strict</code>&#39;)에서는 <code>undefined</code>가 됩니다.</p>
<h2 id="js에서의-class">JS에서의 class</h2>
<p>JS 클래스 컴포넌트 내의 메서드는 기본적으로 그 컴포넌트 인스턴스에 바인딩되지 않습니다. 따라서, 컴포넌트의 메서드를 다른 컴포넌트의 <code>prop</code>으로 전달하거나, 이벤트 핸들러로 설정할 때, 그 메서드 내부에서 <code>this</code>를 사용하면, <code>this</code>는 컴포넌트 인스턴스를 가리키지 않게 됩니다. 이는 <code>this.state</code>나 <code>this.setState</code> 같은 컴포넌트의 <code>state</code>에 접근하려 할 때 문제를 일으킵니다.</p>
<h1 id="문제-해결">문제 해결</h1>
<h2 id="해결책1--함수-바인딩">해결책1 : 함수 바인딩</h2>
<p>이 문제를 해결하기 위한 방법 중 하나는 생성자(<code>constructor</code>) 내에서 메서드를 컴포넌트 인스턴스에 명시적으로 바인딩하는 것입니다. 이를 통해, 메서드가 어디서 호출되든 간에 <code>this</code>가 항상 해당 컴포넌트 인스턴스를 가리키도록 할 수 있습니다.</p>
<pre><code class="language-js">constructor(props) {
    super(props);
    this.setProduct = this.setProduct.bind(this);
}</code></pre>
<p>이 코드는 <code>setProduct</code> 메서드 내의 <code>this</code>가 항상 해당 컴포넌트 인스턴스를 가리키도록 보장합니다. 그래서 <code>this.state</code>나 <code>this.setState</code>와 같은 <code>state</code> 관련 작업을 안전하게 수행할 수 있게 됩니다.</p>
<h2 id="해결책2--화살표-함수">해결책2 : 화살표 함수</h2>
<p>클래스 문법을 사용하여 메서드를 화살표 함수로 선언하는 것은 <code>this</code>가 인스턴스를 가리키게 하는 또 다른 방법입니다. 이 방식을 사용하면, 생성자에서 메서드를 바인딩할 필요가 없습니다.</p>
<pre><code class="language-js">setProduct = () =&gt; {
    // 이곳에서의 this는 컴포넌트 인스턴스를 가리킵니다.
}</code></pre>
<p>이 방법은 코드를 좀 더 간결하게 만들어주며, 바인딩을 잊어버려서 발생할 수 있는 버그를 예방해 줍니다.</p>
<blockquote>
<p>현재는 <code>setProduct</code>를 사용하지 않고 <code>productInput</code> 컴포넌트를 활용하는 방식으로 수정했습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[파일 형식(Base64, Blob, ArrayBuffer, File) 개념 및 장단점 정리]]></title>
            <link>https://velog.io/@taek_jini/%ED%8C%8C%EC%9D%BC-%ED%98%95%EC%8B%9DBase64-Blob-ArrayBuffer-File-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%9E%A5%EB%8B%A8%EC%A0%90-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@taek_jini/%ED%8C%8C%EC%9D%BC-%ED%98%95%EC%8B%9DBase64-Blob-ArrayBuffer-File-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%9E%A5%EB%8B%A8%EC%A0%90-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 07 Aug 2024 02:44:53 GMT</pubDate>
            <description><![CDATA[<p>회사에서 B2C 웹 프로젝트를 진행하면서 멀티미디어 파일을 다룰 일이 많았는데, 당시엔 그저 하란대로만 했다.
개발을 하면서도 왜 여기선 buffer를 쓰지? 왜 여긴 base64로 달라고 하지? 같은 자잘한 궁금증을 품고 있었어서 파일 형식에 대한 개념과 장단점을 정리해보려고 한다.</p>
<h2 id="이진데이터binary">이진데이터(binary)</h2>
<p>브라우저에서 멀티미디어 파일을 <strong>텍스트생성, 업로드, 다운로드</strong>를 하거나 <strong>이미지 파일</strong>을 다뤄야한다면 이진데이터를 다뤄야 한다.</p>
<p>컴퓨터는 우리가 사용하는 모든 데이터를 0과 1로 저장한다.
이진데이터는 0과 1만을 사용하여 2개의 수를 나타내는 진법을 뜻하고, 컴퓨터를 다루는데 있어 가장 근본이 되는 체계라고 볼 수 있다.</p>
<h1 id="base64">Base64</h1>
<p><strong>Base64</strong>는 이진 데이터를 텍스트 형식으로 변환하는 인코딩 방식이다. html에 img의 <code>src</code>에 삽입된 이미지 url이 아닌 숫자와 문자로 구성된 긴 코드(<code>data:image/png;base64</code>)가 base64이다.</p>
<p>우리가 소스 코드 단에 이미지를 불러와 다루어야 할때 링크를 통해 불러오거나 로컬 폴더에 저장되어 있는 이미지 파일을 코드에서 제공하는 파일 시스템을 통해 상대경로로 불러와 다뤄본 경험이 있을 것이다. 하지만 파일도 결국 url과 같이 OS에 저장된 <strong>징검다리(연결선)</strong>를 불러온 것과 같다.</p>
<p>그러나 base64로 변환해주면 우리가 직접 소스 코드 단에 이미지 <strong>데이터 자체</strong>를 저장할 수 있게 된다. 우리가 변수에 문자열이나 숫자를 저장하는 것처럼 변수에 이미지를 저장할 수 있는 것이다.</p>
<p>base64는 이미지 데이터 값을 변환하는 것이 아닌 그냥 출력 형식을 변환하는 것이기 때문에 이미지 자체가 바뀌는 것이 아니다. 즉, <strong>이미지 데이터 정보는 이미 base64 텍스트 자체에 포함</strong>되어 있기 때문에 결과적으로 링크 url을 통해 서버에 요청하지 않고도 직접 이미지를 사용할 수 있게 된다.</p>
<p>이렇게 base64로 변환하면 직접 바이너리를 다룬다는 특징도 있지만, 바이너리 데이터를 텍스트로 바꾸는 64진법 인코딩을 통해, <strong>바이너리 데이터 대비 33%의 데이터의 양 증가</strong>하게 된다는 단점이 있다. 하지만 데이터의 길이가 증가함에도, Base64를 사용하는 가장 큰 이유는 앞서 소개하듯이 Binary 데이터를 텍스트 기반 규격으로 다룰 수 있기 때문이다.</p>
<p>또한 순수 바이너리 형식으로 남아있는보다 전송/저장이 훨씬 쉽다는 점도 꼽을 수 있다. (이진 데이터는 0과 1로 이루어져있고 제어 문자나 특수 문자가 포함될 수 있어서 손상될 확률이 높다)</p>
<ul>
<li><p>장점</p>
<ul>
<li>별도 이미지 파일이 필요없다. 왜냐하면 base64 데이터 자체가 이미지이기 때문.</li>
<li>브라우저 렌더링시, 문서로딩과 같이 로딩 되기에 끊기지 않고 불러온다. 또한 네트워크가 좋지않아도 위와 같은 특징으로 이미지를 로딩할수 있다는 점도 있다.</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>문자열이 매우매우 길기에 가독성이 떨어진다.</li>
<li>Base64 인코딩을 사용하면 원본보다 33%의 용량이 커져서, 남용할경우 오히려 로딩 속도가 저하될 수 있다.</li>
</ul>
</li>
</ul>
<h1 id="blob-binary-large-object">Blob (Binary Large Object)</h1>
<p><strong>BLOB</strong>은 Binary Large OBject의 약자로 주로 이미지, 오디오, 비디오와 같은 멀티미디어 파일 바이너리를 객체 형태로 저장한 것을 의미한다. 멀티미디어 파일들은 대다수 용량이 큰 경우가 많기 때문에, 이를 <strong>데이터베이스에 효과적으로 저장하기 위해 고안된 자료형</strong>이라 볼 수 있다. (string 타입, number 타입이 있듯이 blob 타입이 있다고 이해하면 된다.)</p>
<p>예를들어 데이터베이스에 이미지 파일을 그대로 데이터로 저장하고 싶을때, 바로 blob 포맷으로 변환한 뒤 저장하는 것이다. 브라우저 환경에서도 자바스크립트를 이용해 이러한 blob 데이터에 접근하고 사용할 수 있다. </p>
<p>주로 자바스크립트에서 텍스트, 이미지, 사운드, 비디오와 같은 멀티미디어 데이터를 다룰 때 사용한다.</p>
<p>ex)
<img src="https://velog.velcdn.com/images/taek_jini/post/e59b431b-8be6-4288-be17-00935bb63833/image.png" alt=""></p>
<blockquote>
<p>Blob 객체에는 별도로 type(image/png) 을 명시하기 때문에 Blob 객체를 다운로드/업로드 하는 과정에서 네트워크 요청에서의 Content-Type은 자연스레 명시된 type으로 매칭된다.</p>
</blockquote>
<p>base64와 어떤점이 다른지 비교해보자.</p>
<ul>
<li>base64는 바이너리 데이터를 다루기 위해 <strong>텍스트(문자열)</strong> 형태로 저장한 포맷이라고 했었다.</li>
<li>blob이란 바이너리 데이터를 다루기 위해 <strong>객체(Object)</strong> 저장하는 것이다.</li>
</ul>
<p>blob 데이터는 적절하게 object url로 변환만 해주면 심플하게 브라우저에서 사용할수 있다. 그리고 <strong>blob은 객체이기 때문에 다양한 코드 활용성을 지니고 있어, base64로 변환할수도 있고 뒤에서 배울 buffer로도 변환할수도 있다.</strong></p>
<p>이때 변환된 URL은 현재 탭의 <strong>브라우저 메모리에 저장</strong>되고, 저장된 URL은 매핑된 Blob 객체를 참고하고 있는 형태이다. 이러한 원리 때문에, base64와는 달리 짧은 문자열만으로도 원래의 Blob 객체에 접근이 가능하고 그에 따른 이미지 등의 파일을 가져올 수 있는 것이다. 그래서 변환된 URL은 항상 <strong>현재 문서에서만 유효</strong>하다. (현재 브라우저 메모리에 적재된 상태니까)</p>
<p>변환된 URL을 현재 문서를 새로고침하거나 아니면 다른 페이지에서 사용하려고 한다면 제대로 사용할 수 없다.</p>
<blockquote>
<p>Blob 객체가 URL로 변환되어 매핑이 이루어진 채 메모리에 저장되게 되면, 명시적으로 해당 URL이 해제되기 전까지 브라우저는 해당 URL이 유효하다고 판단하기 때문에 자바스크립트 엔진에서 가비지 컬렉션이 이루어지지 않는다.</p>
</blockquote>
<p><strong>따라서 blob URL을 사용한 이후, 더 이상 사용하지 않을 시점이라고 판단되면 명시적으로 해제해 주는 것이 좋다.</strong></p>
<p>변환은 <code>URL.createObjectURL</code> 메서드를 통해 진행하고, 해제의 경우에는 <code>URL.revokeObjectURL</code> 메서드를 사용한다.</p>
<p>이는 내부적으로 매핑되어 있는 참조를 지우는 메서드로, 메모리에 상주하고 있는 Blob 객체를 지울 수 있다.</p>
<p>예를 들어 이미지를 화면에 출력이 아닌 오로지 이미지 다운으로 <strong>blob</strong>을 사용한다고 하면, 동적으로 생성한 <strong>Blob</strong> 객체는 오직 다운로드 클릭 순간에만 필요하고 그 이후엔 필요하지 않기 때문에 해제를 통해 <strong>메모리 누수</strong>를 방지할 수 있다.</p>
<h1 id="arraybuffer">ArrayBuffer</h1>
<p><strong>ArrayBuffer</strong> 객체는 이미지, 동영상과 같은 멀티미디어 데이터 덩어리를 표준 자바스크립트(브라우저)에서 다루기 위해 도입됐다.</p>
<p>일반적으로 실시간 방송과 같이 영상 내용을 송출할 때에는 영상이라고 하는 <strong>실시간 데이터</strong>를 계속해서 전달해줘야 유저들이 볼 수 있다. 즉, 어떤 식으로든 커다란 데이터를 잘개 쪼개서 전송해야 하는 상황이 발생한다.</p>
<p>여기서 버퍼(buffer)라는 개념이 등장하는데, 일정 구획만큼의 데이터를 쪼개서 전달하는 stream을 저장한 후 일정 크기가 도달하면 출력 장치나 동영상 플레이어로 전달해주는 <strong>중개자 역할</strong>을 하는 객체라고 보면 된다. (버퍼링이 걸리는 이유)</p>
<p>따라서 자바스크립트 용도가 다양해지면서 이처럼 오디오나 비디오 같은 <strong>이진 데이터</strong>들 역시 다룰 필요성이 생기게 되자, 필요한 메모리 공간을 적절하게 할당해서 사용할 수 있는 유연성이 필요해 <strong>ArrayBuffer</strong>를 만들게 되었다고 이해하면 된다.</p>
<p>ArrayBuffer는 자바스크립트에서 원시 데이터를 직접 다루는 수단으로 사용되며, 이는 <strong>메모리를 개발자가 수동으로 관리</strong>할 수 있게 해준다.</p>
<p>지금까지 배운 base64와 blob은 사람이 읽고 다루기 편하게 가공된 데이터 타입이라면, ArrayBuffer는 보다 오리지널에 가깝다고 보면 된다. 레퍼런스 타입으로 되어 있으며, 고정된 길이의 연속된 메모리 공간을 할당해 사용하겠다고 알려주는 역할을 한다.
(오디오 전용인 AudioBuffer, 미디어 전용인 SourceBuffer도 있다.)</p>
<p><strong>특히 성능에 민감한 이슈를 다룬다거나 blob 등의 큰 용량의 파일 데이터를 다루는 경우 ArrayBuffer를 사용해 유연하고 효율적으로 작업할 수 있다.</strong></p>
<blockquote>
<p>이름에 array가 있다고 하여 ArrayBuffer가 자바스크립트 배열과 같진 않다.
ArrayBuffer는 그저 메모리의 연속된 공간을 차지하고 있는 추상적인 메모리 계층과도 같다.
ArrayBuffer는 자바스크립트의 배열과 비교해 다음과 같은 차이점이 있다.</p>
</blockquote>
<ul>
<li>고정길이를 가지고 있고, 이를 늘이거나 줄일 수 없다.</li>
<li>정확히 명시된 크기만큼의 공간을 메모리에서 차지한다.</li>
<li>각각의 바이트에 접근하기 위해서는, 일반 배열처럼 buffer[index]와 같이 인덱스를 통해 접근할 수 없다. 대신에 view라고 불리는 별도의 객체를 생성해서 접근해야 한다.</li>
</ul>
<h1 id="buffer">Buffer</h1>
<p>데이터 덩어리를 다루기 위해 Node.js는 진작부터 <strong>Buffer</strong> 타입을 도입했다</p>
<p>버퍼는 <strong>고정된 크기의 데이터 덩어리를 표현</strong>하기 위한 타입이며, Node.js 의 많은 API들이 Buffer 타입을 지원한다.</p>
<p><code>fs</code> 모듈로 로컬 파일을 가져오는 것도 사실 버퍼로 가져오는 것이다.</p>
<p>클라이언트단에서 ArrayBuffer로 이진 데이터를 다루었다면, 서버단(Node.js)에서는 Buffer 객체로 이진 데이터를 다룬다고 이해하면 된다.</p>
<blockquote>
<p>Node.js에서 멀티미디어 파일을 다룬다고 하면 무조건 Buffer를 이용해 다룬다고 이해하면 된다.</p>
</blockquote>
<h1 id="file-filereader">File, FileReader</h1>
<h2 id="file">File</h2>
<p>파일은 우리에게 매우 익숙한 타입이지만, 자바스크립트에서의 File 객체는 Blob 객체를 확장한 객체로 주로 파일시스템과 관련된 기능을 담당한다.</p>
<p>파일시스템은 OS/서버단의 영역인데, 브라우저 상에서도 파일을 주고 받는 등의 기능이 필요하기 때문에 이를 지원하기 위한 규격으로 볼 수 있다.</p>
<p>브라우저에서 자바스크립트를 이용해 파일을 다루기 위한 방법으로는 File 객체를 이용하거나 html의 input태그를 이용하는 두 가지가 있다.</p>
<h2 id="filereader">FileReader</h2>
<p>FileReader는 Blob 또는 File과 같은 객체로부터 데이터를 읽어 들이기위한 목적으로 사용되는 객체이다.</p>
<p>읽어들인 데이터는 주로 이벤트를 사용하여 필요한 타이밍에 데이터를 전달한다. </p>
<blockquote>
<p>출처 : <a href="https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-Base64-Blob-ArrayBuffer-File-%EB%8B%A4%EB%A3%A8%EA%B8%B0-%EC%A0%95%EB%A7%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%84%A4%EB%AA%85">Inpa Dev, Base64-Blob-ArrayBuffer-File-다루기-정말-이해하기-쉽게-설명</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 성능 최적화 회고]]></title>
            <link>https://velog.io/@taek_jini/%EC%9B%B9-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@taek_jini/%EC%9B%B9-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 06 Aug 2024 01:52:41 GMT</pubDate>
            <description><![CDATA[<h1 id="web-vitals-란">Web Vitals 란?</h1>
<p>웹 바이탈은 웹에서 우수한 사용자 환경을 제공하는 데 필수적인 웹페이지 품질 신호에 관한 통합 가이드를 제공하기 위한 Google 이니셔티브입니다. </p>
<p>이 지표는 사용 가능한 다양한 성능 측정 도구를 단순화하고 사이트 소유자가 가장 중요한 측정 항목인 <strong>코어 웹 바이탈</strong>에 집중할 수 있도록 돕는 것을 목표로 합니다. </p>
<blockquote>
<p>참고 : <a href="https://web.dev/vitals/">https://web.dev/vitals/</a></p>
</blockquote>
<ul>
<li>BEFORE</li>
</ul>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/b2f11010-e2a4-42b2-a70b-74114876c4bd/image.png" alt=""></p>
<ul>
<li>AFTER</li>
</ul>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/ba6dad58-7bf0-4a9a-9b71-8207ef861abe/image.png" alt=""></p>
<p>성능 지표 중 주요 항목으로는 6가지가 있습니다. </p>
<ul>
<li>FCP (First Contentful Paint) : 첫번째 텍스트 또는 이미지가 표시되는 시간</li>
<li>TTI (Time to Interactive) : 사용자 인터렉션이 가능해질 때까지 걸리는 시간</li>
<li>SI (Speed Index) : 웹 페이지 로드 중에 콘텐츠가 시각적으로 표시되는 속도에 대한 지표</li>
<li>TBT (Total Blocking Time) : FCP ~ TTI 사이에서 메인 스레드를 블로킹하는 작업 시간</li>
<li>LCP (Largest Contentful Paint) : 가장 큰 텍스트 또는 이미지가 표시되는 시간</li>
<li>CLS (Comulative Layout Shift) : 누적 레이아웃 이동. 뷰포트 안에 보이는 요소의 이동을 측정. 사용자가 예상치 못한 레이아웃 이동을 경험하는 빈도를 수량화해서 보여준다.</li>
</ul>
<h3 id="전체적인-lighthouse-측정-값-비교-before-→-after">전체적인 LightHouse 측정 값 비교 (BEFORE → AFTER)</h3>
<ul>
<li>FCP : 1.2sec -&gt; 0.3sec</li>
<li>TBT : 400ms -&gt; 140ms</li>
<li>SI : 1.8sec -&gt; 1.3sec</li>
<li>CLS : 0.007 -&gt; 0</li>
<li>Main Bundle : 4.9Mb -&gt; 1.3Mb</li>
<li>사용하지 않는 자바스크립트 줄이기 786Kb 절감</li>
</ul>
<h2 id="이미지-최적화">이미지 최적화</h2>
<hr>
<p>이미지 최적화는 개발비용 대비 성능 효율이 가장 좋습니다.</p>
<p>리소스 중 용량이 큰 편에 속하기 때문에 로드 속도가 오래 걸리고, 컨텐츠 중 <strong>가장 큰 영역을 차지</strong>하는 경우 LCP 성능에 큰 영향 을 주기 때문입니다.</p>
<p>이미지 최적화 방법으론 lazyLoading, 스프라이트 이미지, 이미지 압축 포맷 설정, 이미지 태그 설정 등이 있습니다.</p>
<blockquote>
<p>💡 현재 미니언즈 페이지는 이미지가 많지 않기 때문에 <strong>이미지 태그 설정</strong>만 수정했습니다.</p>
</blockquote>
<h3 id="이미지-태그-설정"><strong>이미지 태그 설정</strong></h3>
<ul>
<li>CSS background-image → img 태그로 변경<ul>
<li>img 태그를 사용하면 브라우저 프리로드 스캐너에 의해 빠르게 요청된다.</li>
<li>프리로드 스캐너는 마크업을 스캔하고 CSS는 스캔하지 않음.</li>
</ul>
</li>
</ul>
<blockquote>
<p>프리로드 스캐너란?</p>
</blockquote>
<ul>
<li>모든 브라우저는 파일을 토큰화 하고 객체 모델로 처리하는 HTML 파서를 기본적으로 가지고 있습니다.
작업은 블로킹 리소스 <strong>(ex.async, defer, type=&quot;module&quot; 같은 옵션이 없는 script 태
그)</strong> 를 만나기 전 까지 계속 됩니다. </li>
<li>css파일 같은 경우는 스타일링이 적용되지 않은 콘텐츠가 잠깐 뜨는 현상을 방지하기 위해 파싱과 렌더링이 차단됩니다. 그리고 script 태그를 만나게 되어도 렌더링 작업이 중단됩니다.</li>
<li>하지만 이러한 작업은 다른 중요한 리소서를 찾는 과정을 지연시킴으로써 퍼포먼스를 저하 시킬 수 있습니다. preload scanner를 사용해서 필요한 요청을 병렬적으로 처리합니다. 
예를들면 HTML 문서에 img 또는 link 와 같은 태그가 있으면 프리로드 스캐너는 HTML 파서가 생성한 토큰을 확인하고 브라우저 프로세스의 네트워크 스레드에 요청을 보냅니다.</li>
</ul>
<h2 id="웹-폰트-최적화">웹 폰트 최적화</h2>
<hr>
<p>웹 폰트는 사용자 로컬 기기에 저장되어 있는 폰트 파일을 사용하는 것이 아닌, 온라인상에서 폰트 파일을 다운로드해서 사용하게 됩니다. </p>
<p>즉, 웹 폰트를 사용하는 페이지에 접근할 때 웹 서버 이외에 또 다른 서버로 웹 폰트를 요청하는 것입니다.</p>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/c6a169e0-36ba-442a-b1da-6a2f6bd7d69f/image.png" alt=""></p>
<blockquote>
<p>출처 : <a href="https://web.dev/articles/optimize-webfont-loading?hl=ko">https://web.dev/articles/optimize-webfont-loading?hl=ko</a></p>
</blockquote>
<ol>
<li><p>브라우저가 HTML을 요청하고, 응답받은 HTML로 DOM 구성</p>
</li>
<li><p>브라우저가 필요한 CSS를 확인하여 요청하고, 응답받은 CSS로 CSSOM 구성</p>
</li>
<li><p>브라우저는 렌더링에 필요한 폰트 요청</p>
</li>
<li><p>브라우저는 폰트 요청의 응답을 기다리지 않고 렌더링 진행</p>
<p> (폰트가 준비되지 않았다면 대체 폰트 렌더링하거나 렌더링하지 않음)</p>
</li>
<li><p>브라우저는 폰트가 준비되면 비어있는 텍스트를 채우거나 대체 폰트를 기존 폰트로 다시 렌더링</p>
</li>
</ol>
<h3 id="웹-폰트-최적화가-필요한-이유">웹 폰트 최적화가 필요한 이유</h3>
<ul>
<li>웹 폰트를 사용할 때 FOIT와 FOUT 현상이 발생할 수 있는데, 이 현상들은 UX를 저하시키고, 개발자가 의도 하지 않은 동작입니다. 이를 최소화하기 위해 최적화가 필요합니다.<ul>
<li>FOIT (Flash Of Invisible Text)<ul>
<li><code>FOIT</code>는 웹 페이지가 렌더링 되었을 때, 필요한 폰트가 아직 준비되지 않아 사용자에게 일시적으로 글자가 보이지 않는 현상을 말합니다.</li>
</ul>
</li>
<li>FOUT(Flash Of Unstyled Text)<ul>
<li>필요한 폰트가 준비되지 않아 글자 자체가 보이지 않는 FOIT과 달리, FOUT는 글자가 보이지만 기본 시스템 폰트로 표시되는 현상을 말합니다.</li>
<li>아예 글자가 보이지 않는 <code>FOIT</code>의 문제점을 해결하고자, 글자라도 보이게 하는 대안적인 현상입니다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="웹-폰트-최적화-방법">웹 폰트 최적화 방법</h3>
<p>폰트 파일의 형식은 형식마다 용량이 다르고, 브라우저 호환 유/무도 다릅니다.</p>
<ul>
<li>아래 표는 미니언즈에서 사용하고 있는 웹 폰트 NotoSansKR 폰트의 확장자 별 용량입니다.</li>
</ul>
<table>
<thead>
<tr>
<th>Name</th>
<th>Tag</th>
<th>Size</th>
</tr>
</thead>
<tbody><tr>
<td>NotoSansKR-Black.woff2</td>
<td>woff2</td>
<td>1,032KB</td>
</tr>
<tr>
<td>NotoSansKR-Black.woff</td>
<td>woff</td>
<td>1,360KB</td>
</tr>
<tr>
<td>NotoSansKR-Black.otf</td>
<td>otf</td>
<td>2,528KB</td>
</tr>
</tbody></table>
<p>WOFF 형식과 WOFF2형식은 압축된 폰트 형식입니다. 따라서 용량이 현저히 작은 것을 알 수 있습니다.</p>
<ul>
<li>웹폰트 형식 별 브라우저 호환 표</li>
</ul>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/d815e59a-7aee-41d6-8de2-a320d68a5775/image.png" alt=""></p>
<blockquote>
<p>출처 : <a href="https://www.w3schools.com/Css/css3_fonts.asp">https://www.w3schools.com/Css/css3_fonts.asp</a></p>
</blockquote>
<p><code>WOFF</code>와 <code>WOFF2</code>는 모든 브라우저에서 사용할 수 할 수 있지만, 브라우저의 레거시 버전까지 대응하진 못합니다. 브라우저의 버전에 따라 대응하고 싶은 버전까지 대응하면 됩니다.</p>
<p>현재 관리하는 페이지는 브라우저의 하위 버전을 모두 호완할 수 있게 <code>otf</code> 확장자까지 대비하고 있습니다.</p>
<ul>
<li>최적화 font-face 예시</li>
</ul>
<pre><code class="language-scss">@font-face {
  font-family: &quot;Noto Sans KR&quot;;
  src: local(&quot;Noto Sans KR&quot;), url(&quot;./NotoSansKR-Black.woff2&quot;) format(&quot;woff2&quot;),
    url(&quot;./NotoSansKR-Black.woff&quot;) format(&quot;woff&quot;), url(&quot;./NotoSansKR-Black.otf&quot;) format(&quot;opentype&quot;);
}</code></pre>
<ul>
<li><p>폰트 다운로드 순서 - <code>woff2</code> &gt; <code>woff</code> &gt; <code>otf</code></p>
<ul>
<li><p>사용자의 local 기기에 폰트가 설치되어 있다면 리소스를 요청하지 않습니다.</p>
</li>
<li><p>브라우저와 호환되는 리소스를 다운받을 시 다음 순서의 리소스는 다운 받지 않습니다.</p>
</li>
<li><p>font-style, font-weight 최적화</p>
</li>
</ul>
</li>
</ul>
<pre><code class="language-scss">@font-face {
  font-family: &quot;Noto Sans KR&quot;;
  font-style: normal;
  font-weight: 500;
  src: local(&quot;Noto Sans KR&quot;), url(&quot;./NotoSansKR-Medium.woff2&quot;) format(&quot;woff2&quot;),
    url(&quot;./NotoSansKR-Medium.woff&quot;) format(&quot;woff&quot;),
    url(&quot;./NotoSansKR-Medium.otf&quot;) format(&quot;opentype&quot;);
}

@font-face {
  font-family: &quot;Noto Sans KR&quot;;
  font-style: normal;
  font-weight: 700;
  src: local(&quot;Noto Sans KR&quot;), url(&quot;./NotoSansKR-Bold.woff2&quot;) format(&quot;woff2&quot;),
    url(&quot;./NotoSansKR-Bold.woff&quot;) format(&quot;woff&quot;), url(&quot;./NotoSansKR-Bold.otf&quot;) format(&quot;opentype&quot;);
}</code></pre>
<p>프로젝트에서 사용하는 굵기와 스타일의 폰트만 다운받아 용량을 최적화합니다.</p>
<h3 id="코드-스플리팅-lazysuspense">코드 스플리팅 (lazy+suspense)</h3>
<blockquote>
<p>💡 웹 애플리케이션의 JavaScript 코드를 여러 부분으로 나누는 기술입니다. 이를 통해 사용자가 웹 페이지를 방문할 때 한 번에 모든 JavaScript 코드를 다운로드하는 대신 필요한 코드 조각만 로드할 수 있습니다. 이로 인해 초기 로딩 시간이 단축되고 사용자 경험이 향상됩니다.</p>
</blockquote>
<h3 id="reactlazy">React.lazy</h3>
<p>컴포넌트를 렌더링할 때 비동기적으로 로딩하게 해주는 함수</p>
<h3 id="suspense">Suspense</h3>
<p>사용자의 네트워크 상태가 안좋거나 파일이 큰 경우, 오랫동안 빈화면 혹은 워터폴 현상으로 인해 사용자의 경험을 해칠 수 있습니다. 이러한 것을 방지하기 위해 <code>Suspense</code>를 사용합니다.</p>
<p><code>Suspense</code>는  <code>Loading UI</code>를 선언적으로 작성할 수 있게 해주는 컴포넌트로, 하위에 선언된 컴포넌트들의 비동기적 활동이 완료되어 렌더링할 수 있는 상태가 될 때까지 렌더링을 멈추고 <code>fallback props</code>로 넘겨진 컴포넌트를 대신하여 보여줍니다.</p>
<h2 id="메인-번들-크기-비교">메인 번들 크기 비교</h2>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/ad51a0ed-a0c6-4f17-ad6d-3c06cff7eb0e/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[react-router v5 -> v6 업그레이드 후기]]></title>
            <link>https://velog.io/@taek_jini/react-router-v5-v6-%EC%97%85%EA%B7%B8%EB%A0%88%EC%9D%B4%EB%93%9C-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@taek_jini/react-router-v5-v6-%EC%97%85%EA%B7%B8%EB%A0%88%EC%9D%B4%EB%93%9C-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Thu, 18 Apr 2024 07:18:06 GMT</pubDate>
            <description><![CDATA[<h1 id="routing이란"><strong>Routing이란?</strong></h1>
<p><strong>&quot;라우팅(routing)&quot;</strong>이란 <strong>사용자가 요청한 URL 경로에 따라 그에 맞는 UI를 보여주는 것</strong>을 라우팅이라고 합니다. React App의 경우 새로운 페이지를 요청하는 것이 아니라 <strong>URL에 맞는 페이지 컴포넌트를 렌더링</strong>해주어야 합니다.</p>
<p>React는 SPA(Single Page Application)로서 하나의 페이지(HTML)을 갖는 애플리케이션입니다. <strong>즉, SPA는 하나의 HTML 파일, 하나의 URL을 가지며 이는 절대 바뀌지 않습니다.</strong></p>
<p>이처럼 변하지 않는 URL이 이상적이지 않습니다.</p>
<p>예를 들어, URL을 다른 사람에게 공유를 하거나 북마크를 하더라도 해당 URL로 접속을 한다면 언제나 시작 페이지를 보여주게 될 것입니다.</p>
<p>우리는 기존처럼 하나의 페이지(SPA), 즉 하나의 HTML 페이지만을 사용하면서 URL에 따라서 화면에 보이는 UI를 바뀌도록 해야하며, URL 경로가 변경되더라도 <strong>새로운 HTML 파일을 서버로부터 요청하지도 않으면서 변경된 URL 경로에 따라 그에 맞는 화면(UI)을 사용자에게 보여주어야 합니다.</strong> 이를 구현하기 위해서 <strong>&quot;react-router-dom&quot;</strong> 패키지를 사용해야 합니다.</p>
<h2 id="업그레이드-해야-하는-이유">업그레이드 해야 하는 이유</h2>
<ul>
<li>react-router 버전 업그레이드는 기존 버전의 성능 향상 목적 뿐만 아니라 새로운 시도라고 볼 수 있을 정도로 많은 변화가 있었습니다.</li>
<li>react-router를 사용하며 앞으로의 버전 업그레이드와 기술 동향을 따라 가기 위해 업그레이드를 진행합니다.</li>
</ul>
<h2 id="업데이트-내용">업데이트 내용</h2>
<h3 id="번들-사이즈-최적화">번들 사이즈 최적화</h3>
<hr>
<p><strong>react-router v6</strong> 는 아래 이미지와 같이 이전 버전 대비 약 70% 정도의 크기가 줄어들었습니다. (<a href="http://bundlephobia.com/">bundlephobia.com</a>)</p>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/d312e7a9-6347-42f4-8493-f939ce0a40f4/image.png" alt=""></p>
<h3 id="switch-→-routes-네이밍-변경">switch → routes 네이밍 변경</h3>
<hr>
<p>기존의 route들을 구성하는 부모 요소를 <code>switch</code> 라는 네이밍으로 사용하였는데 <strong>route의 복수개를 뜻하는</strong> <code>routes</code> <strong>로 이름이 변경</strong>되었습니다. 바뀐 네이밍이 개인적으로는 더욱 직관적이라 마음에 드는 부분입니다.</p>
<pre><code class="language-jsx">// v5
&lt;Switch&gt;
  &lt;Route ... /&gt;
&lt;/Routes&gt;

// v6
&lt;Routes&gt;
  &lt;Route ... /&gt;
&lt;/Routes&gt;</code></pre>
<h3 id="exact-옵션-삭제">exact 옵션 삭제</h3>
<hr>
<p>기존의 <code>/</code> 라우트의 경우 <strong>React Router의 디폴트 매칭 규칙으로 인해 앞 부분만 일치해도 전부 매칭</strong>되기 때문에 정확히 라우트를 일치시키고자 <code>exact</code> 속성을 사용하였으나,</p>
<p> v6부터 기본적으로 정확히 일치하도록 매칭 규칙이 변하여 <strong>v6에서부터는 <code>exact</code> 의 옵션을 더이상 사용하지 않습니다.</strong> 만약 하위경로에 여러 라우팅을 매칭시키고 싶다면 다음과 같이 <strong>URL 뒤에 <code>*</code> 을 사용하여 일치</strong>시킬 수 있습니다.</p>
<h3 id="route에서-컴포넌트-렌더링">route에서 컴포넌트 렌더링</h3>
<hr>
<p>기존 버전에서는 컴포넌트를 렌더링 하기 위해선 <strong>Route 컴포넌트의 <code>render</code> 속성에 arrowFunction 을 사용하여 컴포넌트를 렌더링 하는 방식으로 사용하였습니다.</strong> 물론 기본적으로는 <code>component</code> 속성에 넣어주기도 하지만 컴포넌트를 렌더링 해야 하는 경우는 <code>render</code> 속성을 사용해야 하는데 이때 arrowFunction 으로 선언되는 부분을 <strong>v6에서는 <code>element</code> 속성을 통해 바로 넣어줄 수 있도록 개선되었습니다.</strong> 아래 예제를 보면 v5 대비 훨씬 직관적인 코드로 작성할 수 있도록 개선된 것을 확인하실 수 있습니다. </p>
<pre><code class="language-jsx">// v5
import TestPage from &#39;./TestPage&#39;;

&lt;Route path=&#39;test&#39; component={TestPage} /&gt;
&lt;Route
  path=&#39;test&#39;
  render={props =&gt; (
    &lt;TestPage routeProps={props} isLogin={true} /&gt;
  )}
/&gt;

// v6
&lt;Route path=&#39;test&#39; element={&lt;TestPage /&gt;} /&gt;
&lt;Route path=&#39;test&#39; element={&lt;TestPage isLogin={true} /&gt;} /&gt;</code></pre>
<h3 id="중첩-라우팅">중첩 라우팅</h3>
<hr>
<p>v6에서는 <code>&lt;Route&gt;</code> path 지정시에 상대경로를 지정해줄 수 있게 되면서 중첩 라우팅 구현이 더 간편해졌습니다.</p>
<p>추가적으로 <code>&lt;Outlet&gt;</code> 컴포넌트를 활용하여 상위 컴포넌트 하나의 <code>&lt;Routes&gt;</code>에서 중첩 라우팅을 구현해 줄 수 있습니다. 이는 <code>route</code> 코드를 한데 모아서 구성하기에 간편하고 페이지별 구성할 <code>componenets</code>를 분리하기에 유용합니다.</p>
<blockquote>
<p>💡 현재 프로젝트에서는 기존 v5의 절대 경로 형식을 유지하기로 했습니다.</p>
</blockquote>
<h2 id="withrouter-hoc-삭제">withRouter HOC 삭제</h2>
<hr>
<p>기존 버전에서 <code>match</code>, <code>location</code>, <code>history</code>를 사용할 때 쓰인 <strong>HOC</strong>가 삭제되고 <strong>hooks</strong>로 대체되었습니다.</p>
<p>공식 문서에서도 삭제된 <code>withRouter</code> <strong>HOC</strong>를 대체하여 사용할 코드소스를 제공하였고, 우린 이를 응용하여 대체 <strong>HOC</strong>를 제작하여 사용하기로 하였습니다.</p>
<p>… 이외 데이터라우팅 기능(errorElement, createBrowserRouter)</p>
<h2 id="아쉬운-점">아쉬운 점</h2>
<ul>
<li>추후 상황을 고려하지 않은, 당장의 얕은 생각만으로 업그레이드를 진행하여 비효율적인 개발 비용을 사용했습니다.<ul>
<li>ex) withRouter HOC 제작 시 기존 v5 코드를 그대로 사용할 수 있게 만들었다면 라우팅 기능을 사용하는 모든컴포넌트를 수정하지 않았을 것 같습니다.</li>
</ul>
</li>
<li>react-router 업데이트와 관련된 공식문서를 꼼꼼히 읽지 않고 시작하여 삽질 시간이 증가했습니다.<ul>
<li>공식문서를 정독했다면 수정된 사항에 대해 더 깊은 이해도를 가지고 지금보다 더 유연한 업그레이드를 진행했을 것 같습니다.</li>
</ul>
</li>
<li>현재 v6버전에서 path의 상대 경로와 절대 경로를 둘 다 사용할 수 있도록 조치 했다고 하지만 하위 구성요소에서만 사용 가능하고 중첩 요소에서는 적용되지 않았습니다.<ul>
<li>해당 이슈 솔루션(<a href="https://github.com/remix-run/react-router/issues/8035)%EC%9D%84">https://github.com/remix-run/react-router/issues/8035)을</a> 도움받아 공통 컴포넌트 RootRoutes를 사용하여 절대 경로를 사용했습니다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[관심사의 분리 & Custom Hooks]]></title>
            <link>https://velog.io/@taek_jini/%EA%B4%80%EC%8B%AC%EC%82%AC%EC%9D%98-%EB%B6%84%EB%A6%AC-Custom-Hooks</link>
            <guid>https://velog.io/@taek_jini/%EA%B4%80%EC%8B%AC%EC%82%AC%EC%9D%98-%EB%B6%84%EB%A6%AC-Custom-Hooks</guid>
            <pubDate>Tue, 19 Sep 2023 11:39:58 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/taek_jini/post/d33bde7e-d297-43fc-bf31-13fd5b87a7de/image.png" alt=""></p>
<p>이번 포스팅에선 클린코드, 관심사의 분리, SRP, Custom Hooks에 대해 얘기해보려고 한다.</p>
<h1 id="클린-코드">클린 코드</h1>
<p><strong>클린코드</strong>란 여러 관점을 두고 말을 할 수 있지만 통상 <strong>가독성이 높고 유지보수가 용이한 코드</strong>를 의미한다. </p>
<blockquote>
<p>그 밖에도 일관성, 모듈화, 단순성 등등 기준이 될 수 있지만 적어도 내가 생각하는 클린 코드는 <strong><code>변화에 유연하게 대응할 수 있고 수정과 확장에 용이한 코드</code></strong> 라고 스스로 정의했다 !</p>
</blockquote>
<p>근데 ! 신입 감자인 내가 ! 흠 없는 코드를 작성할 수 있을까 ?
<img src="https://velog.velcdn.com/images/taek_jini/post/eea8e749-b0f9-4417-a2b4-86de9640de15/image.png" alt=""></p>
<p>놉 ! 그건 있을 수 없다. (울 엄마도 콧방귀 낄 소리임) </p>
<p>물론 <strong>좋은 코드</strong>, <strong>깨끗한 코드</strong>를 작성하기 위해 많이 <strong>고민</strong>하고 <strong>시도</strong>해봐야 한다 ! 
대신 완벽할 수 없다면 <strong>마이너스</strong>는 되면 안된다는 것 ! 
즉 , <strong>나쁜 코드는 적지 말자는 게 목표</strong>다.</p>
<p>물론 여기서 말하는 나쁜 코드는 &quot;나중에 봤을 때 나쁜 코드&quot; 를 의미하는 건 아니다. 당시엔 React가 없었을 수도, 10년 뒤엔 또 새로운 언어가 자리 잡아 React를 쓰지 않을 수도 있으니,</p>
<p>그 당시에는 최선이었던 방법이 시간이 흐름에 따라 현재는 적합한 방법이 아닐 수 있다는 것이다. 그때 최선이였던 코드는 비교적 마이그레이션하거나, 개편하기가 쉽다. 하지만, 애초에 <strong>나쁜 코드는 이러한 작업들을 불가능에 가깝게 만든다.</strong></p>
<p>따라서, 개발자는 반드시 <strong>좋고 깨끗한 코드를 작성하기 위해서 노력</strong>해야 한다. 좋은 코드를 작성할 수 있는 능력은 개발자의 실력을 평가할 수 있는 요소 중 하나이기도 하다. 여태껏 개발자들은 좋은 코드를 작성하기 위한 방법과 원칙들을 공유해왔다. 이를 좀 더 알아보도록 하자!</p>
<h1 id="관심사-분리">관심사 분리</h1>
<p>개발에는 <strong>관심사의 분리(Seperation of Concerns)</strong> 이라는 용어가 있다.</p>
<p>이는 좋은 코드를 짜기 위한 가장 기본적인 원칙이며, 더 좋은 애플리케이션을 만들기 위한 여러 디자인 패턴, 기법, 아키텍쳐 등은 결국 모두 이 SoC를 가장 기본적인 원칙으로 삼고 있다.</p>
<p><strong>“관심사&quot;</strong>를 간단히 말하면 <strong>하나의 모듈이 수행하고자 하는 목적</strong>이다, 여기서 모듈이란 함수, 클래스 등의 단위로 해석할 수 있다.</p>
<p>따라서, <strong>관심사의 분리란 각 모듈들이 한번에 여러 관심사를 처리하려고 하지 않고, 하나의 관심사만 처리하도록 분리하는 것</strong>을 의미한다.</p>
<h2 id="관심사를-분리하는-이유">관심사를 분리하는 이유</h2>
<p>그렇다면 왜 관심사를 분리해야 할까?
하나의 모듈에서 여러 기능을 할 수 있으면 이득 아닌가.. ? 
왜 하나의 모듈은 하나의 관심사만 처리해야 하나 싶지만 <strong>관심사를 분리하면 하나의 모듈은 하나의 목적만 가지게 된다.</strong> 
하나의 목적만 가지게 된다는 말을 조금 다르게 해석해보면, <strong>이 코드가 수정될 이유는 한가지만 존재하게 된다는 의미</strong>.</p>
<p>소프트웨어에서의 변화는 필연적이며, 좋은 소프트웨어 일수록 기존의 기능을 수정, 확장 하는 것에 유연해야 한다. 우리는 이를 &quot;유지보수&quot;라고 부른다.</p>
<ul>
<li><strong>관심사를 분리하는 이유</strong>는 소프트웨어의 특정 부분이 변경되는 이유를 한가지로 한정하여 수정을 용이하기 위해서</li>
<li>만약 여러 모듈들이 여러 관심사를 동시에 다룬다면 특정분야를 수정할 때 관련된 모든 모듈을 수정해야 할 것</li>
</ul>
<p>예를 들어, 애플리케이션 내에서 인증&amp;인가에 대해서 모든 모듈들이 관여하고 있다면, 추후 인증&amp;인가의 동작을 수정해야 할 경우에는 모든 모듈들을 일일이 돌아다니며 수정을 해야 할 것이다.</p>
<p>하지만, 인증&amp;인가를 다루는 핵심 모듈을 한가지로 제한해두고 나머지는 이 모듈을 사용하는 형식으로 설계되어 있다면 추후 인증&amp;인가의 동작이 변경되었을 경우에는 해당 모듈만 수정하면 되기에 변화에 유연하게 대응할 수 있게 된다.</p>
<p>이처럼 <strong>관심사의 분리는 소프트웨어를 만드는 프로그래밍에서 가장 기본이 되는 원칙</strong>이다. 기본이 되는 원칙이기에 비슷한 개념을 표현하는 <code>단일 책임 원칙(SRP)</code>, <code>KISS</code> 등이 생겨났다.</p>
<blockquote>
<ul>
<li><code>단일 책임 원칙(Single Responsibility Principle)</code>: 관심사의 분리와 유사한 개념이지만, 관심사란 표현 대신 책임이란 용어를 사용한다. 각 모듈들은 책임(수행해야 하는 동작)을 가지고 있으며 각기 하나의 책임만을 가져야 한다는 원칙</li>
</ul>
</blockquote>
<ul>
<li><code>KISS(Keep It Simple, Stupid)</code>: 각 모듈들은 간단하고, 단순하게 만들라는 의미로서 여러 기능을 포함시키면서 복잡하게 만들면 유지보수가 힘들어지기에, 하나의 기능만 수행하도록 하라는 의미. SoC, SRP등의 원칙과 유사한 의미를 가지고 있다.</li>
</ul>
<h1 id="custom-hooks">Custom Hooks</h1>
<h2 id="리액트의-관심사">리액트의 관심사</h2>
<p>리액트가 가진 관심사는 어떤 것들이 있을까? 
<strong>리액트는 UI를 구축하기 위한 라이브러리</strong>이다.
따라서 리액트가 가진 핵심적인 관심사는</p>
<ul>
<li>UI</li>
<li>로직 (UI를 변경시키는 부분)</li>
</ul>
<p>위 두가지로 나눌 수 있다.</p>
<p>이 중 <strong>UI</strong>는 실제 코드상에서는 <strong>JSX</strong>라는 형태로 표현된다. 
그리고 <strong>로직</strong>은 유저의 입력에 반응하고, <strong>API를 호출</strong>하고, 스크린의 변화에 반응하는 등 여러 동작들을 통해서 <strong>UI에 영향을 미치는 행위</strong>라고 할 수 있다.</p>
<h2 id="presentational---container">Presentational - Container</h2>
<p>초창기에 유명해진 기법인 <code>Presentational - Container 패턴</code>이다. 
<code>Presentational - Container</code> 기법은 컴포넌트를 크게 두 계층으로 분리하는 방법이다.</p>
<ul>
<li><p><strong>Container</strong>는 로직들을 다루는 부분으로 UI에는 관여하지 않고 오로지 UI를 구성하고 변화하기 위한 로직에만 집중하는 컴포넌트이다.</p>
</li>
<li><p><strong>Presntational</strong>은 반대로 로직은 상관하지 않고 UI가 어떻게 구성되어야 하는지에만 집중하는 컴포넌트이다.</p>
</li>
</ul>
<p>이렇게 컴포넌트를 두 계층으로 나누어서 Presentational을 Container로 감싼 후, 필요한 정보들과 로직을 모두 props로 전달해주는 형태로 설계하는 방법이 Presentatinal - Container 패턴이다.</p>
<p>이 패턴은 Hook이 등장하기 전까지는 관심사를 분리하는 표준 패턴으로 사용되었지만 Hook이 등장한 후에는 더이상 Presentational - Container 패턴을 많이 사용하지 않는다.</p>
<h4 id="어떤-단점이-있었고-hooks은-어떤-장점이-있어서-바뀐걸까-">어떤 <strong>단점</strong>이 있었고, Hooks은 어떤 장점이 있어서 바뀐걸까 ?</h4>
<p>이 기법으로 프로젝트를 진행하며 느낀 <strong>단점</strong>은 이렇다.</p>
<ul>
<li>불필요한 props가 추가된다.</li>
<li>props를 단번에 파악하기 어렵다.</li>
<li>초기 추상화 작업이 어렵다.</li>
</ul>
<p>그렇다면 Hook은 어떻게 활용할 수 있을지 알아보자 !</p>
<h2 id="custom-hook">Custom Hook</h2>
<p>커스텀 훅은 리액트가 기본적으로 제공해주는 훅들을(useState, useEffect 등) 이용해서 만든 함수이다.</p>
<p><strong>로직</strong>은 <strong>UI를 변경</strong>시키기 위함이고, <strong>함수형 컴포넌트</strong>에서 로직은 대부분 <strong>useState, useEffect 등의 Hook을 통해서 구현</strong>된다.</p>
<p>훅을 통해서 편리하게 state를 선언하고 effect를 발생시킬 수 있게 되었지만, 
컴포넌트 내부에 많은 로직들이 들어가게 되면 컴포넌트가 복잡해지고, 
무엇보다 <strong>동일한 로직들을 여러 컴포넌트에 걸쳐서 재사용하기 힘들다는 단점</strong>이 있었다.</p>
<p>일적반으로 <strong>동일한 로직이 보일경우 함수</strong>로 추출하듯이, 
리액트에서도 <strong>Hook들을 이용한 동일한 로직들을 별도의 함수로 추출</strong>해서 
여러 컴포넌트에 걸쳐서 사용하고자 하는 시도가 있었고 결국 <strong>커스텀 훅</strong>이란 기법을 만들게 되었다.</p>
<blockquote>
<p><strong>커스텀 훅의 조건</strong></p>
</blockquote>
<ul>
<li><strong>React의 Hook들을 호출하는 함수여야 한다.</strong><ul>
<li>말 그대로 <code>Hooks</code>를 커스텀 하는 것이기 때문에 <code>Hooks</code>를 호출해야 한다. </li>
<li>return이 <code>Hooks</code>일 필요는 없다.</li>
</ul>
</li>
<li><strong>함수의 이름은 <code>use</code>로 시작해야 한다.</strong><ul>
<li>React에서 Hook의 동작을 처리하는 내부적인 규칙과도 관련이 되어 있고, 공식적인 컨벤션이기 때문에 Custom Hook을 작성할 때는 꼭 <code>use</code> 로 시작하는 이름을 지어야 한다.</li>
<li>eslint의 설정에서도 use로 시작한다면 useEffect 의존성 배열에 추가하지 않아도 된다.</li>
</ul>
</li>
<li><strong>같은 Hook을 사용하는 두 개의 컴포넌트는 state를 공유하지 않는다.</strong><ul>
<li>두 Custom Hook은 서로 호출되는 위치와 타이밍이 다르며, 애초에 서로 다른 스코프(유효범위)를 생성하기 때문에 컴포넌트를 여러번 호출하는 것처럼 완전히 독립적으로 작동한다.</li>
</ul>
</li>
</ul>
<h3 id="예제">예제</h3>
<ul>
<li><p>Custom Hooks 리팩토링 전</p>
<pre><code class="language-js">export default function Example() {
const [isLightMode, setIsLightMode] = useState(true);

function changeMode() {
  setIsLightMode((prev) =&gt; !prev);
}

return (
  &lt;&gt;
    &lt;h1
      style={{
        backgroundColor: isLightMode ? &quot;white&quot; : &quot;black&quot;,
        color: isLightMode ? &quot;black&quot; : &quot;white&quot;
      }}
    &gt;
      current mode: {isLightMode ? &quot;Light Mode&quot; : &quot;Dark Mode&quot;}
    &lt;/h1&gt;
    &lt;button onClick={changeMode}&gt;change mode&lt;/button&gt;
  &lt;/&gt;
);
}</code></pre>
</li>
<li><p>Custom Hooks 리팩토링 후</p>
<pre><code class="language-js">export default function App() {
const [isLightMode, changeMode] = useToggle(true);

return (
  &lt;&gt;
    &lt;h1
      style={{
        backgroundColor: isLightMode ? &quot;white&quot; : &quot;black&quot;,
        color: isLightMode ? &quot;black&quot; : &quot;white&quot;
      }}
    &gt;
      current mode: {isLightMode ? &quot;Light Mode&quot; : &quot;Dark Mode&quot;}
    &lt;/h1&gt;
    &lt;button onClick={changeMode}&gt;change mode&lt;/button&gt;
  &lt;/&gt;
);
}
</code></pre>
</li>
</ul>
<p>const useToggle = (defaultValue) =&gt; {
  const [toggle, setToggle] = useState(defaultValue);</p>
<p>  const changeToggle = () =&gt; {
    setToggle((prev) =&gt; !prev);
  };</p>
<p>  return [toggle, changeToggle];
};</p>
<p>```</p>
<h3 id="중요하게-생각하는-점">중요하게 생각하는 점</h3>
<ul>
<li><strong>커스텀 훅은 추상적이여야 한다</strong> <ul>
<li>즉, 구체적인 구현 방법을 서로 몰라야 한다. ( 알 필요가 없다. )</li>
<li>코드가 어떻든 <strong>제시된 요구사항을 충족</strong>했다는 것만 알고 있으면 된다.</li>
</ul>
</li>
<li><strong>커스텀 훅을 함수로 만들어서 줄 때는 기본적으로 메모이제이션을 해서 주는게 좋다.</strong><ul>
<li><strong>WHY??</strong> 로직을 어떻게 쓸 지는 아무도 모른다. 
만약 useEffect에 함수를 넣는다면 무한루프가 돌 수도 있다 !</li>
</ul>
</li>
</ul>
<p>참고자료</p>
<ul>
<li>원티드 프리온보딩 인턴십 세션 강의</li>
<li><a href="https://react.dev/learn/reusing-logic-with-custom-hooks">https://react.dev/learn/reusing-logic-with-custom-hooks</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[useEffect ! 너 내 도도도독!!!]]></title>
            <link>https://velog.io/@taek_jini/useEffect-SRP-Custom-Hook-Lets-go</link>
            <guid>https://velog.io/@taek_jini/useEffect-SRP-Custom-Hook-Lets-go</guid>
            <pubDate>Tue, 19 Sep 2023 11:08:10 GMT</pubDate>
            <description><![CDATA[<h1 id="의존성-배열">의존성 배열</h1>
<p>의존성 배열이란 뭘까 ?
짧게 얘기하자면 이렇다.</p>
<ul>
<li>useEffect에 두번째 인자로 넘기는 배열.</li>
<li>두번째 인자를 넘기지 않으면 Effect는 매번 실행되고, 
빈 배열을 넘긴다면 컴포넌트의 첫번째 렌더링 이후에만 실행된다.</li>
</ul>
<p>난 지금까진 이렇게까지만 알고 있었다.
근데 이 글을 적는다는건 문제가 있었다는거겠죠 .. ㅠ</p>
<blockquote>
<p><strong><code>useEffect(effect, 의존성)</code></strong>
여기서 <strong>effect는 함수의 형태</strong>로 표현되고, 
<strong>의존성은 여러 의존성들을 한번에 전달하기 위해서 배열의 형태</strong>로 표현된다.</p>
</blockquote>
<p>useEffect에서 의존성 배열이란 <strong>“무언가가 의존하고 있는 요소들의 모음”</strong> 이라고 할 수 있다. 그리고 여기서 말하는 무언가는 바로 <strong>effect 함수</strong>입니다. 
즉, useEffect의 의존성 배열은 <strong>“effect 함수가 의존하고 있는 요소들의 모음”</strong> 이라고 할 수 있습니다.</p>
<p><strong>“의존하고 있다”</strong> 라는 말이 어색하고 잘 이해가 안될 수도 있습니다. 이를 쉽게 풀어서 설명하자면 단순히 그냥 effect 함수가 사용하고 있는 외부의 값들이 의존성입니다.</p>
<pre><code class="language-js">function Component(){
    const [count, setCount] = useState(0);

    const effect = () =&gt; {
        document.title = `you clikced ${count} times`
    };

    useEffect(effect, [count]];
}</code></pre>
<p>위의 예시에서 effect 함수는 <code>count</code> 라는 외부의 값을 내부에서 사용하고 있다. 따라서 <strong>effect 함수의 의존성은 <code>count</code> 이며 count를 의존성 배열</strong>에 넣어줘야하는 것이다.</p>
<p>이렇게 되면 useEffect는 리렌더링이 된 후 의존성 배열을 검사해서 의존성 배열에 있는 값들이 변경되었을 경우에 다시 새로운 의존성을 가지고 effect를 실행시켜 줍니다.</p>
<h1 id="useeffect-의존성-배열을-잘-설정하는-법">useEffect 의존성 배열을 잘 설정하는 법</h1>
<p>대부분 필요없는 요소들을 굳이 의존성 배열에 넣는 실수는 잘 하지 않는다.
하지만, 필요한 의존성을 제대로 의존성 배열에 넣어주지 않는 실수를 많이 한다.</p>
<p>유경험자로서 그 이유를 빗대어 보자면</p>
<ul>
<li>넣었더니 에러가 나서</li>
<li>뭘 넣으라는건지 몰라서</li>
</ul>
<p>부끄럽지만 딱 이 두가지 이유로 지금껏 실수를 하고 있었다 ! </p>
<p>지금부턴 useEffect에서 버그가 발생하지 않게 의존성 배열을 잘 설정하는 방법을 적어보려고 한다.</p>
<h2 id="step-1-모든-의존성을-의존성-배열에-명시해라">Step 1. 모든 의존성을 의존성 배열에 명시해라.</h2>
<p>말로만 들으면 엄청 쉽다. 외부 값을 사용했으면 의존성을 추가해주면 되는 일이니까.
하지만 ! 내가 ! 지금껏 못 넣은 이유는 ! 
의존성을 추가했더니 무한루프에 빠졌기 때문에 부득이하게 뺄 수 밖에 없었다.. 
<img src="https://velog.velcdn.com/images/taek_jini/post/e5fbb731-3e8f-463a-adf3-04e735f62074/image.png" alt=""></p>
<blockquote>
<h3 id="왜였을까">왜였을까?</h3>
<p>함수 컴포넌트의 내부에서 선언한 <strong>Object</strong>, <strong>Function</strong>의 경우에는 함수 컴포넌트의 매 호출마다 새로운 객체, 함수가 선언되고 <strong>참조형 데이터 타입의 특징으로 인해 객체 내부의 요소들이 동일하더라도 새롭게 생성된 객체와 이전 객체를 비교하면 서로 다른 객체</strong>라고 판단되게 된다.
그럼 무한루프 시작인거지 ㅎ</p>
</blockquote>
<p>그래서 문제를 해결하기 위한 3가지 방안을 가져왔습니다 !</p>
<h3 id="1-의존성을-제거하자-함수를-effect-안에-선언하기">1. 의존성을 제거하자 (함수를 effect 안에 선언하기)</h3>
<ul>
<li><p>나쁜 예</p>
<pre><code class="language-js">// bad
function Component(){
  const [count, setCount] = useState(0);

  const increaseCount = () =&gt; {
      setCount(prev =&gt; prev + 1);
  }

  useEffect(increaseCount, [increaseCount]];
}</code></pre>
</li>
<li><p>좋은 예</p>
<pre><code class="language-js">// good
function Component(){
  const [count, setCount] = useState(0);

  useEffect(() =&gt; {
      const increaseCount = () =&gt; {
          setCount(prev =&gt; prev + 1);
      };

      increaseCount();
  }, []];
}</code></pre>
<p><code>useEffect</code>에서 활용해야 하는 함수를 <code>effect</code> 안에다가 선언하여 의존성에 추가하지 않게끔 만드는 것이다.</p>
<h3 id="2-함수를-컴포넌트-바깥으로-이동시키기">2. 함수를 컴포넌트 바깥으로 이동시키기</h3>
</li>
<li><p>나쁜 예</p>
<pre><code class="language-js">// bad
function Component() {
  const getUserAuth = () =&gt; {
      localStorage.getItem(&quot;ACCESS_TOKEN&quot;);
  };

  useEffect(() =&gt; {
      const token = getUserAuth();
      // login....
  }, []];
};</code></pre>
</li>
<li><p>좋은 예</p>
<pre><code class="language-js">// good
function Component() {

  useEffect(() =&gt; {
      const token = getUserAuth();
      // login....
  }, [getUserAuth]];
</code></pre>
</li>
</ul>
<p>};</p>
<p>const getUserAuth = () =&gt; {
    localStorage.getItem(&quot;ACCESS_TOKEN&quot;);
};</p>
<pre><code>컴포넌트 바깥에 함수를 두면 어떻게 될까 ?
한번 선언되고 값이 절대 바뀌지 않는다. 
즉, **컴포넌트가 렌더링 되어도 값이 변하지 않기 때문에 의존성에 영향을 미치지 않는다.**

### 3. 메모이제이션 (최후의 수단)
``` js 
function Component(){
    const [count, setCount] = useState(0);

    const increaseCount = () =&gt; {
        setCount(prev =&gt; prev + 1);
    }

    useEffect(() =&gt; {
        // do something 1
        increaseCount();
    }, []];

    useEffect(() =&gt; {
        // do something 2
        increaseCount();
    }, []];
}</code></pre><p>위 예시처럼 <strong>1개 이상의 effect를 사용해야 하는 상황</strong>에선 어떻게 해야할까? 
함수를 안에 넣자니 중복코드가 되어버리고, 
컴포넌트 밖으로 빼자니 다른 값들을 참조할 수가 없다. </p>
<p>이런 경우엔 메모이제이션을 사용해주는게 맞다.</p>
<pre><code class="language-js">    const increaseCount = useCallback(() =&gt; {
        setCount(prev =&gt; prev + 1);
    }, []);</code></pre>
<blockquote>
<p><strong>이렇게되면 메모이제이션 비용이 늘어나지 않나요 ?</strong></p>
</blockquote>
<ul>
<li>네, 맞습니다. 퍼포먼스적인 측면에서 효율을 따지자면 중복코드를 사용하는게 더 좋을수도 있지만
중복코드를 사용하면서 코드의 복잡성을 높이고 유지보수성을 낮추는 상황보단 낫습니다 !
그렇기에 최후의 수단으로만 <strong>메모이제이션</strong>을 사용하는 이유기도 합니다.</li>
</ul>
<h2 id="step-2-가능하다면-의존성을-적게-만들어라">Step 2. 가능하다면 의존성을 적게 만들어라</h2>
<p>다 넣으라매.... 무슨 뜻일까 ? </p>
<p>예시를 보자면 </p>
<pre><code class="language-js">
  useEffect(() =&gt; {
    const intervalID = setInterval(() =&gt; {
      setCount(count + 1);
    }, 1000);

    return () =&gt; clearInterval(intervalID);
  }, [count, setCount]);</code></pre>
<p>딱 보기엔 괜찮은 코드 같다. 사용한 외부 값들을 다 집어넣어줬으니.. 
하지만 <strong>&quot;최소화 하라&quot;</strong> 를 적용해보자</p>
<pre><code class="language-js">
  useEffect(() =&gt; {
    const intervalID = setInterval(() =&gt; {
      setCount(prevCount =&gt; prevCount + 1);
    }, 1000);

    return () =&gt; clearInterval(intervalID);
  }, [setCount]);</code></pre>
<p><code>setState()</code>의 전달인자를 함수로 만들어서 이전의 state를 인자로 받고 새로운 state를 반환하는 함수를 생성할 수 있다.
이렇게 한다면 <code>count</code> 를 useEffect에서 직접적으로 사용하지 않고, 의존성에서 제거할 수 있다.</p>
<p>참고자료</p>
<ul>
<li>원티드 프리온보딩 인턴십 세션 강의</li>
<li><a href="https://overreacted.io/a-complete-guide-to-useeffect/">https://overreacted.io/a-complete-guide-to-useeffect/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CSS] 헷갈리는 문법 정리]]></title>
            <link>https://velog.io/@taek_jini/CSS-%ED%97%B7%EA%B0%88%EB%A6%AC%EB%8A%94-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@taek_jini/CSS-%ED%97%B7%EA%B0%88%EB%A6%AC%EB%8A%94-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Fri, 18 Aug 2023 09:19:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>네이버 메인페이지 클론코딩</strong>을 진행하고 <strong>CSS에서 중요하다고 판단되거나 아차 싶던 내용</strong>들을 포스팅하는 글입니다.</p>
</blockquote>
<h2 id="position">position</h2>
<p>CSS의 <code>position</code> 속성은 요소가 웹 페이지에 어떻게 위치할지 결정합니다.</p>
<blockquote>
<p><strong>간단 정리</strong></p>
</blockquote>
<ul>
<li><strong>static</strong> : 초기값. 기준 위치를 설정하지 않음.</li>
<li><strong>relative</strong> : 현재 위치를 기준으로 상대 위치를 지정.</li>
<li><strong>absolute</strong> : 부모 위치를 기준으로 절대 위치를 지정.</li>
<li><strong>fixed</strong> : 윈도우(브라우저 창)를 기준으로 절대 위치를 지정하여 요소를 그 위치에 고정(fix)시킴</li>
<li><strong>sticky</strong> : 지정된 위치에 뷰포트가 도달했을 때, 요소가 그 위치에 고정(fix)됨</li>
</ul>
<h3 id="static">static</h3>
<ul>
<li><code>static</code>은 <strong>position의 기본값</strong>으로, position을 지정하지 않아도 기본적으로 적용된다.</li>
<li>position이 <code>static</code>인 상태에는
<code>top</code>, <code>bottom</code>, <code>left</code>, <code>right</code>, <code>z-index</code>를 지정해도 <strong>위치가 변경되지 않는다.</strong></li>
</ul>
<p><strong>따라서,</strong> 요소를 <strong>원하는 위치에 배치</strong>시키고 싶은 경우에는
적합한 상황에 따라 아래에서 언급하고 있는 <strong>다른 position 값을 지정</strong>해줘야한다.</p>
<h3 id="relative">relative</h3>
<ul>
<li><code>relative</code>는 현재 위치를 기준으로 <strong>상대 위치</strong>를 지정하게 해준다.</li>
<li><em>상대 위치*</em>라는 말이 좀 어렵지만 요소 <strong>자신의 원래 위치가 기준점</strong>이 된다고 생각하면 쉽다.</li>
<li><strong>특징</strong><ul>
<li><code>top</code>, <code>bottom</code>, <code>left</code>, <code>right</code>의 <strong>지정이 가능</strong>해진다.</li>
<li><code>z-index</code>의 <strong>지정이 가능</strong>해진다.</li>
<li><code>relative</code>의 기준점은 요소 <strong>자기 자신이 원래 배치</strong>되었던 위치이다.</li>
<li><code>absolute</code>의 <strong>기준 위치</strong>가 된다.</li>
</ul>
</li>
</ul>
<h3 id="absolute">absolute</h3>
<ul>
<li><code>absolute</code>는 <code>relative</code>나 <code>fixed</code>가 지정된 <strong>부모 위치를 기준</strong>으로 자신의 <strong>위치를 지정</strong>할 수 있게 해준다.</li>
<li><strong>특징</strong><ul>
<li><strong>부모 요소(=기준 위치)</strong>에 <code>relative</code> 또는 <code>fixed</code>를 지정해야한다.</li>
<li><strong>자식 요소(=이동시키고 싶은 요소)</strong>에 <code>absolute</code>를 지정한다.</li>
<li><strong>자식 요소</strong>를 <code>top</code>, <code>bottom</code>, <code>left</code>, <code>right</code> 등으로 구체적인 위치를 조절할 수 있다.</li>
</ul>
</li>
</ul>
<h3 id="fixed">fixed</h3>
<ul>
<li><code>fixed</code>는 브라우저 화면(윈도우)을 기준으로 요소를 <strong>정해진 위치에 고정(fix)</strong>시킬 수 있다.</li>
<li>페이지의 <code>top</code> 버튼 과 <code>header</code>를 상단에 고정시킬 때 자주 사용된다.</li>
<li><strong>특징</strong><ul>
<li>브라우저 화면(윈도우) 전체가 기준점이 된다.</li>
<li>스크롤을 해도 요소가 <strong>지정된 위치에 계속 고정</strong>되어 있다.</li>
<li><code>top</code>, <code>bottom</code>, <code>left</code>, <code>right</code>의 <strong>지정이 가능</strong>하다.</li>
<li><code>z-index</code>의 <strong>지정이 가능</strong>하다.</li>
</ul>
</li>
</ul>
<h3 id="sticky">sticky</h3>
<ul>
<li><code>sticky</code>는 요소가 지정된 기준점(<code>top</code>, <code>bottom</code>, <code>left</code>, <code>right</code> 등으로 설정해둔 위치)에 도달했을 때, 그 기준점에 <strong>요소를 고정(fix)</strong>시켜준다.</li>
<li>ex) 페이지를 스크롤하여 <strong>div.menu(sticky)가 top: 30에 도달</strong>했을 때 <strong>그 위치(top: 30)에 그대로 고정(fix)</strong>된다.</li>
<li><strong>특징</strong><ul>
<li>브라우저 화면(윈도우) 전체가 기준점이 된다.</li>
<li><strong>기준점</strong>에 도달했을 때 요소가 <strong>그 위치(기준점)에 고정(fix)</strong>된다.</li>
<li><code>top</code>, <code>bottom</code>, <code>left</code>, <code>right</code>의 지정이 가능하다.</li>
<li><code>z-index</code>의 지정이 가능하다.</li>
</ul>
</li>
</ul>
<h2 id="display">display</h2>
<p>  CSS의 <code>display</code> 속성은 요소가 화면에 어떻게 표시될지 결정합니다.</p>
<h3 id="block">block</h3>
<ul>
<li><code>block</code>은 요소가 <strong>뷰 포트</strong>의 <strong>가로 영역을 100%</strong> 점유하게 되는 특징이 있다.
따라서 block 요소와 인접된 다른 요소는 양 옆으로 배치될 수 없다.</li>
<li>대표적인 요소로는 <code>&lt;div&gt;</code>,<code>&lt;p&gt;</code>,<code>&lt;h1&gt;</code>,<code>&lt;ul&gt;</code> 등이 있다.</li>
<li><strong>특징</strong><ul>
<li><strong>블럭</strong>처럼 <strong>세로로 쌓이는 듯이 나열</strong>되는 것이 특징</li>
<li><code>width</code>, <code>height</code>, <code>margin</code>, <code>padding</code>의 설정이 가능하여 자유도가 높다.</li>
</ul>
</li>
</ul>
<h3 id="inline">inline</h3>
<ul>
<li><code>inline</code>은 각 요소가 인접된 요소의 양 옆으로 나열되는 것이 가능</li>
<li>대표적인 요소는 <code>&lt;a&gt;</code>,<code>&lt;span&gt;</code>,<code>&lt;img&gt;</code>,<code>&lt;svg&gt;</code> 등이 있다.</li>
<li><strong>특징</strong><ul>
<li>요소를 <strong>가로로 나열</strong>할 수 있다.</li>
<li><code>width</code>, <code>height</code>의 <strong>설정이 불가능</strong>하다.</li>
<li><code>margin</code>은 <strong>좌우 값만 설정</strong>이 가능하다.</li>
<li><code>padding</code>은 <strong>상하좌우 설정</strong>이 가능하지만, <strong>상,하의 padding은 다른 요소에게 영향을 줄 수 없다.</strong></li>
<li><code>text-align</code>와 <code>vertical-align</code>을 지정하는 것이 가능하다.<h3 id="inline-block">inline-block</h3>
</li>
</ul>
</li>
<li><code>inline-block</code>은 <code>inline</code>과 <code>block</code>의 <strong>장점</strong>들을 모아놓은 것이다.</li>
<li>요소의 <strong>배치</strong>는 <code>inline</code>적인 성질을 갖고 <strong>형태</strong>는 <code>block</code>적인 성질을 갖는다.</li>
<li><strong>특징</strong><ul>
<li><code>inline</code>처럼 요소를 <strong>가로로 나열</strong>할 수 있다.</li>
<li><code>block</code>처럼 <code>width</code>, <code>height</code>의 <strong>설정이 가능</strong>하다.</li>
<li><code>block</code>처럼 상하좌우에 <code>margin</code>, <code>padding</code>의 <strong>설정이 가능</strong>하다.</li>
<li><code>inline</code>처럼 <code>text-align</code>, <code>ertical-align</code>을 <strong>사용하는 것이 가능</strong>하다.</li>
</ul>
</li>
</ul>
<h3 id="flex">flex</h3>
<ul>
<li><code>flex</code>는 요소를 <code>Flexbox</code>로 만듭니다.</li>
<li>1차원(한 축)레이아웃을 다룰 때 유용하다.</li>
<li><code>Flexbox</code>는 <code>item</code> 간의 <strong>공간 배분과 정렬을 쉽게 할 수 있는 레이아웃</strong> 모델입니다.</li>
<li><strong>특징</strong><ul>
<li><strong>1차원 적인 레이아웃</strong>이며 꼬지처럼 한 방향으로만 갈 수 있다.</li>
<li><code>flex</code> 내부의 자식들을 <code>flex item</code> 이라고 부르며 <code>flex-direction</code>,<code>flex-wrap</code>,<code>justify-content</code>,<code>align-items</code>,<code>align-content</code> 등의 속성을 사용해 <code>item</code>들을 <strong>배치하고 정렬</strong>할 수 있다.</li>
</ul>
</li>
</ul>
<h3 id="grid">grid</h3>
<ul>
<li><code>grid</code>는 행과 열을 기반으로 복잡한 레이아웃을 만들 수 있다.</li>
<li>2차원(행과 열)레이아웃을 다룰 때 유용하다.</li>
<li><strong>특징</strong><ul>
<li><code>flex</code>가 꼬지라면 <code>grid</code>는 신문에 각 부분처럼 공간을 나눌 수 있다.</li>
<li><code>grid-template-rows</code>, <code>grid-template-columns</code>, <code>grid-template-areas</code>, <code>grid-row</code>, <code>grid-column</code> 등의 속성을 사용하여 <code>item</code>들을 <code>grid</code>에 배치하고 정렬할 수 있다.</li>
</ul>
</li>
</ul>
<h2 id="nth">nth</h2>
<ul>
<li>nth 선택자 중 <code>first-child</code>, <code>last-child</code>, <code>nth-child</code>, <code>nth-of-type</code> 처럼 자주 사용되는 선택자들을 정리해 두려고 합니다.<h3 id="nth-child">nth-child</h3>
!codepen[TaekJinJang/embed/LYMEZpb?default-tab=result&amp;theme-id=dark&quot;]</li>
</ul>
<pre><code class="language-css">/* 첫번째 요소 선택 */
.section01 li:first-child{background:skyblue;}

/* 마지막 요소 선택 */
.section02 li:last-child{background:skyblue;}

/* 6번째 요소 선택 */
.section03 li:nth-child(6){background:skyblue;}

/* 홀수 선택 */
.section04 li:nth-child(odd){background:skyblue;}

/* 짝수 선택 */
.section05 li:nth-child(even){background:skyblue;}

/* 세번째 요소마다 선택 */
.section06 li:nth-child(3n){background:skyblue;}

/* 두번째 요소부터 세번째 요소마다 선택 */
.section07 li:nth-child(3n+2){background:skyblue;}

/* 6번부터 모든 요소 선택 */
.section08 li:nth-child(n+6){background:skyblue;}

/* 1번부터 3번까지 요소 선택 */
.section09 li:nth-child(-n+3){background:skyblue;}

/* 3번부터 6번까지 요소 선택  */
.section10 li:nth-child(n+3):nth-child(-n+6){background:skyblue;}

/* 끝에서 3번째 요소 선택 */
.section11 li:nth-last-child(3){background:skyblue;}

/* 끝에서 3번째 요소까지 선택 */
.section12 li:nth-last-child(-n+3){background:skyblue;}

/* 끝에서 3번째 요소부터 세번째 요소마다 선택 */
.section13 li:nth-last-child(3n+3){background:skyblue;}

/* 끝에서 3번째 요소부터 첫번째 요소까지 선택 */
.section14 li:nth-last-child(n+3){background:skyblue;}

/* 그룹 내 형제요소가 5개 이상일 때 전체 선택 */
.section15 li:nth-last-child(n+5),
.section15 li:nth-last-child(n+5) ~ li{background:skyblue;}

/* 그룹 내 형제요소가 3개 이하일 때 전체 선택 */
.section16 li:nth-last-child(-n+3):first-child,
.section16 li:nth-last-child(-n+3):first-child ~ li{background:skyblue;}

/* 3번째 요소만 제외하고 전체 선택 */
.section17 li:not(:nth-child(3)) {background:skyblue;}
</code></pre>
<h3 id="nth-of-type">nth-of-type</h3>
<p>!codepen[TaekJinJang/embed/BavyzjP?default-tab=result&amp;theme-id=dark&quot; ]</p>
<pre><code class="language-css">/* group 내 첫번째 div 요소 선택 */
.section01 .group div:first-of-type{background:skyblue;}

/* group 내 마지막 div 요소 선택 */
.section02 .group div:last-of-type{background:skyblue;}

/* 두번째 span 요소 선택 */
.section03 .group span:nth-of-type(2){background:skyblue;}

/* 첫번째 p 요소 부터 두번째 p요소마다 선택 */
.section04 .group p:nth-of-type(2n+1){background:skyblue;}

/* 세번째 p 요소부터 전체선택 + 첫번째 p 요소부터 6개 선택의 교집합 */
.section05 .group p:nth-of-type(n+3):nth-of-type(-n+6){background:skyblue;}
</code></pre>
<h2 id="가상요소-before-after">가상요소 (::before, ::after)</h2>
<h3 id="beforeafter">::before,::after</h3>
<ul>
<li>::before - 선택한 elements 앞에 가상 콘텐츠 삽입</li>
<li>::after - 선택한 elements 뒤에 가상 콘텐츠 삽입</li>
</ul>
<h3 id="content를-쓰는-이유">content를 쓰는 이유</h3>
<p>content는 가상 요소 before,after를 사용할 때 꼭 써줘야하는 속성.
<code>content=&quot;&quot;;</code> HTML에는 존재하지 않고 <strong>CSS에서만 존재하는 가상요소</strong>이며 
가상요소이기 때문에 <strong>JS에서 제어하기 어렵다는 특징</strong>이 있음.</p>
<h3 id="example--divider-">example ( divider )</h3>
<p>!codepen[/TaekJinJang/embed/GRPgqjx?default-tab=result&amp;theme-id=dark]</p>
<h2 id="css-속성-선택자">CSS 속성 선택자</h2>
<h3 id="example">example</h3>
<ul>
<li><code>div[attr=&quot;val&quot;]</code>    <strong>(=)</strong> : <code>&quot;attr&quot;</code> 속성 값이 <code>&quot;val&quot;</code> 인 <code>div</code> 요소를 선택</li>
<li><code>div[attr~=&quot;val&quot;]</code> <strong>(~=)</strong> : <code>&quot;attr&quot;</code> 속성 값 중 <code>&quot;val&quot;</code> 단어가 있는 <code>div</code> 요소를 선택</li>
<li><code>div[attr^=&quot;val&quot;]</code> <strong>(^=)</strong> : <code>&quot;attr&quot;</code> 속성 값이 <code>&quot;val&quot;</code> 로 시작하는 <code>div</code> 요소를 선택</li>
<li><code>div[attr$=&quot;val&quot;]</code> <strong>($=)</strong> : <code>&quot;attr&quot;</code> 속성 값이 <code>&quot;val&quot;</code> 로 끝나는 <code>div</code> 요소를 선택</li>
<li><code>div[attr*=&quot;val&quot;]</code> <strong>(*=)</strong> : <code>&quot;attr&quot;</code> 속성 값이 <code>&quot;val&quot;</code>을 어느식으로든 포함하는 <code>div</code>요소를 선택</li>
<li><code>div[attr|=&quot;val&quot;]</code> <strong>(|=)</strong> : <code>&quot;attr&quot;</code> 속성 값이 <code>&quot;val&quot;</code> 이거나 <code>&quot;val&quot;</code> 으로 시작하는 <code>div</code> 요소를 선택</li>
</ul>
<blockquote>
<p>출처
<a href="https://developer.mozilla.org/ko/docs/Web/CSS">https://developer.mozilla.org/ko/docs/Web/CSS</a>
<a href="https://velog.io/@hsecode/nth-child">https://velog.io/@hsecode/nth-child</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Typescript] 타입 분석하기 - 1]]></title>
            <link>https://velog.io/@taek_jini/Typescript-%ED%83%80%EC%9E%85-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0-1</link>
            <guid>https://velog.io/@taek_jini/Typescript-%ED%83%80%EC%9E%85-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0-1</guid>
            <pubDate>Fri, 04 Aug 2023 02:05:20 GMT</pubDate>
            <description><![CDATA[<h3 id="foreach-map-제네릭-분석">forEach, map 제네릭 분석</h3>
<pre><code class="language-ts">function add&lt;T&gt;(x:T, y:T) : T {return x}

add(1,2); // 자동으로 T의 타입을 number로 추론해줌.
add&lt;number&gt;(1,2); // 타입스크립트가 추론을 잘못해줄 때 지정해줘야됨.</code></pre>
<hr>
<ul>
<li>filter<pre><code class="language-ts"># interface 안에서도 제너릭사용가능
interface Array&lt;T&gt; {
forEach(callbackfn: (value: T, index: number, array: T[]) =&gt; void, thisArg?: any): void;
}
# 제네릭이 타입추론 제대로 해줌
// (parameter) item: number
[1, 2, 3].forEach((value) =&gt; { console.log(value); }); 
</code></pre>
</li>
</ul>
<p>// (parameter) item: string
[&#39;1&#39;, &#39;2&#39;, &#39;3&#39;].forEach((value) =&gt; { console.log(value); }); </p>
<p>// (parameter) item: string | number | boolean
[&#39;123&#39;, 123, true].forEach((value) =&gt; { console.log(value); });</p>
<h1 id="제너릭을-사용하지않고-가능성있는-타입을-적어주면">제너릭을 사용하지않고 가능성있는 타입을 적어주면?</h1>
<p>function add(x: string | number, y: string | number) {
}
// 이런 함수가 가능하게됨<br>add(&#39;1&#39;, 2);
add(1, &#39;2&#39;);</p>
<p>// 제네릭을 사용하면 위와같은 케이스들을 잘 걸러준다. 
function add<T>(x: T, y: T) {}
add<number>(1, 2); // 제네릭 타입 파라미터가 뭔지 적어줄수도 있음
add(&#39;1&#39;, &#39;2&#39;)
add(true, false); </p>
<p>function add<T>(x: T, y: T): T {return x}</p>
<pre><code>- map
``` ts
# 제네릭 부분에 (T, U) 값을 직접 적어주면 덜 헷갈림 
interface Array&lt;T&gt; {
  forEach(callbackfn: (value: T, index: number, array: T[]) =&gt; void, thisArg?: any): void;
  map&lt;U&gt;(callbackfn: (value: T, index: number, array: T[]) =&gt; U, thisArg?: any): U[];
}

// [&#39;1&#39;, &#39;2&#39;, &#39;3&#39;] string[]
const strings = [1, 2, 3].map((item) =&gt; item.toString());</code></pre><h3 id="filter-제네릭-분석">filter 제네릭 분석</h3>
<pre><code class="language-ts">interface Array&lt;T&gt; {
    filter&lt;S extends T&gt;(predicate: (value : number, index:number, array:T[]) =&gt; value is S ,thisArg?:any) : S;
    filter(predicate : (value : T, index:T, array: T[]) =&gt; unknown, thisArg?: any): T[]
}

const filtered = [1,2,3,4,5].filter((v) =&gt; v % 2);
// 위의 filtered는 둘중 어느것에 더 적합할까? 
// v가 number이기 때문에 v % 2가 unknown은 아닐 것임.
// 결론적으로 첫번째를 따를 것이다. 

const filter2 = [&#39;1&#39;,2,&#39;3&#39;,4,&#39;5&#39;].filter((v) =&gt; typeof v === &#39;string&#39;);
// filter2를 number | string 으로 타입을 추론함.</code></pre>
<ul>
<li><p><strong>왜 타입 추론을 제대로 못하는지 생각해보자</strong></p>
<pre><code class="language-ts">interface Array&lt;T&gt; {
  filter&lt;S extends number | string&gt;(predicate: (value : number | string, index:number | string, array:number | string[]) =&gt; value is S ,thisArg?:any) : (number | string)[];
  filter(predicate : (value : T, index:number, array:  T[]) =&gt; unknown, thisArg?: any):  T[]
}
// 둘 다 어짜피 string | number</code></pre>
<p>첫 번째 filter의 S는 string | number이기 때문에 string이 될 가능성이 남아있지만, 두 번째 filter의 T는 고정되어 있기 때문에 타입이 바뀔 가능성이 존재하지 않는다. 타입 추론을 제대로 해줄 수 없음</p>
</li>
<li><p><strong>추론을 잘 하게 하려면 어떡해야할까?</strong></p>
<pre><code class="language-ts">interface Array&lt;T&gt; {
  filter&lt;S extends T&gt;(predicate: (value : T, index:T, array:T[]) =&gt; value is S ,thisArg?:any) : S[];
  filter(predicate : (value : T, index:number, array:  T[]) =&gt; unknown, thisArg?: any):  T[]
}
const predicate = (value: string | number) : value is string =&gt; typeof value === &#39;string&#39;;
// T는 string | number, S는 string으로 타입을 좁혀서 추론하게 한다. 
const filter2 = [&#39;1&#39;,2,&#39;3&#39;,4,&#39;5&#39;].filter((v) =&gt; typeof v === &#39;string&#39;);</code></pre>
<p>predicate를 똑같이 가져와서 타입을 좁혀준다.</p>
</li>
</ul>
<h3 id="foreach-타입-직접-만들기">forEach 타입 직접 만들기</h3>
<ul>
<li>나쁜 예<pre><code class="language-ts">interface Arr {
   forEach(callback : (item: number | string) =&gt; void) : void; 
}
</code></pre>
</li>
</ul>
<p>const a : Arr = [1,2,3];
a.forEach((item) =&gt; {
    console.log(item);
});</p>
<p>const b : Arr = [&#39;1&#39;,&#39;2&#39;,&#39;3&#39;];
b.forEach((item) =&gt; {
    console.log(item);
    item.charAt(3); // 할경우 에러가 난다. item의 타입을 잘못 선언한것.
});</p>
<pre><code>- 좋은 예
``` ts
interface Arr&lt;T&gt; {
    forEach(callback : (item: T) =&gt; void) : void; 
}
// 현재는 index에 관한 파라미터가 없지만 사용하려면 넣어주면 된다. 
const a : Arr&lt;number&gt; = [1,2,3];

a.forEach((item) =&gt; {
    console.log(item);
});</code></pre><h3 id="map-타입-직접-만들기">map 타입 직접 만들기</h3>
<pre><code class="language-ts">interface Arr&lt;T&gt; {
    forEach(callback : (item: T) =&gt; void) : void;
    // map(callback: (v) =&gt; void): void
    //map(callback: (v: T) =&gt; T): T[] -&gt; return 값에 toString이 있을 시 오류
    # 새로운 타입 S를 도입
      map&lt;S&gt;(callback: (v: T)=&gt; S): S[];
    map&lt;S&gt;
}  
const a:Arr&lt;number&gt; = [1,2,3];
const b = a.map((v) =&gt; v+1); // [2,3,4]
const c = a.map((v) =&gt; v.toString()); </code></pre>
<p>Arr<number> 을 입력하면 T의 타입이 number가 된다. 따라서 map 콜백함수의 v에도 타입을 T로 지정해줘야 타입스크립트가 any에서 number로 타입을 잘 지정해준다. </p>
<h3 id="filter-타입-직접-만들기">filter 타입 직접 만들기</h3>
<pre><code class="language-ts">interface Arr&lt;T&gt; {
    //filter(callback: (v:T)=&gt; boolean) : T[];
    filter&lt;S extends T&gt;(callback: (v:T)=&gt; v is S) : S[];
}
// extends를 사용하지 않을 경우
// S는 T의 부분집합이라서 T가 S로 좁혀질 수 있다. 
const a:Arr&lt;number&gt; = [1,2,3]; 
const b = a.filter((v) =&gt; v % 2 === 0); // [2] number[]

const c: Arr&lt;number|string&gt; = [1,&#39;2&#39;,3,&#39;4&#39;,5];
//const d = c.filter((v) =&gt; typeof v === &#39;string&#39;);
// 타입을 (string|numebr)[] 로 추론한다. 

const d = c.filter((v): v is string =&gt; typeof v === &#39;string&#39;);
// is 타입가드로 타입을 좁혀준다. </code></pre>
<h3 id="공변성과-반공변성">공변성과 반공변성</h3>
<ul>
<li><p>return 값</p>
<pre><code class="language-ts">function a(x:string) : number {
  return +x;
}
type B = (x:string) =&gt; number | string;
const b:B = a;
// a는 number로 반환, B는 number or string으로 반환.
//서로 타입이 다른데 가능할까? 라는 의문이 생긴다.
// -&gt; return 값은 더 넓은 타입으로 대입이 가능하다. </code></pre>
</li>
<li><p>매개변수</p>
</li>
</ul>
<pre><code class="language-ts">function a(x:string) : number | string{
  return +x;
}
type B = (x:string) =&gt; number;
const b:B = a;
// return 값이 넓은 타입에서 좁은 타입으로는 대입이 불가능하다.
// x 

function a(x: number | string) : number{
    return +x;
}
type B = (x:string) =&gt; number;
const b:B = a;

//매개변수 같은 경우는 좁은 타입으로 대입된다. </code></pre>
<blockquote>
<p><strong>제일 중요한 것</strong></p>
</blockquote>
<ul>
<li>매개변수 : 넓은 타입에서 좁은 타입으로 대입 가능</li>
<li>return 값 : 좁은 타입에서 넓은 타입으로 대입 가능</li>
</ul>
<h3 id="하나에는-걸리겠지오버로딩">하나에는 걸리겠지(오버로딩)</h3>
<p>  <strong>오버로딩이란?</strong> -&gt; 같은 타입을 여러번 선언하는것</p>
<pre><code class="language-ts">  function add(x:number, y:number): number
function add(x:string, y:string) : string
function add(x: number | string, y: number| string){
    return x+y;
}</code></pre>
<p> *<em>declare 란? *</em> -&gt; 타입만 지정해주고 바디부분은 구현하지 않아도 다른 곳에 있다고 생각한다.</p>
<pre><code class="language-ts">  declare function add(x:number, y:number, z?:number): number</code></pre>
<h3 id="타입스크립트는-건망증이-심하다--에러-처리법-">타입스크립트는 건망증이 심하다 ( 에러 처리법 )</h3>
<p>  err의 타입은 unknown으로 지정.</p>
<pre><code class="language-ts">  interface CustomError {
    name : string;
    message: string;
    stack?: string;
    response?: {
        date:any;
    }
}

declare const axios: Axios;

(async () =&gt; {
    try {
        await axios.get();
    } catch (err: unknown) {
        console.error((err as CustomError).response?.data);
    // err.response?.data -&gt; 알 수 없는 형식이라고 뜬다. 일회성이기 때문에.
      // const customError = err as CustomError 처럼 변수 지정을 해줘야 재사용 가능
    }
})();

(async () =&gt; {
    try {
        await axios.get();
    } catch (err: unknown) {
        if (err instanceof typeof CustomError) {
        const CustomError = err as CustomError;
        console.error(CustomError.response?.data);
        CustomError.data;
        }
    }
})();
// interface로 지정할 경우 에러가 난다.</code></pre>
<p>  매번 (err as CustomError)을 지정해주거나 변수에 err as CustomError를 지정해준다. </p>
<pre><code class="language-ts">  class CustomError extends Error{
    response?: {
        data: any;
    }
}

(async () =&gt; {
    try {
        await axios.get();
    } catch (err: unknown) {
        if (err instanceof CustomError) {
        const CustomError = err as CustomError;
        console.error(CustomError.response?.data);
        CustomError.data;
        }
    }</code></pre>
<p>interface를 사용할 경우 instanceof를 쓸 수 없음. ( 타입가드로 사용할 수 없음. )</p>
<p>js에 남아있으면서도 타입 가드가 가능한 class로 지정해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Typescript] 기본 문법 총정리]]></title>
            <link>https://velog.io/@taek_jini/Typescript-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@taek_jini/Typescript-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EC%B4%9D%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 01 Aug 2023 02:44:52 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/taek_jini/post/4f8c0dfb-e0e8-4d26-84c8-a79651d6afb4/image.png" alt=""></p>
<h3 id="타입스크립트는-변수-매개변수-리턴값에-타입을-붙이는-것-">타입스크립트는 변수, 매개변수, 리턴값에 타입을 붙이는 것 !</h3>
<pre><code class="language-ts">const a: number = 5; // 변수 이름 뒤에 : 타입 (소문자)
const b: string = &#39;5&#39;;
const c: boolean = true;
const d: undefined = undefined;
const e: null = null;
const f: any = 123; // 모든타입이 다 됨. 
const g: true = true; // 값을 고정
const h: 5 = 5; //숫자 고정</code></pre>
<h3 id="타입스크립트를-사용하는-목적은-any-타입을-없애는-것">타입스크립트를 사용하는 목적은 any 타입을 없애는 것</h3>
<pre><code class="language-ts">// function 함수
function add(x: number, y: number) // 매개변수 부분의 타입 지정
: number { return x+y }; // return 부분의 타입 지정 :

// type으로 타입을 선언하는 방식 (type alias)
type Add = (x: number, y: number) =&gt; number; 
const add: (x: number, y: number) =&gt; number = (x, y) =&gt; x + y; 

//화살표함수
const add: Add = (x,y) =&gt; x + y; // 리턴값이 화살표 뒤에 나옴


// 객체
const obj: 
{ lat: number, lon: number} 
= {lat: 37.5, lon: 127.5}; 

// 배열
const arr: string[] = [&#39;123&#39;, &#39;456&#39;] 
const arr2: number[] = [123, 456] 
const arr3: Array&lt;number&gt; = [123, 456] // Generic Type
const arr4: [number, number, string] = [123, 456, &#39;hello&#39;] // 길이 고정</code></pre>
<h3 id="타입-추론을-적극-활용하자">타입 추론을 적극 활용하자</h3>
<pre><code class="language-ts">const b: string = &#39;5&#39;</code></pre>
<p>-&gt; b는 5라는 문자열, 하지만 타입을 string으로 적어줄 경우 b가 5가 아닌 문자열이라는 타입으로 범위가 넓어진다. ( 더 부정확해지는 것 )</p>
<ul>
<li>타입추론을 정확히 한다면 타입을 명시 안해도 된다.</li>
<li>타입은 최대한 좁게 적기 <h3 id="js-변환-시-사라지는-부분을-파악하자-npx-tsc">js 변환 시 사라지는 부분을 파악하자 (npx tsc)</h3>
<h4 id="타입을-없애도-올바른-자바스크립트-코드여야-한다">타입을 없애도 올바른 자바스크립트 코드여야 한다.</h4>
<pre><code class="language-ts">아래의 타입코드를 자바스크립트로 변환하면 타입코드는 전부 사라짐 (bold처리)
//before (ts)
const f: true = true;  
type Add = () =&gt; number;
interface Minus {}
Array&lt;string&gt;
</code></pre>
</li>
</ul>
<p>function add(x: number, y: number): number; // 타입 설정
function add(x, y) { // 실제 코드 선언
  return x + y;
}
//after (js)
const f = true;  </p>
<p>function add(x, y) { // 실제 코드 선언
  return x + y;
}</p>
<p>// 문자열 형식을 억지로 다른 타입으로 바꿈 
// as를 사용해서 앞의 타입을 강제로 다른 타입으로 바꿔줌
let aa  = 123;
aa = &#39;hello&#39; as unknown as number; </p>
<pre><code>&gt; .ts -&gt; `function add(x: number, y:number) { return x + y};`
.js -&gt; `function add(x, y) { return x+y };`

### never 타입
https://ui.toast.com/weekly-pick/ko_20220323
- 빈 배열을 선언하면 never 타입 적용
- `const array:never[] -&gt; array` 에는 아무 타입도 올 수 없다. 그래서 빈 배열을 선언할 때는 반드시 타입을 지정해줘야 함
``` ts
try {
  const array: string[] = []; // 타입 지정
  array[0];
} catch(error) {
  error;
}</code></pre><h3 id="원시-래퍼-타입-템플릿-리터럴-타입-rest-튜플">원시 래퍼 타입, 템플릿 리터럴 타입, rest, 튜플</h3>
<ul>
<li>타입 설정은 소문자로만 !! ex) string</li>
<li>control + spacebar : 자동완성 추천</li>
</ul>
<pre><code class="language-ts">type World = &quot;world&quot;;
const a: World = &#39;world&#39;; 

const b = `hello ${a}`;

// 타입에서도 사용가능
type Greeting = `hello ${World}`; // &quot;hello world&quot; 

type World = &quot;world&quot; | &#39;hell&#39;;
type Greeting = `hello ${World}`; 
// &#39;hello hell&#39;, &#39;hello world&#39;  타입추천을 정교하게
const c : Greeting = &#39;hello hell&#39;| &#39;hello world&#39; </code></pre>
<pre><code class="language-ts">let arr: string[] = [];
let arr2: Array&lt;string&gt; = [];

function rest(a, ...args: string[]) { // 타입 추가
  console.log(a, args); 
}
rest(&#39;1&#39;,&#39;2&#39;,&#39;3&#39;); // 1, [2,3]

const tuple: [string, number] = [&#39;1&#39;, 1]; 

// 에러처리 o
tuple[2] = &#39;hello&#39;; 

// 에러처리 x
tuple.push(&#39;hello&#39;);</code></pre>
<h4 id="enum-keyof-typeof">enum, keyof, typeof</h4>
<ul>
<li>enum
속성이 순서대로 0,1,2,3, ... 으로 부여된다.<pre><code class="language-ts">// enum
const enum EDirection {
Up = 3, 
Down = 5, 
Left = 4, 
Right = 6, 
}
// 만약 값을 지정하지 않았다면 a = 0
const a = EDirection.Up; // a = 3
</code></pre>
</li>
</ul>
<p>// object
const ODirection = {
  Up: 0,
  Down: 1,
  Left: 2,
  Right: 3,
} as const;
// as const가 없으면 -&gt;
// Up: number; Down: number; Left: number; Right: number; 
// as const -&gt; 할당한 값을 그대로 사용(정확한 값이 들어감)</p>
<pre><code>| enum | object |
| --- | --- |
| - 자바스크립트에 코드를 남기고싶지않을때 사용| - 자바스크립트에 코드를 남기고싶을떄 사용 |
| - 자바스크립트에서 코드가 사라짐 | 자바스크립트에서 코드가 남아있음 |
| 값을 지정해줄수 있음 (=) | 값을 지정해줄수 있음 (:) |

### keyof
``` ts
const obj = { a: &#39;123&#39;, b: &#39;hello&#39;, c: &#39;world&#39;};

// a,b,c만 꺼내오고싶을떄
// typeof : 자바스크립트 값을 type으로 쓰고싶을떄 typeof을 사용
// keyof : 키값만 받아옴
type Key = keyof typeof obj; // type Key = &quot;a&quot; | &quot;b&quot; | &quot;c&quot;

typeof obj 는 obj 객체의 타입을 추론 { a: string, b: string, c: string }
keyof typeof obj는 typeof obj 타입의 모든 속성 키를 유니온 타입으로 추출 
a | b | c

const obj = { a: &#39;123&#39;, b: &#39;hello&#39;, c: &#39;world&#39;} as const;
type Key = typeof obj[keyof typeof obj]; 
// type Key = &quot;123&quot; | &quot;hello&quot; | &quot;world&quot; // value들만 가져옴

typeof obj = { readonly a: &#39;123&#39;, readonly b: &#39;hello&#39;, readonly c: &#39;world&#39; }
keyof typeof obj = &#39;a&#39; | &#39;b&#39; | &#39;c&#39;
typeof obj[keyof typeof obj] = &#39;123&#39; | &#39;hello&#39; | &#39;world&#39; </code></pre><h3 id="union---과-intersection--">union( | ) 과 intersection( &amp; )</h3>
<pre><code class="language-ts">모든 속성이 다 있어야한다
type A = { hello: &#39;world&#39; } &amp; { zero: &#39;cho&#39; }; 
const a: A = { hello: &#39;world&#39;, zero: &#39;cho&#39;}; 

하나만 있어도 된다.
type A = { hello: &#39;world&#39; } | { zero: &#39;cho&#39; }; 
const a: A = { hello: &#39;world&#39;}; // 가능</code></pre>
<h3 id="타입-에일리어스-alias-와-인터페이스의-상속--extends-">타입 에일리어스( Alias )와 인터페이스의 상속 ( extends )</h3>
<ul>
<li>Type을 상속의 개념으로 쓸 수 있음<pre><code class="language-ts">type Animal = { breath: true };
type Poyouryu = Animal &amp; { breed: true };
type Human = Poyouryu &amp; { think: true};
</code></pre>
</li>
</ul>
<p>const zerocho: Human = { breath: true, breed: true, think: true };</p>
<pre><code>- 인터페이스 상속
```ts
interface A { breath: true}

interface B extends A { breed: true } // 확장

const b: B = { breath: true, breed: true };</code></pre><ul>
<li><p><strong>인터페이스</strong> 특징</p>
<ul>
<li>같은이름으로 여러번 선언을 할수있다</li>
<li>선언할떄마다 합쳐진다 -&gt; 다른 사람이 작성한 라이브러리 코드를 수정가능</li>
</ul>
</li>
</ul>
<pre><code class="language-ts">interface A { talk: () =&gt; void; }

interface A { eat: () =&gt; void; }

interface A { shit: () =&gt; void; }

const a: A = { talk() {}, eat() {}, shit() {}}</code></pre>
<blockquote>
<p>*<em>Naming Rule *</em></p>
</blockquote>
<ul>
<li>예전에는 타입 지정 변수 앞에 대문자로 I,T,E 처럼 <code>interface</code>,<code>type</code>,<code>enum</code> 을 구분지어줬는데 이젠 IDE에 다 나오기 때문에 거의 안붙임</li>
</ul>
<h3 id="타입을-집합으로-생각하자--좁은-타입과-넓은-타입-">타입을 집합으로 생각하자 ( 좁은 타입과 넓은 타입 )</h3>
<ul>
<li>좁은 타입에서 넓은 타입으로 대입 가능</li>
<li>반대로 넓은 타입에서 좁은 타입으로 대입은 불가능<pre><code class="language-ts">type A = string | number; // 넓은 타입
type B = string; // 좁은 타입
</code></pre>
</li>
</ul>
<h1 id="객체">객체</h1>
<p>type A = { name: string };
type B = { age: number}; </p>
<h1 id="넓은-타입">넓은 타입</h1>
<p>type AB = A | B; </p>
<h1 id="객체는-상세할수록-좁음--a--b-">객체는 상세할수록 좁음 ( A &amp; B );</h1>
<p>type C = { name: string, age: number };</p>
<p>const ab: AB = { name: &#39;zerocho&#39;};
const c: C = {name: &#39;zerocho&#39;, age: 29};  </p>
<h1 id="잉여속성검사때문에-좁은타입에서-넓은타입에-대입해도-오류가-날수있다">잉여속성검사때문에 좁은타입에서 넓은타입에 대입해도 오류가 날수있다.</h1>
<h1 id="데이터를-변수로-뺴주고-대입하면-에러가-사라짐">데이터를 변수로 뺴주고 대입하면 에러가 사라짐</h1>
<p>const obj = { name: &#39;zerocho&#39;, age: 29, married: false};
const c: C = obj;</p>
<pre><code>
### void의 두 가지 사용법
``` ts 
function a() { // function a(): void 로 추론
}

const b = a(); // const b: void 로 추론

# 리턴값이 있으면 에러가 뜸 (undefined 가능, null 불가능)
# return을 안적거나, return만 적거나 undefined 

function a(): void { 
  return &#39;3&#39;; // 에러 : &#39;string&#39; 형식은 &#39;void&#39; 형식에 할당할 수 없습니다.
}
interface Human {
  talk: () =&gt; void; // talk 메서드
}

const human: Human = {
  talk() {}    // talk() {return &#39;abc&#39;} -&gt; 가능
}</code></pre><hr>
<blockquote>
<p><strong>void</strong> 3가지 </p>
</blockquote>
<ul>
<li>return 값이 void</li>
<li><blockquote>
<p>(함수에 직접적인 리턴값이 void인 경우에만 return에 값을넣었을떄 오류)</p>
</blockquote>
</li>
<li>매개변수에 void함수</li>
<li><blockquote>
<p>(리턴값이 존재할수 있음, 리턴값을 무시)</p>
</blockquote>
</li>
<li>메서드가 void함수</li>
<li><blockquote>
<p>(리턴값이 존재할수 있음, 리턴값을 무시)</p>
</blockquote>
</li>
</ul>
<pre><code class="language-ts">function a(): void { // function 선언 void
}

function a(callback: () =&gt; void): void { // 매개변수로 선언한 void
}

interface Human { 
  talk: () =&gt; void; // 메서드로 선언할떄 void 두개의 역할이 조금 다름
}</code></pre>
<hr>
<blockquote>
<p><strong>declare</strong>
forEach 선언을 다른 파일에서했을때, declare를 사용하면,
forEach 외부에서 선언했음을 보증</p>
</blockquote>
<pre><code class="language-ts">declare function forEach&lt;T&gt;(arr: T[], callback: (el: T) =&gt; undefined): void; 
// declare function forEach&lt;T&gt;(arr: T[], callback: (el: T) =&gt; void): void;
let target: number[] = [];
forEach([1, 2, 3], el =&gt; target.push(el));</code></pre>
<h3 id="unknown과-any-타입-대응-표-">unknown과 any( 타입 대응 표 )</h3>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/9cbe6d6a-bebd-405a-a806-b68333b9e080/image.png" alt=""></p>
<pre><code class="language-ts">interface A {
  talk: () =&gt; void;
}
const a: A = {
  talk() { return 3; }
}


# unknown vs any

# any 를 사용하면 이후로 타입스크립트가 타입검사를 포기함 
const b: any = a.talk(); 
b.method();

# unknown 을 사용하면 사용자가 직접 b의 타입을 정해줌. 정해진 타입만 사용가능
const b: unknown = a.talk(); 
(b as A).talk();

# unknown 예)
try {
} catch (error) { // var error: unknown
  error.message // -&gt; (error as Error).message 
}</code></pre>
<h3 id="타입-좁히기-타입-가드-">타입 좁히기( 타입 가드 )</h3>
<pre><code class="language-ts">function numOrStr(a: number | string) {
  if (typeof a === &#39;string&#39;) {
    a.split(&#39;,&#39;);  
  } else {
    a.toFixed(1);
  }
}


function numOrNumArr(a: number | number[]) {
  if (Array.isArray(a)) { // number[]
    a.slice(1);  
  } else {                // number
    a.toFixed(1);
  }
}
numOrNumArr(123);
numOrNumArr([1, 2, 3]);</code></pre>
<hr>
<pre><code class="language-ts"># 클래스 자체의 타입은 typeof 클래스(typeof A)입니다.
class A {
  aaa() {}
}
class B {
  bbb() {}
} 
function aOrB(param: A | B) {
  if (param instanceof A) { // Class 간에는 서로 instanceof 로 구별한다
    param.aaa();
  }

}
aOrB(new A()); 
aOrB(new B());</code></pre>
<hr>
<pre><code class="language-ts">type B = { type: &#39;b&#39;, bbb: string };
type C = { type: &#39;c&#39;, ccc: string };
type D = { type: &#39;d&#39;, ddd: string };
type A = B | C | D;

# 값으로 구분
function typeCheck(a: A) {
  if (a.type === &#39;b&#39;) {
    a.bbb;
  } else if (a.type === &#39;c&#39;) {
    a.ccc; // 타입추론 : (parameter) a: C
  } else {
    a.ddd;
  }
}

# 속성명으로 구분
function typeCheck(a: A) {
  if (&#39;bbb&#39; in a) { // in 연산자 // a객체안에 bbb라는 속성이 있으면
    a.type; // a: B
  } else if (&#39;ccc&#39; in a) {
    a.ccc; 
  } else {
    a.ddd;
  }
}

# 객체에 태그(라벨)을 달아놓는 습관 중요, 타입검사할떄 편함

# 객체들간 구별할때 2가지 방식을 사용
(1)
const human = { type: &#39;human&#39; };

(2)
const human = { talk()};
if (&#39;talk&#39; in a) {
}</code></pre>
<h3 id="커스텀-타입-가드--is-형식-조건자-">커스텀 타입 가드 ( is, 형식 조건자 )</h3>
<ul>
<li>type을 구분해주는 커스텀 함수를 직점 만들 수 있음<pre><code class="language-ts"># 리턴값에 is가 들어가있으면 커스텀 타입가드 함수이다
function catOrDog(a: Cat | Dog): a is Dog { 
# 타입 판별을 직접 만들기 ( a 에 meow라는 속성이있으면 false, 그렇지 않으면 true )
if ((a as Cat).meow) { return false } 
return true;
}
</code></pre>
</li>
</ul>
<p>const cat: Cat | Dog = { meow: 3 }
if (catOrDog(cat)) {
    console.log(cat.meow);
}
if (&#39;meow&#39; in cat) {
    console.log(cat.meow);
}</p>
<pre><code>- 타입스크립트가 타입 추론을 잘못 할 때 custom type guard를 만들어서 정확한 type을 추론할 수 있음
``` ts
const isRejected = (input: PromiseSettledResult&lt;unknown&gt;): 
input is PromiseRejectedResult =&gt; input.status === &#39;rejected&#39;;

const isFulfilled = &lt;T&gt;(input: PromiseSettledResult&lt;T&gt;): 
input is PromiseFulfilledResult&lt;T&gt; =&gt; input.status === &#39;fulfilled&#39;;

const promises = await Promise.allSettled
([Promise.resolve(&#39;a&#39;), Promise.resolve(&#39;b&#39;)]);
const errors = promises.filter(isRejected);</code></pre><h3 id="-와-object">{} 와 Object</h3>
<ul>
<li>v. 4.8 이상 새로운 타입 -&gt; {}, object<pre><code class="language-ts"># {}, Object: 모든 타입(null, undefined 제외)
빈객체 {}
const x: {} = &#39;hello&#39;; 
</code></pre>
</li>
</ul>
<h1 id="대문자-object">대문자 Object</h1>
<p>const y: Object = &#39;hi&#39;; </p>
<h1 id="객체--객체-지양-대신-interface-type-class-사용하기">객체 ( 객체 지양, 대신 interface, type, class 사용하기)</h1>
<p>const xx: object = &#39;hi&#39;; 
const yy: object = { hello: &#39;world&#39; }; </p>
<p>const z: unknown = &#39;h1&#39;;</p>
<h1 id="버전-48-unknown--모든값을-다-받을수있음--unknown----null--undefined">버전 4.8 unknown : 모든값을 다 받을수있음  unknown = {} | null | undefined</h1>
<p>// 과거에는 unknown인 변수를 if문안에 넣으면 그대로 unknown이 나옴
// 4.8 버전부터는 unknown인 변수를 if문안에 넣으면 unknown = {} | null | undefined </p>
<p>if (z) {
  z; // {}
} else {
  z; // null | undefined 여기에 속함
}</p>
<pre><code>### readonly, Index Signature, Mapped type
- readonly
``` ts
interface A {
  readonly a: string;
  b: string;
}

const aaaa: A = {a: &#39;hello&#39;, b: &#39;world&#39;};

# 에러 -&gt; 읽기 전용 속성이므로 &#39;a&#39;에 할당할 수 없습니다.
aaaa.a = &#39;123&#39;; </code></pre><ul>
<li>Index Signature<pre><code class="language-ts"># 어떤 키든 전부다 같은 속성으로 지정가능 
type A = { [key: string]: string }; 
const aaaa: A = { a: &#39;hello&#39;, b: &#39;world&#39; };</code></pre>
</li>
<li>Mapped type<pre><code class="language-ts">type B = &#39;Human&#39; | &#39;Mammal&#39; | &#39;Animal&#39;;
</code></pre>
</li>
</ul>
<h1 id="key-가-b의-3중-하나-키-줄이기">key 가 B의 3중 하나 (키 줄이기)</h1>
<p>type A = { [key in B]: number }; 
const aaaa: A = { Human: 123, Mammal: 5, Animal: 7 };</p>
<p>type A = { [key in B]: B }; 
const aaaa: A = { Human: &#39;Animal&#39;, Mammal: &#39;Human&#39;, Animal: &#39;Mammal&#39; };</p>
<pre><code>### class의 새로운 기능들
``` ts
class A {
  a: string;
  b: number;
  constructor(a: string, b: number = 123) {
    this.a = a;
    this.b = b;
  }
  method() {

  }
}
# 2번쨰 인수의 기본값이 이미 지정되어있기떄문에 필수아님 (2번쨰 인수 지정하면 덮어씀)
const a = new A(&#39;123&#39;); </code></pre><p><strong>constructor</strong> : 생성자의 매개변수를 받을 수 있음</p>
<ul>
<li>class의 이름은 그 자체로 타입이 될 수 있음. 하지만, new A() -&gt; instance를 나타냄</li>
<li>class 타입 typeof<pre><code class="language-ts">type AA = A;
const a: A = new A(&#39;123&#39;);
const b: typeof A = A;</code></pre>
</li>
<li>class 추가된 문법<pre><code class="language-ts"># implements, private, protected 타입스크립트에 있는 키워드
# class 구현, interface 추상 
# class는 interface를 따라야함 -&gt; a, b속성
</code></pre>
</li>
</ul>
<p>interface A {
  readonly a: string;
  b: string;
}</p>
<p>class B implements A { </p>
<h1 id="class의-모양을-interface에서-통제가능">class의 모양을 interface에서 통제가능</h1>
<p>  a: string = &#39;123&#39;; 
  b: string = &#39;world&#39;;
}
class C extends B {}
new C().a;
new C().b;</p>
<h1 id="자바스크립트로-바꿧을떄">자바스크립트로 바꿧을떄</h1>
<p>class B{
  constructor() {
    this.a = &#39;123&#39;;
    this.b = &#39;world&#39;;
  }
}
class C extends B {</p>
<p>}
new C().a;
new C().b;</p>
<pre><code>
------------

```ts
interface A {
  readonly a: string;
  b: string;
}
# private, protected : 객체지향에서 사용
class B implements A { 
   private a: string = &#39;123&#39;; 
  protected b: string = &#39;world&#39;;
  c: string = &#39;wow&#39;; 

  method() {
    console.log(this.a);  // o
    console.log(this.b);  // o
    console.log(this.c);  // o
  }
}
class C extends B {
  console.log(this.a); // x
  console.log(this.b); // o
  console.log(this.c); // o


}
# instance
new C().a; // x
new C().b; // x
new C().c; // o
</code></pre><h3 id="옵셔널-제네릭-기본">옵셔널, 제네릭 기본</h3>
<ul>
<li><p>optional : 속성뒤에 위치 -&gt; ?</p>
<pre><code class="language-ts">// abc(...args: number[]) -&gt; 전부다 받고싶을떄 이거씀
function abc(a: number, b?: number, c: number?) {}
abc(1) // o
abc(1, 2) // o
abc(1, 2, 3) // o

let obj: { a: string, b?: string }  = { a: &#39;hello&#39;, b: &#39;world&#39; } // b 필수 x
obj = { a: &#39;hello&#39; };
</code></pre>
</li>
</ul>
<pre><code>- Generic : 타입이 뭔지 모를 때 사용, 사용할 때 타입이 정해짐
```ts
# 같은 타입은 같은 알파벳으로 지정
function add&lt;T&gt;(x: T, y: T): T { return x + y }

add&lt;number&gt;(1, 2);
add(1, 2);
add&lt;string&gt;(&#39;1&#39;, &#39;2&#39;);
add(&#39;1&#39;, &#39;2&#39;);
add(1, &#39;2&#39;);

# 타입에 제한을 두고싶을떄 -&gt; T에 extends를 붙이면 T가 제한됨
function add&lt;T extends string&gt;(x: T, y: T): T { return x + y } 
function add&lt;T extends string | number&gt;(x: T, y: T): T { return x + y } 
function add&lt;T extends number, K extends string&gt;(x: T, y: K): T { return x + y } 


function add&lt;T extends {a: string}&gt;(x: T): T {return x};
add({a: &#39;hello&#39;})

function add&lt;T extends string[]&gt;(x: T): T {return x};
add([&#39;1&#39;, &#39;2&#39;, &#39;3&#39;])

콜백함수 형태를 제한
function add&lt;T extends (a: string) =&gt; number&gt;(x: T): T { return x};
add((a) =&gt; +a)

// &lt;T extends {...}&gt; // 특정 객체
// &lt;T extends any[]&gt; // 모든 배열
// &lt;T extends (...args: any) =&gt; any&gt; // 모든 함수
// &lt;T extends abstract new (...args: any) =&gt; any&gt; // 생성자 타입
// &lt;T extends keyof any&gt; // string | number | symbol</code></pre><h3 id="기본값-타이핑">기본값 타이핑</h3>
<pre><code class="language-ts">const a = (b = 3, c = 5) =&gt; { // const a = (기본값) =&gt; {
  return &#39;3&#39;;
}

const a = (b: number = 3, c: number =5) =&gt; { 
  return &#39;3&#39;;
}

# 매개변수의 기본값 객체
const a = (b: { children: string } = {children: &#39;zerocho&#39;}) =&gt; { 

}

# 리액트 .jsx에서 &lt;T&gt; 사용시 에러가 날수있으니, 기본값 | extend  설정해주기
const add = &lt;T = unknown&gt;(x: T, y: T): T =&gt; { { x, y} }; 
const result - add(1,2);</code></pre>
<blockquote>
<p>참고자료 : <a href="https://github.com/ZeroCho/ts-all-in-one">제로초 ts-all-in-one</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TypeScript] 기본 세팅]]></title>
            <link>https://velog.io/@taek_jini/TypeScript-%EA%B8%B0%EB%B3%B8-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@taek_jini/TypeScript-%EA%B8%B0%EB%B3%B8-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Wed, 26 Jul 2023 04:48:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/taek_jini/post/e86f44b3-e249-4e12-9d24-73e9c88d557b/image.png" alt=""></p>
<blockquote>
<p>이 포스트를 시작으로 타입스크립트에 대해 공부하며 그에 대한 내용을 기록할 예정입니다. 
문법을 중점으로 공부하기보다 다른 TS 예제들을 잘 분석할 수 있는 능력을 기르고 나아가 기존 리액트 프로젝트를 TS로 리팩토링해보는게 목표입니다.</p>
</blockquote>
<p>모든 내용은 타입스크립트 공식 문서에 내장되어 있습니다.</p>
<h2 id="기본-지식--제로초-git-참고-">기본 지식 ( 제로초 git 참고 )</h2>
<ul>
<li>메인 룰: TypeScript는 최종적으로 JavaScript로 변환된다. 순전한 TypeScript 코드를 돌릴 수 있는 것은 deno이나 대중화되지가 않았음. 브라우저, 노드는 모두 js 파일을 실행한다.</li>
<li>TypeScript는 언어이자 컴파일러(tsc)이다. 컴파일러는 ts 코드를 js로 바꿔준다.</li>
<li>tsc는 tsconfig.json(tsc --init 시 생성)에 따라 ts 코드를 js(tsc 시 생성)로 바꿔준다. 인풋인 ts와 아웃풋인 js 모두에 영향을 끼치므로 tsconfig.json 설정을 반드시 봐야한다.</li>
<li>단순히 타입 검사만 하고싶다면 <code>tsc --noEmit</code> 하면 된다. </li>
<li>개인 의견: tsconfig.json에서 <code>esModuleInterop: true</code>, <code>strict: true</code> 두 개만 주로 켜놓는 편. <strong><code>strict: true</code></strong>가 핵심임.</li>
<li>ts 파일을 실행하는 게 아니라 결과물인 js를 실행해야 한다.</li>
<li>Vscode같은 에디터에선 <code>tsc</code> 커맨드만 실행하면 ts -&gt; js로 변환해준다.</li>
</ul>
<h2 id="타입스크립트-프로젝트-생성">타입스크립트 프로젝트 생성</h2>
<p>타입스크립트도 npm 패키지이기 때문에 node 프로젝트를 먼저 생성해야 한다.</p>
<ul>
<li>생성할 디렉토리 안에서 <code>npm init -y</code> 를 통해 프로젝트를 설정</li>
<li><code>npm i typescript</code>로 타입스크립트를 설치</li>
<li>타입스크립트를 전역에 설치하지 않았기 때문에 <code>npx</code>를 붙여서 <code>tsc</code>커맨드를 사용해야 한다.</li>
<li><code>npx tsc --init</code> 위 커맨드를 실행하여 tsconfig.json 파일을 생성</li>
</ul>
<h2 id="tsconfigjson">tsconfig.json</h2>
<p>타입스크립트를 자바스크립트로 컴파일하기 위한 옵션들을 적은 파일이다.
주요 옵션들에 대해서만 간단히 적고 마침 !</p>
<ul>
<li><p><code>strict</code> : 타입 체크 여부, 이 옵션을 true로 하지 않으면 타입스크립트를 사용하는 의미가 X</p>
</li>
<li><p><code>allowJS</code> : 자바스크립트 코드 허용 여부, js를 ts로 옮기는 중일 때 켜두면 좋음</p>
</li>
<li><p><code>module</code> : 어떤 모듈 시스템을 사용할 것인지, ex) CommonJS, ES2020 등</p>
</li>
<li><p><code>forceConsistentCasingInFileNames</code> : 임포트한 파일명의 대소문자 구분, true로 하자.</p>
</li>
<li><p><code>skipLibCheck</code> : 패키지 타입 체크 여부, true로 하면 실제로 사용하는 패키지의 타입만 체크</p>
</li>
<li><p><code>esModuleInterop</code> : ES6 모듈 사양을 준수하여 CommonJS 모듈 임포트, true로 하면 좋음</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[원티드 프리온보딩 챌린지] 리액트 리팩토링 실전가이드: 테스트부터 최적화까지-사전 과제]]></title>
            <link>https://velog.io/@taek_jini/%EC%9B%90%ED%8B%B0%EB%93%9C-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%B1%8C%EB%A6%B0%EC%A7%80-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%8B%A4%EC%A0%84%EA%B0%80%EC%9D%B4%EB%93%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%B6%80%ED%84%B0-%EC%B5%9C%EC%A0%81%ED%99%94%EA%B9%8C%EC%A7%80-%EC%82%AC%EC%A0%84-%EA%B3%BC%EC%A0%9C</link>
            <guid>https://velog.io/@taek_jini/%EC%9B%90%ED%8B%B0%EB%93%9C-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%B1%8C%EB%A6%B0%EC%A7%80-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%8B%A4%EC%A0%84%EA%B0%80%EC%9D%B4%EB%93%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%B6%80%ED%84%B0-%EC%B5%9C%EC%A0%81%ED%99%94%EA%B9%8C%EC%A7%80-%EC%82%AC%EC%A0%84-%EA%B3%BC%EC%A0%9C</guid>
            <pubDate>Tue, 25 Jul 2023 04:13:13 GMT</pubDate>
            <description><![CDATA[<h1 id="테스트">테스트</h1>
<hr>
<blockquote>
<p><strong>유닛테스트 vs 통합테스트 vs E2E테스트를 비교하여 설명해주세요</strong></p>
</blockquote>
<ol>
<li><p>유닛테스트 </p>
<ul>
<li>개별 코드 유닛을 테스트하는 것으로, 코드의 가장 작은 단위를 테스트합니다. 주로 함수, 메서드 또는 모듈 단위로 테스트를 수행합니다.</li>
<li>의존성을 최소화하여 각각의 코드 단위를 격리하여 테스트하고, 다른 외부 요소들 ( DB,네트워크 ) 가 포함되지 않아야 한다.</li>
<li>통합 테스트, E2E 테스트와 비교하자면 가장 작은 단위로 테스트 하기 때문에 테스트의 양이 가장 적고 효율적이다. 다만 프로젝트 구조가 명확하지 않고, 단일 유닛의 기능이 자주 변경된다면 테스트 효율이 떨어질 수 있다.
 ** * 의존성(Dependencides): 여러가지 의미를 가지고 있지만 보통 한 모듈이 어떤 용도에 의해 다른 모듈에 불러와져서 사용되는 것을 의미**</li>
</ul>
</li>
<li><p>통합테스트</p>
</li>
</ol>
<ul>
<li>두 개 이상의 모듈이 실제로 연결되는 상태를 확인해보는 테스트</li>
<li>모듈 간 연결을 통해 발생하는 오류를 검증</li>
<li>유닛 테스트에 비해 광범위하게 테스트할 수 있어서 리팩토링에 쉽게 깨지지 않는게 장점</li>
</ul>
<ol start="3">
<li>E2E테스트</li>
</ol>
<ul>
<li>End-to-End 테스트는 프로덕트를 사용자 관점에서 테스트 하는 방법</li>
<li>DB,네트워크 등 여러 인프라가 갖추어진 상태에서 테스트</li>
<li>유저의 입장을 예상해서 시나리오를 작성하며 그에 맞춘 테스트를 진행하기 때문에, 전체 테스트 시간이 비교적 오래 걸리고 많은 테스트가 필요</li>
<li>다양한 앱의 의존관계가 정확히 작동하는 지 확인하며, 다양한 시스템 컴포넌트에 정확한 정보가 전달되는지 체크할 수 있음</li>
</ul>
<blockquote>
<p><strong>리액트 테스트에 사용되는 도구들을 비교하여 설명해주세요</strong></p>
</blockquote>
<ol>
<li>Jest</li>
</ol>
<ul>
<li>Meta에서 만들어진 JavaScript 기반의 테스트용 프레임워크</li>
<li>프론트엔드 뿐만 아니라 node 환경에서 테스팅 가능</li>
<li>모킹, 스냅샷 테스팅, 코드 커버리지 등 다양한 기능을 내장하고 있어 많은 개발자들이 사용</li>
</ul>
<ol start="2">
<li>React Testing Library (RTL)</li>
</ol>
<ul>
<li>RTL은 엄밀히 따지자면 test runner는 아니지만 test runner를 위한 공간(virtual DOM)을 제공</li>
<li>테스트가 필요한 react 컴포넌트를 렌더링하는 역할</li>
<li>세부적인 구현보다 실제 사용자가 사용하는 부분만 테스트하여 잘 깨지지 않는 테스트를 만듦</li>
</ul>
<h1 id="최적화">최적화</h1>
<hr>
<blockquote>
<p><strong>CDN(Content Distributed Network)에 대해 설명해주세요</strong></p>
</blockquote>
<ul>
<li><p>콘텐츠를 효율적으로 전달하기 위해 여러 노드를 가진 네트워크에 데이터를 저장하여 제공하는 시스템 또는 여러 장소에 걸쳐 분산된 서버들의 그룹</p>
</li>
<li><p>CDN은 서버와 사용자 사이의 물리적인 거리를 줄여 콘텐츠 로딩에 소요되는 시간을 최소화한다. 그리고 각 지역에 캐시 서버를 분산 배치해, 근접한 사용자의 요청에 원본 서버가 아닌 캐시 서버가 콘텐츠를 전달함</p>
</li>
<li><p>CDN은 Bootstrap,jQuery와 같은 라이브러리들의 Stylesheet 나 JavaScript 파일(정적 asset)을 전송하는데 널리 쓰인다.</p>
</li>
<li><p>CDN의 장점</p>
<ul>
<li>인터넷 서비스 사용자(ISP/Internet Service Provider)에 직접 연결되어 데이터를 전송하기 때문에 콘텐츠 병목 현상(서버의 과부하)를 방지</li>
<li>사용자가 거주하는 곳과 가까운 곳에 설치되기 때문에 물리적 거리로 인한 속도저하를 막음</li>
<li>CDN 서비스는 검증받은 최신 인증 방식을 채택하기 때문에 해당 서비스를 이용하는 것만으로도 보안등급이 올라감</li>
</ul>
</li>
<li><p>CDN의 단점</p>
<ul>
<li>Cache 서버가 많이 없거나 성능이 안정적이지 않으면  CDN 서버에 문제가 발생했을 때 데이터를 불러올 수 없음.(*SPOF/ 단일 장애점 문제 : 한 지점이 중단되면 전체 시스템이 중단되는 현상)</li>
<li>특정 국가나 지역만을 타깃으로 웹 서비스를 운영한다면 CDN 서비스를 활용할 필요가 없다. 오히려 불필요한 연결 지점(노드)만 늘어나서 웹 서비스 성능 저하의 원인이 될 수 있음.</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>Web Vitals에 대해 설명해주세요</strong></p>
</blockquote>
<ul>
<li>2021년 5월 구글이 SEO를 향상시키기 위해 발표한 웹 페이지 품질 수치 기준. 구글에서는 하단에 설명할 3가지 요소를 합쳐서 &#39;Core Web Vitals&#39;라고 언급</li>
<li>크게 로딩 시간(loading performance), 상호작용(interactivity), 시각적 안정성(visual stability) 이 <strong>3가지 요소</strong>로 나뉜다.</li>
<li>로딩 시간: Largest Contentful Paint(최대 콘텐츠풀 페인트, <strong>LCP</strong>)/사용자가 URL을 요청한 시점부터 표시 영역의 가장 큰 시각 콘텐츠 요소를 렌더링을 마치는 데 걸리는 시간을 의미한다. 좋은 사용자 경험을 제공하려면 웹 페이지를 처음 로딩할 때 2.5초 이내에 LCP가 발생해야 한다.</li>
<li>상호작용: First Input Delay(최초 입력 지연, <strong>FID</strong>)/웹 페이지 사용자가 상호 작용을 했을 때 다음 행동을 하는 데 걸리는 시간을 의미한다. 단위는 ms(밀리세컨드)를 사용하며 100ms 이하는 좋음, 300ms 이하는 개선 필요, 300ms 초과는 상태가 나쁘다고 판단한다.</li>
<li>시각적 안정성: Cumulative Layout Shift(누적 레이아웃 시프트, <strong>CLS</strong>)/웹 페이지가 로딩하는 단계에서 레이아웃이 변경되는 정도를 의미한다.최적의 사용자 경험을 제공하려면 0.1 이하가 나타나야 하며 0.25 이하는 개선 필요, 0.25 초과는 나쁜 상태라고 볼 수 있다.</li>
</ul>
<blockquote>
<p><strong>Lighthouse에 대해 설명해주세요</strong></p>
</blockquote>
<ul>
<li>구글이 웹 페이지의 품질을 개선시키기 위해 만든 오픈 소스 기반의 퍼포먼스 측정 도구</li>
<li>Chrome 브라우저 내 개발자도구(Developer Tools)에 내장되어 있어서 별도로 설치할 필요없이 단축키 F12나 &#39;오른쪽 마우스 버튼 클릭-&quot;검사&quot; 항목 클릭-개발자 도구-Lighthouse 탭&#39;을 통해 이용 가능</li>
<li>웹 페이지의 성능을 측정할 때 사용되는 기준<ul>
<li><strong>Performance</strong>: 웹 페이지의 로딩 속도 등 실제 성능 측정</li>
<li><strong>Progressive Web App</strong>: PWA로 부르며, 웹과 네이티브 앱(모바일 전용 앱)의 기능 모두의 이점을 가지도록 만들어진 서비스인지 체크</li>
<li><strong>Best Practices</strong>: Best Practices를 따라 개발되었는지 체크</li>
<li><strong>Accessibility</strong>: 접근성/보통 폰트 사이즈, 메뉴간 간격 등을 측정한다.</li>
<li><strong>SEO</strong>: Search Engine Optimization/검색 엔진 수집 최적화</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] EC2 프리티어 환경에서 마주할 수 있는 이슈 ( 메모리 부족 )]]></title>
            <link>https://velog.io/@taek_jini/AWS-EC2-%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%A7%88%EC%A3%BC%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%9D%B4%EC%8A%88-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%B6%80%EC%A1%B1</link>
            <guid>https://velog.io/@taek_jini/AWS-EC2-%ED%94%84%EB%A6%AC%ED%8B%B0%EC%96%B4-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%A7%88%EC%A3%BC%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%9D%B4%EC%8A%88-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%B6%80%EC%A1%B1</guid>
            <pubDate>Fri, 14 Jul 2023 14:09:53 GMT</pubDate>
            <description><![CDATA[<p><code>npm run build</code> 시에 메모리 부족으로 build를 실패했다.</p>
<blockquote>
<p><strong>프리티어의 가장 큰 단점은 RAM이 1G 라는 것인데 그게 뭔 상관일까 ?</strong></p>
</blockquote>
<ul>
<li>서버 메모리가 부족해서 빌드하다가 메모리에 과부하가 걸림</li>
<li><blockquote>
<p>RAM은 실행중인 픅로그램의 속도와 실행에 관련이 있다. 
만약 프로그램이 잡아먹는 RAM이 컴퓨터(우분투 환경)의 RAM 크기를 초과해버린다면 프로그램이 느려지거나, 실행이 안될 수 있다.</p>
</blockquote>
</li>
</ul>
<p>그렇다면 해결방법은 ? </p>
<ul>
<li>package.json에서 빌드 명령어 변경</li>
<li>Swap file을 사용하여 Amazon EC2 인스턴스에서 스왑 공간으로 사용할 메모리를 할당</li>
</ul>
<h3 id="packagejson에서-빌드-명령어-변경">package.json에서 빌드 명령어 변경</h3>
<p>프로젝트의 빌드 명령어를 변경해주는 것이다.
예시로 들자면 이런식이다.</p>
<p>*<em>before *</em></p>
<pre><code class="language-js">{...
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;react-scripts start&quot;,
    &quot;build&quot;: &quot;react-scripts build&quot;,
    &quot;test&quot;: &quot;react-scripts test&quot;,
    &quot;eject&quot;: &quot;react-scripts eject&quot;
  },
...
}</code></pre>
<p>** after**</p>
<pre><code class="language-js">{...
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;react-scripts start&quot;,
    //&quot;build&quot;: &quot;set \&quot;GENERATE_SOURCEMAP=false\&quot; &amp;&amp; react-scripts build&quot;,// 윈도우
      &quot;build&quot; : &quot;GENERATE_SOURCEMAP=false react-scripts build&quot;,// 리눅스
    &quot;test&quot;: &quot;react-scripts test&quot;,
    &quot;eject&quot;: &quot;react-scripts eject&quot;
  },
...
}</code></pre>
<p><code>GENERATE_SOURCEMAP=false</code> 코드를 추가했다. 
빌드 후에 빌드 파일에 대한 소스 코드를 확인할 수 있도록 해주는 코드다.</p>
<p>default 값이 true 라서 이를 false로 바꿔준  것.</p>
<blockquote>
<p><strong>true</strong>면 뭐가 어떻고 <strong>false</strong>면 뭐가 어떤데 ?</p>
</blockquote>
<ul>
<li>true 라면 버그가 발생했을 때 빌드파일을 통해 소스 코드를 확인할 수 있기 때문에 어떤 문제가 있는 확인할 수 있다.<ul>
<li>그렇다는건 디버깅 정보를 포함하여 빌드하기 때문에 빌드 용량이 커지며, 메모리 부족을 초래한다.</li>
</ul>
</li>
<li>false면 빌드 용량이 작아지고 메모리를 덜 사용하게 된다.</li>
</ul>
<h3 id="swap-file을-사용하여-amazon-ec2-인스턴스에서-스왑-공간으로-사용할-메모리를-할당">Swap file을 사용하여 Amazon EC2 인스턴스에서 스왑 공간으로 사용할 메모리를 할당</h3>
<p>이상한 단어가 껴있어서 거부감이 들 수 있지만 메모리 부족 현상이 있을 때 디스크의 램을 끌어와 사용한다는 뜻이다.</p>
<blockquote>
<p>aws 공식 문서에도 자세히 설명이 되어 있다. <a href="https://repost.aws/ko/knowledge-center/ec2-memory-swap-file">바로가기</a></p>
</blockquote>
<p>차근차근 따라가보자</p>
<ul>
<li>일단 dd 명령어를 통해 swap 메모리를 할당
<code>sudo dd if=/dev/zero of=/swapfile bs=128M count=16</code><blockquote>
<p>128씩 16개의 공간을 만드는 것이여서 우리의 경우 count를 16으로 할당하는 것이 좋다. 즉, 2GB정도 차지하는 것이다.</p>
</blockquote>
</li>
</ul>
<ul>
<li><p>스왑 파일에 대한 읽기 및 쓰기 권한을 업데이트
<code>$ sudo chmod 600 /swapfile</code>
 </p>
</li>
<li><p>Linux 스왑 영역을 설정
<code>$ sudo mkswap /swapfile</code></p>
</li>
</ul>
<ul>
<li><p>스왑 공간에 스왑 파일을 추가하여 스왑 파일을 즉시 사용할 수 있도록 만듦
<code>$ sudo swapon /swapfile</code>
 </p>
</li>
<li><p>절차가 성공했는지 확인
<code>$ sudo swapon -s</code>
 </p>
</li>
<li><p>/etc/fstab 파일을 편집하여 부팅 시 스왑 파일을 활성화
편집기에서 파일을 엽니다.
<code>$ sudo vi /etc/fstab</code></p>
</li>
<li><p>파일 끝에 다음 줄을 새로 추가하고 파일을 저장한 다음 종료
<code>/swapfile swap swap defaults 0 0</code>
 </p>
</li>
</ul>
<p> </p>
<p>다음과 같이 적용됬는지 확인 !</p>
<pre><code>total       used       free     shared    buffers     cached
Mem:       1009136     941624      67512          4      11696      96836
-/+ buffers/cache:     833092     176044
Swap:      2097148     304972    1792176</code></pre><p>이렇게 하면 <code>build</code>가 되긴 된다. 비록 HDD에 할당하기 때문에, RAM에 비해 속도는 현저히 낮아지지만 가난한 우리에겐 한줄기 빛일 뿐 ㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] 프론트 서버를 배포해보자]]></title>
            <link>https://velog.io/@taek_jini/AWS-%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%84%9C%EB%B2%84%EB%A5%BC-%EB%B0%B0%ED%8F%AC%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@taek_jini/AWS-%ED%94%84%EB%A1%A0%ED%8A%B8-%EC%84%9C%EB%B2%84%EB%A5%BC-%EB%B0%B0%ED%8F%AC%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Fri, 14 Jul 2023 13:42:24 GMT</pubDate>
            <description><![CDATA[<p>지난 시간에 인스턴스 생성과 Route53 설정까지 마쳤으니 ( <del>사실 호다닥 넘어감</del> ) 프론트 서버 배포를 진행해보려 한다.</p>
<ol>
<li>ec2 인스턴스에 들어가서 ssh 클라이언트 탭에 있는 예시를 복사해준다.<blockquote>
<p>ssh -i &quot;pem 파일명&quot; ubuntu@<del>~</del>.ap-northeast-2.compute.amazonaws.com</p>
</blockquote>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/1788f159-40f8-4d6f-a95a-746f2423f868/image.png" alt=""></p>
<p>pem키가 있는 폴더 ( ** 절대 공유되면 안돼 !!!!!!!!! ** ) 에서 붙여넣기를 해주면 끝
<img src="https://velog.velcdn.com/images/taek_jini/post/b7d51057-e7e6-4bec-815e-ddac1b3b4790/image.png" alt=""></p>
<p>우분투 가상 환경에 들어왔다면 아래의 목록을 설치해줘야 한다. (처음엔 깡통임)</p>
<ul>
<li>npm</li>
<li>node.js</li>
<li>git</li>
<li>git clone <code>배포할 프로젝트</code></li>
</ul>
<p><strong>업데이트</strong>
우선 다음 명령어로 update를 진행줘야한다.
<code>sudo apt update</code></p>
<p><strong>npm 설치를 위한 명령어</strong>
다음으로 npm을 설치해준다.
<code>sudo apt install npm</code></p>
<p><strong>node.js 설치를 위한 명령어</strong>
다음으로 node.js을 설치해준다.
<code>sudo apt install nodejs</code></p>
<p><strong>git 설치를 위한 명령어</strong>
프로젝트를 가져오기 위해 git을 설치해준다.
<code>sudo apt install git</code></p>
<p><strong>github에 등록된 나의 프로젝트 가져오기</strong>
git clone <code>배포할 프로젝트의 git 주소</code></p>
<p>여기까지 왔다면 프로젝트에 필요한 패키지들을 설치해줘야한다.</p>
<p>우선 프로젝트로 경로를 바꿔줘야 하니 <code>ls</code> 명령어를 통해 현 디렉토리 위치를 파악 후 이동해준다.
<img src="https://velog.velcdn.com/images/taek_jini/post/98f3e63c-53f0-414f-b64c-1cbddfb18fb4/image.png" alt="">
들어왔다면 <code>npm i</code>로 라이브러리 패키지들을 설치 해 주고 <code>npm run build</code> 로 빌드해준다 !</p>
<h3 id="pm2-쓰는-이유">pm2 쓰는 이유</h3>
<p> pm2란? 
 production process manager for node.js 이다.
 즉 , node.js 를통해서 만든 프로그램을 관리해주는 프로그램</p>
<blockquote>
<p><strong>프로세스 관리가 필요한 이유는 ?</strong>
 우리가 ssh 프로그램으로 EC2에 접속한 터미널을 강제 종료한다면 로컬에 띄워져있던 ssh 프로세스가 강제 종료되고, EC2 프로세스도 같이 종료될 거다. 즉, EC2 서버도 종료된다. (?!)
 그 이유는 node.js는 기본적으로 foreground process 형태로 실행되기 때문인데 이를 background process로 바꿔주는게 pm2의 가장 큰 역할이다.</p>
</blockquote>
<p> 그렇다면 <code>sudo npm i pm2</code> 를 통해 pm2 를 설치해주고 사용하면 된당</p>
<p> 처음 킬 땐 <code>sudo npx pm2 start npm -- start</code> 명령어로 실행시켜주며
 켜져있는 상황에서 수정사항을 서버에 반영하고 싶다면 <code>sudo npx pm2 reload all</code>을 해주면 된다.</p>
<p> 자주사용하는 pm2 명령어로는 
 서버가 잘 돌아가고 있는지 모니터링하고싶을때 - &gt; <code>sudo npx pm2 monit</code>
 서버가 잘 켜져있는건지 확인하고 싶을 때 -&gt; <code>sudo npx pm2 list</code></p>
<blockquote>
<p>프로젝트 배포 후 꽤 지난 지금 포스팅을 하려다보니 놓친 부분이 많습니다. ( 인스턴스 인바운드 규칙이라던가.. ) 그렇기에 자세한 내용은 참고자료에 있는 링크를 통해 공부해주시길 바랍니다 ㅠ</p>
</blockquote>
<p> 참고자료
 <a href="https://velog.io/@kcj_dev96/%ED%94%84%EB%A1%A0%ED%8A%B8-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EB%B2%95-1%ED%8E%B8AWS-EC2-with-React-Express-%ED%99%98%EA%B2%BD">프론트 배포 방법 1편</a></p>
<p> <a href="https://velog.io/@cjy0029/AWS-EC2-%ED%94%84%EB%A1%A0%ED%8A%B8-%EB%B0%B0%ED%8F%AC-%EC%9D%B4%EB%A0%87%EA%B2%8C%EB%A7%8C-%ED%95%B4%EB%B3%B4%EC%9E%90#4-route53">AWS EC2 프론트 배포 이렇게만 해보자.</a></p>
<p> <a href="https://chat.openai.com/">https://chat.openai.com/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AWS] 배포 방법을 알아보자]]></title>
            <link>https://velog.io/@taek_jini/AWS-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EB%B2%95%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@taek_jini/AWS-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EB%B2%95%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Fri, 14 Jul 2023 12:47:48 GMT</pubDate>
            <description><![CDATA[<p>AWS로 서비스를 배포하는 방법에는 크게 4가지가 있다.</p>
<h4 id="1-aws-ec2">1. AWS EC2</h4>
<ul>
<li>웹 서버, WAS 서버 배포 가능</li>
</ul>
<h4 id="2-aws-s3와-cloudfront">2. AWS S3와 CloudFront</h4>
<ul>
<li>정적 콘텐츠(프론트 서버) 배포<blockquote>
<h5 id="s3와-cloudefront-란">S3와 CloudeFront 란?</h5>
<ul>
<li>S3(Simple Storage Service)는 최고의 확장성의 데이터 가용성, 보안 및 성능을 제공하는 객체 스토리지 서비스이며 데이터를 버킷 내의 객체(해당 파일을 설명하는 모든 메타데이터)로 저장하는 객체 스토리지 서비스</li>
<li>ClodeFront(CDN) 는 <code>.html</code>,<code>.css</code>,<code>.js</code>및 이미지 파일과 같은 정적 및 동적 웹 콘텐츠를 사용자에게 더 빨리 배포하도록 지원하는 웹 서비스</li>
<li>CDN(Content Delivery Network or Content Distribution Network) 는 콘텐츠를 효율적으로 전달하기 위해 여러 노드를 가진 네트워크에 데이터를 저장해서 제공하는 시스템</li>
</ul>
</blockquote>
</li>
</ul>
<ol start="3">
<li>AWS amplify</li>
</ol>
<ul>
<li>정적 콘텐츠 (프론트 서버) 배포</li>
</ul>
<ol start="4">
<li>Nginx</li>
</ol>
<ul>
<li>정적 콘텐츠 (프론트 서버) 배포</li>
<li>S3 + CloudFront 방법보다 간편하게 배포가 가능하며 배포 자동화 ( 자동으로 S3+CloudFront  구현 )</li>
</ul>
<p>이 중에 EC2를 활용한 배포에 대해 설명하려고 한다.</p>
<h4 id="인스턴스-생성-및-유형-선택과-키-발급은-정리가-잘-된-블로그들이-많으니-패스-"><del>인스턴스 생성 및 유형 선택과 키 발급은 정리가 잘 된 블로그들이 많으니 패스 ~!</del></h4>
<p><img src="https://velog.velcdn.com/images/taek_jini/post/95a31e36-2273-4616-8f6f-2d0359d8a67e/image.png" alt=""></p>
<p>왜 사람들은 인스턴스 OS를 우분투로 선택할까 ? </p>
<ul>
<li>가장 많이 사용되는 리눅스 배포판 중 하나</li>
<li>서버 운영에 최적화</li>
<li>특정 플랫폼에 종속되지 않고 보편적으로 사용 가능 </li>
<li><em>ex) aws에서 만든 리눅스 패키지를 사용할 때 구글 클라우드로 플랫폼을 옮긴다면 패키지 명령어를 새로 익혀야함.*</em></li>
</ul>
<h3 id="route-53">Route 53</h3>
<ul>
<li>클라우드 기반 DNS(Domain Name System) 웹 서비스</li>
<li>사용자의 요청을 EC2, ELB, S3 버킷 등 인프라로 직접 연결 가능</li>
</ul>
<blockquote>
<p>*<em>만약 <code>naver.com</code> 라는 도메인에 들어간다면? *</em> </p>
</blockquote>
<ol>
<li>도메인(naver.com)이 가지고 있는 네임서버에 접속</li>
<li>네임서버에 접속한 도메인(naver.com)과 연결된 IP 정보를 확인</li>
<li>네임서버에서 도메인(naver.com)과 연결된 IP를 전달</li>
<li>네임서버에서 전달한 서버의 IP 주소로 접속</li>
<li>서버의 IP로 연결된 브라우저에 서버의 내용을 출력</li>
</ol>
<p>도메인을 Route53이 아닌 외부(가비아 등등) 에서 구매했다면
<img src="https://velog.velcdn.com/images/taek_jini/post/2c605c3b-82d1-42f3-b2ae-298ab3eb4274/image.png" alt=""></p>
<p>도메인 등록 후 파란 박스( <strong>네임서버</strong> ) 를 구매한 사이트에 가서 등록</p>
<p>다음 포스팅에서 프론트 서버 배포에 대해 다뤄보겠습니닷</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[VSCode] 커스텀 자동완성 만들기 (Snippet)]]></title>
            <link>https://velog.io/@taek_jini/VSCode-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%9E%90%EB%8F%99%EC%99%84%EC%84%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-Snippet</link>
            <guid>https://velog.io/@taek_jini/VSCode-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%9E%90%EB%8F%99%EC%99%84%EC%84%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-Snippet</guid>
            <pubDate>Mon, 03 Jul 2023 05:01:13 GMT</pubDate>
            <description><![CDATA[<p>Vscode로 개발 중 자주 사용하는 함수형 컴포넌트나 변수들이 있다면 Snippet 설정을 통해 생산성을 높일 수 있습니다.</p>
<h2 id="설정-방법">설정 방법</h2>
<ol>
<li><p>vscode 왼쪽 하단 톱니바퀴 버튼을 누른 후 User Snippets를 클릭해줍니다.
<img src="https://velog.velcdn.com/images/taek_jini/post/bd407df1-1b08-4de1-8be8-cbcd194a9764/image.png" alt=""></p>
</li>
<li><p>클릭했다면 중앙 상단에 어떤 언어에 대한 Snippet 을 만들 것인지 나옵니다. 저같은 경우엔 백준 문제를 node.js 로 풀 때 <code>&quot;fs&quot;</code> 모듈 을 통해 파일 입출력을 받아야 하기 때문에 그에 대한 매 문제 반복되는 코드를 자동완성으로 저장하려합니다. 따라서 <code>javascript.json</code> 으로 들어가 줍니다.
<img src="https://velog.velcdn.com/images/taek_jini/post/0e7bf028-148e-49c9-8dfe-ed4ffe096d63/image.png" alt=""></p>
</li>
<li><p>처음 들어왔다면 주석처리된 코드들만 있을텐데 거기에 원하는 코드를 적어줍니다.
<img src="https://velog.velcdn.com/images/taek_jini/post/7036b95a-e1be-46c6-8978-25e4bc2b25ab/image.png" alt="">
위 사진의 주석은 초기 <code>javascript.json</code> 파일에서 주는 예시이며 아래는 쉽게 풀어쓴 <strong>제 예시</strong>입니다. 
한가지 염려해야 할 점이 있다면 js 파일이 아닌 json 파일이기때문에 코드 형식도 json에 맞춰서 작성해주셔야 합니다.</p>
<blockquote>
<p><strong>실제 사용 예시</strong>
&lt;javascript.json&gt;
<img src="https://velog.velcdn.com/images/taek_jini/post/ff321a87-66f7-403f-95a2-8724d75d0ad4/image.png" alt="">
&lt;index.js&gt;
<img src="https://velog.velcdn.com/images/taek_jini/post/454d0e7a-bd63-4c20-be64-93c40837c779/image.png" alt=""></p>
</blockquote>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 - Dynamic Programming(동적 계획법)]]></title>
            <link>https://velog.io/@taek_jini/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Dynamic-Programming%EB%8F%99%EC%A0%81-%EA%B3%84%ED%9A%8D%EB%B2%95</link>
            <guid>https://velog.io/@taek_jini/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Dynamic-Programming%EB%8F%99%EC%A0%81-%EA%B3%84%ED%9A%8D%EB%B2%95</guid>
            <pubDate>Mon, 22 May 2023 06:50:16 GMT</pubDate>
            <description><![CDATA[<h2 id="1-개요">1. 개요</h2>
<p><strong>DP</strong>, 즉 <strong>다이나믹 프로그래밍</strong>은 기본적인 아이디어로 하나의 큰 문제를 여러 개의 작은 문제로 나누어서 그 결과를 저장하여 다시 큰 문제를 해결할 때 사용하는 것이다.
특정한 알고리즘이 아닌 하나의 <strong>문제해결 방법</strong>으로 볼 수 있다,</p>
<blockquote>
<p>간단하게 설명하자면 <em><strong>큰 문제를 작은 문제로 쪼개서 그 답을 저장해두고 재활용하는 기법</strong></em></p>
</blockquote>
<h2 id="2-dp의-사용-조건">2. DP의 사용 조건</h2>
<ul>
<li>최적 부분 구조 (Optimal Substructure)<ul>
<li>큰 문제를 작은 문제로 나눌 수 있고, 작은 문제의 답을 모아 큰 문제를 해결할 수 있는 경우의 의미</li>
</ul>
</li>
<li>중복되는 부분 문제 (Overlapping Subproblem)<ul>
<li>동일한 작은 문제를 반복적으로 해결해야 하는 경우</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>※ 일반적인 프로그래밍 분야에서 동적(Dynamic)의 의미는?</strong></p>
</blockquote>
<ul>
<li>자료구조에서 동적 할당(Dynamic Allocation)은 &#39;프로그램이 실행되는 도중에 실행에 필요한 메모리를 할당하는 기법&#39;을 의미한다.</li>
<li>반면에 DP에서 Dynamic은 별다른 의미가 없다. 알고리즘을 만든 사람도 멋있다는 이유로 Dynamic이란 단어를 프로그래밍에 결합했다.</li>
</ul>
<h2 id="3-다익스트라dijkstra-알고리즘">3. 다익스트라(Dijkstra) 알고리즘</h2>
<p>다익스트라(Dijkstra) 알고리즘은 DP를 활용한 대표적인 최단 경로 탐색 알고리즘이다. <strong>하나의 정점에서 다른 모든 정점으로 가는 최단 경로를 구하는 알고리즘</strong>.
<img src="https://velog.velcdn.com/images/taek_jini/post/13852a25-dff4-4604-a1bc-6c6c578f845a/image.png" alt="">
사진은 프로그래머스 알고리즘 문제 - 배달 에 있는 예시이다.</p>
<h3 id="알고리즘-과정">알고리즘 과정</h3>
<ol>
<li><p>출발 노드를 설정한다.</p>
</li>
<li><p>출발 노드를 기준으로 각 노드의 최소 비용을 저장한다.</p>
</li>
<li><p>방문하지 않은 노드 중에서 가장 비용이 적은 노드를 선택한다.</p>
</li>
<li><p>해당 노드를 거쳐서 특정한 노드로 가는 경우를 고려하여 최소 비용을 갱신한다.</p>
</li>
<li><p>위 과정에서 3번 ~ 4번을 반복한다.</p>
<p>예제문제 : [배달 - 프로그래머스] (<a href="https://school.programmers.co.kr/learn/courses/30/lessons/12978">https://school.programmers.co.kr/learn/courses/30/lessons/12978</a>)</p>
<h2 id="연습문제-풀이-과정">연습문제 풀이 과정</h2>
<pre><code class="language-js">function solution(N, road, K) {
const arr = Array(N + 1).fill(Number.MAX_SAFE_INTEGER);
const lines = Array(N + 1).fill().map((_)=&gt;[])
let answer = 0;

road.forEach(([v1,v2,c]) =&gt; {
// 연결되어 있는 경로를 모두 lines배열에 저장한다.

    lines[v1].push({ to: v2, cost: c });
    lines[v2].push({ to: v1, cost: c });
});

let queue = [{ to: 1, cost: 0 }];
arr[1] = 0;

while(queue.length) {
    let {to} = queue.pop(); 

    lines[to].forEach((next)=&gt; {
        if(arr[next.to] &gt; arr[to] + next.cost) {
            // 기존 경로의 값보다 우회하는 값이 더 작으면 해당 값 저장
            arr[next.to] = arr[to] + next.cost;
            queue.push(next)
        }
    })
}

answer = arr.filter((item) =&gt; item &lt;= K).length

return answer;
}</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 정규 표현식(Regular Expression) 문법 정리 및 예제]]></title>
            <link>https://velog.io/@taek_jini/JS-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9DRegular-Expression-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC-%EB%B0%8F-%EC%98%88%EC%A0%9C</link>
            <guid>https://velog.io/@taek_jini/JS-%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9DRegular-Expression-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC-%EB%B0%8F-%EC%98%88%EC%A0%9C</guid>
            <pubDate>Sat, 29 Apr 2023 02:55:46 GMT</pubDate>
            <description><![CDATA[<p><strong>정규 표현식(Regular Expression)</strong> 은 문자열에서 특정 내용을 찾거나 대체하는데 사용한다.
대표적인 예로는 이메일이나 전화번호를 입력하라고 했을때 특정 양식에 맞지 않는 값을 입력하면 필터링되어 경고창이 뜨게 되는데 그 때 사용되는게 정규표현식이다.</p>
<p>이처럼 조건문과 반복문 떡칠을 해야 할 것 같은 복잡한 코드들도 정규표현식을 이용하면 매우 간단하게 처리할 수 있다.</p>
<blockquote>
<p><strong>전화번호</strong> 양식을 검사한다면 </p>
</blockquote>
<ul>
<li>3자리-3~4자리-4자리(사이에 1자 아무거나 가능))</li>
<li><blockquote>
<p>/^[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3,4}[-\s.]?[0-9]{4}$/</p>
</blockquote>
</li>
<li>3자리-3~4자리-4자리(사이에 &quot;-&quot; 고정)</li>
<li><blockquote>
<p>/^\d{3}-\d{3,4}-\d{4}$/</p>
</blockquote>
</li>
<li>휴대폰&amp;전화번호</li>
<li><blockquote>
<p>/(^02.{0}|^01.{1}|[0-9]{3})([0-9]+)([0-9]{4})/</p>
</blockquote>
</li>
<li>휴대폰번호</li>
<li><blockquote>
<p>/^01(?:0|1|[6-9])[.-]?(\d{3}|\d{4})[.-]?(\d{4})$/</p>
</blockquote>
</li>
</ul>
<p>이런식으로 해결할 수 있다 ! 코테 스터디 팀원분이 정리해놓은 글을 보고 나도 정리해두고 두고두고 봐야겠다는 생각이 들었다.</p>
<h3 id="정규식-메서드">정규식 메서드</h3>
<blockquote>
</blockquote>
<ul>
<li><strong>(&quot;문자열&quot;).match(/정규표현식/플래그)</strong>       <pre><code>      -&gt; &quot;문자열&quot;에서 &quot;정규표현식&quot;에 매칭되는 항목들을 배열로 반환</code></pre></li>
<li><strong>(&quot;문자열&quot;).replace(/정규표현식/, &quot;대체문자열&quot;)</strong><br>-&gt; &quot;정규표현식&quot;에 매칭되는 항목을 &quot;대체문자열&quot;로 변환</li>
<li><strong>(&quot;문자열&quot;).split(정규표현식)</strong>    </li>
<li><blockquote>
<p>&quot;문자열&quot;을 &quot;정규표현식&quot;에 매칭되는 항목으로 쪼개어 배열로 반환</p>
</blockquote>
</li>
<li><strong>(정규표현식).test(&quot;문자열&quot;)</strong></li>
<li><blockquote>
<p>&quot;문자열&quot;이 &quot;정규표현식&quot;과 매칭되면 true, 아니면 false반환</p>
</blockquote>
</li>
<li><strong>(정규표현식).exec(&quot;문자열&quot;)</strong></li>
<li><blockquote>
<p>match메서드와 유사(단, 무조건 첫번째 매칭 결과만 반환)</p>
</blockquote>
</li>
</ul>
<h3 id="정규식-플래그">정규식 플래그</h3>
<p>정규식 플래그는 정규식을 만들 때 고급 검색을 위한 <strong>전역 옵션</strong>을 설정할 수 있도록 지원하는 기능이다.</p>
<blockquote>
</blockquote>
<ul>
<li><strong>i (Ignore Case)</strong></li>
<li><blockquote>
<p>대소문자를 구별하지 않고 검색한다.</p>
</blockquote>
</li>
<li><strong>g (Global)</strong></li>
<li><blockquote>
<p>문자열 내의 모든 패턴을 검색한다.</p>
</blockquote>
</li>
<li><strong>m    (Multi Line)</strong></li>
<li><blockquote>
<p>문자열의 행이 바뀌더라도 검색을 계속한다.</p>
</blockquote>
</li>
<li><strong>s</strong></li>
<li><blockquote>
<p>​(모든 문자 정규식)이 개행 문자 \n도 포함하도록</p>
</blockquote>
</li>
<li><strong>u    (unicode)</strong>    </li>
<li><blockquote>
<p>유니코드 전체를 지원</p>
</blockquote>
</li>
<li><strong>y    (sticky)</strong>    </li>
<li><blockquote>
<p>문자 내 특정 위치에서 검색을 진행하는 ‘sticky’ 모드를 활성화</p>
</blockquote>
</li>
</ul>
<h3 id="정규식-검색-기준-패턴">정규식 검색 기준 패턴</h3>
<blockquote>
</blockquote>
<ul>
<li><strong>|</strong></li>
<li><blockquote>
<p>OR , a|b</p>
</blockquote>
</li>
<li><strong>[]</strong>    <ul>
<li>괄호안의 문자들 중 하나. or 처리 묶음 보면 된다.</li>
<li>/abc/ : &quot;abc&quot;를 포함하는</li>
<li>/[abc]/ : &quot;a&quot; 또는 &quot;b&quot; 또는 &quot;c&quot; 를 포함하는</li>
<li>[다-바] : 다 or 라 or 마 or 바</li>
</ul>
</li>
<li><strong>[^문자]</strong>    <ul>
<li>괄호안의 문자를 제외한 것</li>
<li>ex) [^lgEn] =&gt; &quot;l&quot; &quot;g&quot; &quot;E&quot; &quot;N&quot; 4개 문자를 제외</li>
<li>※ 대괄호 안에서 쓰면 제외의 뜻, 대괄호 밖에서 쓰면 시작점 뜻</li>
</ul>
</li>
<li><strong>^문자열</strong></li>
<li><blockquote>
<p>특정 문자열로 시작 (시작점)   <em>ex) /^www/</em></p>
</blockquote>
</li>
<li><strong>문자열$</strong>    </li>
<li><blockquote>
<p>특정 문자열로 끝남 (종착점)  _ ex) /com$/_</p>
</blockquote>
</li>
</ul>
<h3 id="정규식-갯수-반복-패턴">정규식 갯수 반복 패턴</h3>
<blockquote>
</blockquote>
<ul>
<li><strong>?</strong>    </li>
<li><blockquote>
<p>없거나 or 최대 한개만  <strong>ex) /apple?/</strong></p>
</blockquote>
</li>
<li><strong>&#39;*&#39;</strong></li>
<li><blockquote>
<p>없거나 or 있거나 (여러개) <em>*ex) /apple</em>/ **</p>
</blockquote>
</li>
<li><strong>+</strong>    </li>
<li><blockquote>
<p>최소 한개 or 여러개 <strong>ex) /apple+/</strong></p>
</blockquote>
</li>
<li><strong>*?</strong>    </li>
<li><blockquote>
<p>없거나, 있거나 and 없거나, 최대한개 : 없음   // <em><strong>{0}와 동일</strong></em></p>
</blockquote>
</li>
<li><strong>+?</strong>    </li>
<li><blockquote>
<p>최소한개, 있거나 and 없거나, 최대한개 : 한개    // <em><strong>{1}와 동일</strong></em></p>
</blockquote>
</li>
<li><strong>{n}</strong>    </li>
<li><blockquote>
<p>n개</p>
</blockquote>
</li>
<li><strong>{Min,}</strong>    </li>
<li><blockquote>
<p>최소 Min개 이상</p>
</blockquote>
</li>
<li><strong>{Min, Max}</strong>    </li>
<li><blockquote>
<p>최소 Min개 이상, 최대 Max개 이하    // <em><strong>{3,5}? == {3}와 동일</strong></em></p>
</blockquote>
</li>
</ul>
<h3 id="정규식-그룹-패턴">정규식 그룹 패턴</h3>
<blockquote>
</blockquote>
<ul>
<li><strong>()</strong>    </li>
<li><blockquote>
<p>그룹화 및 캡쳐</p>
</blockquote>
</li>
<li><strong>(?: 패턴)</strong>    </li>
<li><blockquote>
<p>그룹화 (캡쳐 X)</p>
</blockquote>
</li>
<li><strong>(?=)</strong>    </li>
<li><blockquote>
<p>앞쪽 일치(Lookahead), <strong>ex) /ab(?=c)/</strong></p>
</blockquote>
</li>
<li><strong>(?!)</strong></li>
<li><blockquote>
<p>부정 앞쪽 일치(Negative Lookahead),   <strong>ex) /ab(?!c)/</strong></p>
</blockquote>
</li>
<li><strong>(?&lt;=)</strong>    </li>
<li><blockquote>
<p>뒤쪽 일치(Lookbehind),  <strong>ex) /(?&lt;=ab)c/</strong> </p>
</blockquote>
</li>
<li><strong>(?&lt;!)</strong></li>
<li><blockquote>
<p>부정 뒤쪽 일치(Negative Lookbehind),   <strong>ex) /(?&lt;!ab)c/</strong></p>
</blockquote>
</li>
</ul>
<h3 id="정규식-캡처-기능">정규식 캡처 기능</h3>
<p><strong>캡처</strong>는 쉽게 생각하면 복사본을 생성하는 것이다. (실제 개념과는 다르지만 이해할 땐 이렇게 이해했다.)</p>
<p>정규식의 캡처 원리는 패턴 <strong>()</strong> 안에 있는 대상들을 그룹화하여 캡처(복사)한다.
캡처된 표현식은 당장 사용하지 않으며, 캡처를 제외한 표현식들이 모두 작동하고 난 뒤 캡처된 표현식을 검색한다. 이 기능에 대해선 다음 알고리즘 문제 포스팅때 사용하도록 할 예정이다 </p>
<h3 id="참고-사이트">참고 사이트</h3>
<ul>
<li><a href="https://www.notion.so/JavaScript-1da1ea3dd46f4c93b5c480c34e6d6699?p=2cc955a2d184429b952d12174561ed1f&amp;pm=s">JS 코테 스터디 노션 - 양혜진님</a></li>
</ul>
<ul>
<li><a href="https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EC%A0%95%EA%B7%9C%EC%8B%9D-RegExp-%EB%88%84%EA%B5%AC%EB%82%98-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC">https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EC%A0%95%EA%B7%9C%EC%8B%9D-RegExp-%EB%88%84%EA%B5%AC%EB%82%98-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC</a></li>
</ul>
<ul>
<li><a href="https://blogpack.tistory.com/560">복사해서 바로 사용하는 자주 사용하는 정규표현식(Regular Expression) Top 20</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[parseInt() 와 Number() 의 차이]]></title>
            <link>https://velog.io/@taek_jini/parseInt-%EC%99%80-Number-%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@taek_jini/parseInt-%EC%99%80-Number-%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Mon, 17 Apr 2023 01:42:55 GMT</pubDate>
            <description><![CDATA[<p>이분 탐색에 대한 알고리즘 문제를 풀다가 Number()를 사용하니 출력값이 undefined가 나오고 parseInt()를 사용하니 제대로 된 출력값이 나왔다 
문제의 코드다!</p>
<pre><code class="language-js">function solution(target, arr){
                let answer;
                arr.sort((a, b)=&gt;a-b);
                let lt=0, rt=arr.length-1;
                while(lt&lt;=rt){
                    let mid=parseInt((lt+rt)/2);
                    if(arr[mid]===target){
                        answer=mid+1;
                        break;
                    }
                    else if(arr[mid]&gt;target) rt=mid-1;
                    else lt=mid+1;
                }

                return answer;</code></pre>
<p>배열에서 target 값을 찾는 문제였는데 변수 mid의 값이 Number()를 쓰면 <strong>3.5 , 13.5</strong> 이렇게 나올 수도 있던거다. 그렇다면 arr[3.5]를 찾고있으니 undefined이 나올 수 밖에 없었다.</p>
<pre><code class="language-js">let test = &#39;100.12345&#39;;
parseInt(test); // 100
Number(test); // 100.12345
parseFloat(test); // 100.12345
</code></pre>
<ul>
<li>parseInt()는 정수만 뽑아서 출력해주고 Number()는 전체값을 출력해준다.</li>
</ul>
<pre><code class="language-js">let test = &#39;25살&#39;;
parseInt(test); // 25
Number(test); // NaN</code></pre>
<p>&#39;25&#39;라는 숫자와 나이를 칭하는 &quot;살&quot;을 더한 문자열을 변환봤을 때 차이이다.</p>
<ul>
<li>parseInt()는 25라는 숫자를 파싱해서 뽑아내온다.</li>
<li>Number()는 문자열이 포함되어 있기 때문에 NaN(Not a Number)를 출력해준다.</li>
</ul>
<pre><code class="language-js">let test = &#39;제1회&#39;;
parseInt(test); // NaN
Number(test); // NaN</code></pre>
<p>&#39;제&#39;라는 문자열로 시작했을 때의 예제이다.</p>
<ul>
<li>문자열로 시작했다면 parseInt()도 NaN을 출력해준다.</li>
</ul>
<h3 id="결론">결론</h3>
<p>parseInt()는 문자열로 된 부분에서 숫자(정수)만 뽑아서 변환해주는것이 특징이다.
Number()은 문자열 전체가 숫자일때 소수점까지 숫자타입으로 가져올 수 있다.</p>
<blockquote>
<p>이 둘의 차이점을 명확히 구분하여 필요할 때마다 적절히 활용하면 될 것 같다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] Set, Map, Object 정리]]></title>
            <link>https://velog.io/@taek_jini/JS-Set-Map-Object-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@taek_jini/JS-Set-Map-Object-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 06 Apr 2023 05:21:34 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스에서 알고리즘문제를 풀고 난 뒤 다른 사람의 풀이를 보면 
new Set과 Map 으로 간단하게 푸는 사람이 많아 공부 겸 정리를 해보려고 합니다.</p>
<h2 id="set">Set</h2>
<p>Set 객체는 자료형에 관계없이 <strong>원시 값과 객체 참조 모두 유일한 값</strong>을 저장할 수 있다.</p>
<p>더 자세히 알아보자면 </p>
<ul>
<li>Set은 중복을 허용하지 않는 데이터 집합</li>
<li>중복을 확인하기 위해 자료형을 변형하지 않는다. 즉, 1과 &#39;1&#39;을 다른 것으로 판단한다.</li>
<li>forEach(callback(value,key,set)...), for of 로 접근 가능하다.</li>
</ul>
<blockquote>
<p> new Set(iterable) – Set을 만듭니다. 이터러블 객체를 전달받으면(대개 배열을 전달받음) 그 안의 값을 복사해 Set에 넣어줍니다.
set.add(value) – 값을 추가하고 Set 자신을 반환합니다.
set.delete(value) – 값을 제거합니다. 호출 시점에 Set 내에 값이 있어서 제거에 성공하면 true, 아니면 false를 반환합니다.
set.has(value) – Set 내에 값이 존재하면 true, 아니면 false를 반환합니다.
set.clear() – Set을 비웁니다.
set.size – Set에 몇 개의 값이 있는지 세줍니다.</p>
</blockquote>
<h3 id="예시">예시</h3>
<pre><code class="language-js">const mySet = new Set(); // {}
mySet.add(1); // {1}
mySet.add(2); //{1,2}
mySet.size; //2
mySet.delete(1); //{2}
mySet.has(2); //true
mySet.has(1); //false
myset.clear(); // {}

let arr = [...mySet]; //Spread 연산자를 이용해 array로 만들 수 있다.

//array중복제거 하고싶으면 Set바꿨다 arr 하면 쉽게 해결
function arrayDuplicates(item) {
  return [...new Set(item)];
}</code></pre>
<h2 id="map">Map</h2>
<p>Map 객체는 키-값 쌍과 키의 원래 삽입 순서를 기억합니다. 모든 값(객체 및 원시 값 모두)은 키 또는 값으로 사용될 수 있습니다.</p>
<p>정리해서 알아보자면 </p>
<ul>
<li>Map 객체는 키-값을 저장한다.</li>
<li>Set과 마찬가지로 key는 자료형을 구분한다.</li>
<li>Set처럼 배열을 넣어서 초기화 시킬 수 도 있다. (forEach,for of 등 도 가능)</li>
</ul>
<blockquote>
<p>new Map() – 맵을 만듭니다.
map.set(key, value) – key를 이용해 value를 저장합니다.
map.get(key) – key에 해당하는 값을 반환합니다. key가 존재하지 않으면 undefined를 반환합니다.
map.has(key) – key가 존재하면 true, 존재하지 않으면 false를 반환합니다.
map.delete(key) – key에 해당하는 값을 삭제합니다.
map.clear() – 맵 안의 모든 요소를 제거합니다.
map.size – 요소의 개수를 반환합니다.</p>
</blockquote>
<h3 id="예시-1">예시</h3>
<pre><code class="language-js">let myMap = new Map(); //{}
myMap.set(1, &#39;hello&#39;); //{1=&gt;&#39;hello&#39;}
myMap.set(&#39;1&#39;, &#39;hello&#39;); //{1=&gt;&#39;hello&#39;, &#39;1&#39;=&gt;&#39;hello&#39;}
myMap.set(1, &#39;world&#39;); //{1=&gt;&#39;world&#39;, &#39;1&#39;=&gt;&#39;hello&#39;}
myMap.get(1); //&#39;world&#39;
myMap.has(1); //true
myMap.delete(1); // {&#39;1&#39; =&gt; &#39;hello&#39;}
//clear, size 는 Set과 같다.

let myMap = new Map([&#39;name&#39;, &#39;jini&#39;], [&#39;city&#39;, &#39;JC&#39;]); 
//{&quot;name&quot; =&gt; &quot;jini&quot;, &quot;city&quot; =&gt; JC&quot;}</code></pre>
<p>Map을 좀 더 직관적으로 응용하면
map.has()로 키값을 검사하고, 없으면 map.set()을 통해 key와 value를 넣어줍니다.
그리고 map.get으로 key값을 가져와서 value에 넣거나 value에 있는 배열에 push해 줄 수 있습니다.</p>
<blockquote>
<p>위 내용을 예시로 배열에 중복값이 있다면 value를 + 1 해주는 예시 코드입니다.</p>
</blockquote>
<pre><code class="language-js">let temp = new Map();
        for (let i = 0; i &lt; s.length; i++) {
          let item = s[i];
          if (temp.has(item)) temp.set(item, temp.get(item) + 1);
          else temp.set(item, 1);
        }</code></pre>
<h4 id="객체---맵-으로-바꾸기">객체 -&gt; 맵 으로 바꾸기</h4>
<pre><code class="language-js">let obj = { 
  name:&#39;jini&#39;,
  age:25
};
let myMap = new Map(Object.entries(obj)); 
// 객체를 2차원의 key:value 형태로 만들고 맵으로 변환
// [[&quot;name&quot;,&quot;jini&quot;],[&quot;age&quot;,25]]</code></pre>
<blockquote>
<p>맵 -&gt; 객체 는 반대로 Object.fromEntries 메서드를 사용하면 된다. 각 요소가 [키,값] 쌍인 배열을 객체로 바꿔줍니다.</p>
</blockquote>
<p>참고 : <a href="https://ko.javascript.info/map-set">https://ko.javascript.info/map-set</a></p>
<h2 id="object-vs-map">Object vs Map</h2>
<h4 id="object란">Object란?</h4>
<p>Object 역시 Map과 같이 key-value로 저장한다. 
object에서 key를 주로 property라고 부른다. object에서도 key는 unique하고 하나의 value와만 associated 돼 있다.</p>
<p>차이점을 보자면</p>
<ul>
<li>Key Type<ul>
<li>Object는 문자열,숫자,심볼 밖에 못들어간다.<ul>
<li>Map은 모두 다 들어갈 수 있고 서로 구분이 된다 (array,object...)</li>
</ul>
</li>
</ul>
</li>
<li>Map은 데이터의 순서를 보존한다</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>