<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>aiden.life.happy</title>
        <link>https://velog.io/</link>
        <description>frontend developer</description>
        <lastBuildDate>Tue, 07 Mar 2023 00:52:20 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>aiden.life.happy</title>
            <url>https://velog.velcdn.com/images/aiden-goo/profile/863e8fa1-89ab-4fc5-9cdc-0c45c29313b2/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. aiden.life.happy. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/aiden-goo" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[리액트 퍼포먼스를 위해 해야 할 것 3가지]]></title>
            <link>https://velog.io/@aiden-goo/%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%8D%BC%ED%8F%AC%EB%A8%BC%EC%8A%A4%EB%A5%BC-%EC%9C%84%ED%95%B4-%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83-3%EA%B0%80%EC%A7%80</link>
            <guid>https://velog.io/@aiden-goo/%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%8D%BC%ED%8F%AC%EB%A8%BC%EC%8A%A4%EB%A5%BC-%EC%9C%84%ED%95%B4-%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83-3%EA%B0%80%EC%A7%80</guid>
            <pubDate>Tue, 07 Mar 2023 00:52:20 GMT</pubDate>
            <description><![CDATA[<h1 id="1-memo사용">1. memo()사용</h1>
<p>React.memo()는 HOC 이며 props가 바뀌지 않으면 리렌더하지 않는다.</p>
<h1 id="2-props로-inline-function-사용하지-않기">2. props로 inline function 사용하지 않기</h1>
<p>불필요한 리렌더링을 유발함.
컴포넌트가 리렌더할 때 그 하위 컴포넌트들도 모두 리렌더링 된다는 사실은 다들 알고있을것이다.
컴포넌트가 prop으로 새로운 함수를 받으면 매번 리렌더링이 발생한다. 그럼 props가 변한게 없더라도 하위 컴포넌트까지 덩달아 리렌더하게 된다.</p>
<pre><code>// 리렌더링을 유발함. props에 inline function때문.
function ParentComponent() {
  const [count, setCount] = useState(0);

  return (
    &lt;div&gt;
      &lt;ChildComponent onClick={() =&gt; setCount(count + 1)} /&gt;
      &lt;p&gt;Count: {count}&lt;/p&gt;
    &lt;/div&gt;
  );
}

function ChildComponent({ onClick }) {
  return &lt;button onClick={onClick}&gt;Increment Count&lt;/button&gt;;
}</code></pre><p>위의 리렌더링 유발요소를 제거하면 아래와 같음.</p>
<pre><code>function ParentComponent() {
  const [count, setCount] = useState(0);

  function handleIncrement() {
    setCount(count + 1);
  }

  return (
    &lt;div&gt;
      &lt;ChildComponent onClick={handleIncrement} /&gt;
      &lt;p&gt;Count: {count}&lt;/p&gt;
    &lt;/div&gt;
  );
}

function ChildComponent({ onClick }) {
  return &lt;button onClick={onClick}&gt;Increment Count&lt;/button&gt;;
}</code></pre><p>ParentComponent 내부에 handleIncrement() 함수를 정의하고, 이를 ChildComponent의 onClick prop으로 전달. 이렇게 함으로써 매번 동일한 함수 참조가 전달되어 불필요한 다시 렌더링을 방지할 수 있음.</p>
<h1 id="3-한-번에-너무-많은-컴포넌트를-렌더링하거나-중첩된-컴포넌트를-과도하게-사용하지-않기">3. 한 번에 너무 많은 컴포넌트를 렌더링하거나 중첩된 컴포넌트를 과도하게 사용하지 않기</h1>
<p>가상 DOM에 추가되는 각 컴포넌트는 추가적인 처리 시간이 필요하므로, 많은 컴포넌트가 있는 경우 렌더링 과정이 느려질 수 있다.</p>
<p>예를 들어, 페이지에 렌더링하려는 긴 목록이 있다고 가정해 보면</p>
<p>모든 항목을 한꺼번에 렌더링하는 대신, 페이지네이션이나 무한 스크롤링과 같은 기법을 사용하여 한 번에 작은 부분집합의 항목을 렌더링할 수 있음.</p>
<p>이렇게 하면 렌더링해야 할 컴포넌트 수가 줄어들어 성능이 크게 향상될 수 있음.</p>
<p>마찬가지로, 컴포넌트의 중첩이 과도한 경우에도 렌더링 시간이 느려질 수 있음. 각 중첩 수준마다 추가적인 처리 시간이 필요하며, 코드의 디버깅과 유지보수가 어려워질 수 있음.</p>
<p>불필요한 리렌더링, 상태의 적절하지 않은 사용, 그리고 과도한 제3자 라이브러리 사용과 같은 실수를 피함으로써 개발자는 애플리케이션의 성능을 최적화하고 매끄러운 사용자 경험을 제공할 수 있다.</p>
<p>또한, 컴포넌트 계층을 간단하게 유지하고 코드 분할, 페이지네이션과 같은 기법을 사용하여 렌더링해야 할 컴포넌트 수를 줄일 수 있으므로, 더 빠른 렌더링 시간을 얻을 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useMemo를 사용한 컴포넌트 리렌더링 방지]]></title>
            <link>https://velog.io/@aiden-goo/useMemo%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A6%AC%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%B0%A9%EC%A7%80</link>
            <guid>https://velog.io/@aiden-goo/useMemo%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A6%AC%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%B0%A9%EC%A7%80</guid>
            <pubDate>Sat, 10 Dec 2022 13:19:23 GMT</pubDate>
            <description><![CDATA[<p>컴포넌트의 props가 바뀌지 않았다면 리렌더링을 방지하여 성능최적화를 이뤄보자!</p>
<pre><code>import React from &#39;react&#39;

const MakeUser = ({name, id, handleClick}) =&gt; {

    const handleClick = () =&gt; {
        console.log(&#39;handle click&#39;)
    }
    return (
        &lt;div&gt;
            {name}, {id}
            &lt;button onClick={handleClick}
        &lt;/div&gt;
    )
}

export default React.memo(MakeUser) // 요 부분!!</code></pre><p>작은 단위의 컴포넌트도 React.memo로 감싸주면 된다.
그런데 함수가 참조하고 있는 값이 바뀌면 함수들은 다시 만들어진다.
참조되는 값을 없애고 함수형 업데이트로 변경해준다.</p>
<h3 id="함수형-업데이트">함수형 업데이트?</h3>
<pre><code>const [value, setValue] = useState(0)

const onChange = () =&gt; {
    setValue(value + 1) // 이거말고
    setValue(v =&gt; v + 1) // 이게 함수형 업데이트
}</code></pre><h3 id="그럼-함수에-함수형-업데이트를-어떻게-적용">그럼 함수에 함수형 업데이트를 어떻게 적용?</h3>
<pre><code>const hanldeRemove = useCallback((id) =&gt; {
    setUser(users.filter(user =&gt; user.id !== id)) // 이렇게 값을 바로 인자로 넘겨주지 말고
},[user])</code></pre><pre><code>const hanldeRemove = useCallback((id) =&gt; {
    setUser(
        (users) =&gt; users.filter(user =&gt; user.id !== id))
    ) // 이렇게 함수의 리턴값으로 전달

},[]) // 의존성 값을 뺐음</code></pre><p>이렇게 해주면 특정항목을 수정할 때 리렌더링을 하게 됌.</p>
<blockquote>
<p>onClick으로 설정해 준 함수들은 useCallback으로 재사용한다고해서 리렌더링을 막을수는 없으므로 굳이 그렇게 할 필요가 없다.
실제로 렌더링을 방지할 수 있는 컴포넌트에 사용하기</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트-static 고정변수와 싱글톤패턴]]></title>
            <link>https://velog.io/@aiden-goo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-static-%EA%B3%A0%EC%A0%95%EB%B3%80%EC%88%98%EC%99%80-%EC%8B%B1%EA%B8%80%ED%86%A4%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@aiden-goo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-static-%EA%B3%A0%EC%A0%95%EB%B3%80%EC%88%98%EC%99%80-%EC%8B%B1%EA%B8%80%ED%86%A4%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Sat, 03 Dec 2022 09:02:25 GMT</pubDate>
            <description><![CDATA[<h3 id="필요성">필요성</h3>
<p>클래스의 각 인스턴스가 일부 프로퍼티를 공유해야하는 상황에서 static 키워드를 사용할 수 있다.</p>
<pre><code>class Shooter {
  static totalBullet = 100
  shoot(){
    Shooter.totalBullet--
    console.log(Shooter.totalBullet)
  }
}

const shooter1 = new Shooter()
const shooter2 = new Shooter()

shooter1.shoot() // 99
shooter2.shoot() // 98
</code></pre><p>위의 두 인스턴스는 동일한 변수 totalBullet을 공유한다.</p>
<blockquote>
<p>그러나 클래스를 상속받은 서브클래스와는 공유하지 않는다.</p>
</blockquote>
<pre><code>class Shooter {
  static totalBullet = 100
  shoot(){
    Shooter.totalBullet--
    console.log(Shooter.totalBullet)
  }
}

const shooter1 = new Shooter()
const shooter2 = new Shooter()

shooter1.shoot()
shooter2.shoot()

class SubShooter extends Shooter {
  shoot2(){
    SubShooter.totalBullet--
    SubShooter.totalBullet--
    SubShooter.totalBullet--
    console.log(SubShooter.totalBullet);
  }
}

const shooter3 = new SubShooter()
shooter3.shoot2() // 위에서 shoot()이 두번 실행되었으므로 98에서 시작하므로 95
shooter1.shoot() // 98
shooter1.shoot() // 96
shooter1.shoot() // 95
shooter1.shoot() // 94
shooter1.shoot() // 93
shooter1.shoot() // 92
shooter1.shoot() // 91
shooter1.shoot() // 90
shooter1.shoot() // 89
shooter1.shoot() // 88
shooter1.shoot() // 87
shooter1.shoot() // 86
shooter1.shoot() // 85
shooter1.shoot() // 84
shooter3.shoot2() // 95 - 3 = 92</code></pre><blockquote>
<p>Shooter클래스의 인스턴스와 Shooter클래스를 상속받은 클래스의 인스턴스는 서로 공유되지 않는다.</p>
</blockquote>
<h3 id="싱글톤패턴">싱글톤패턴</h3>
<blockquote>
<p>단 하나의 인스턴스를 생성하는 디자인 패턴</p>
</blockquote>
<p>new 키워드로 여러개의 인스턴스를 생성할 수 있으므로 new 키워드 사용을 막아야함. static 키워드로 클래스 메서드를 정적메서드로 만들면 인스턴스가 아닌 클래스에 속하도록 만들수가 있음.</p>
<pre><code>class AppState {
  counter = 0;
  private static instanceRef: AppState // AppStore의 단일 인스턴스에 대한 참조
  private constructor(){} // new 연산자 사용 불가하도록 막기
  static getInstance(): AppState{
    if(AppState.instanceRef === undefined){
      AppState.instanceRef = new AppState()
    }
    return AppState.instanceRef
  }
}

// const app = new AppState()
// Constructor of class &#39;AppState&#39; is private and only accessible within the class declaration.


// a, b가 동일한 인스턴스를 리턴받음.
const a = AppState.getInstance()
const b = AppState.getInstance()

a.counter++
a.counter++
b.counter++
b.counter++
console.log(a.counter); // 4
console.log(b.counter); // 4</code></pre><p>private생성자이므로 new키워드로 인스턴스를 생성할 수 없고 AppState클래스에서 제공하는 getInstance()로 클래스의 생성자를 호출할 수 있다. 인스턴스는 AppState.instanceRef하나뿐이므로 a, b가 동일한 인스턴스를 사용하여 동일한 결과값을 얻었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[308 Permanent Redirect]]></title>
            <link>https://velog.io/@aiden-goo/308-Permanent-Redirect</link>
            <guid>https://velog.io/@aiden-goo/308-Permanent-Redirect</guid>
            <pubDate>Fri, 02 Dec 2022 08:57:30 GMT</pubDate>
            <description><![CDATA[<p>api호출중 <code>308</code> 상태코드가 발견되었다.
이것은</p>
<blockquote>
<p>내가 요청한 api주소가 바뀌었으니 응답헤더에 있는 <code>Location</code>에 있는 주소로 요청하라는 의미이다.</p>
</blockquote>
<p>아래 이미지를 보자.</p>
<p><img src="https://velog.velcdn.com/images/aiden-goo/post/93af6dc1-90ac-44c9-851f-835c9b032304/image.png" alt=""></p>
<p>원인은 api주소 마지막에 <code>/</code> 를 붙여서 그런것 같다.
응답헤더의 <code>Location</code>에 <code>/api/projects</code> 라고 친절히 나와있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트-public, private, protected 접근제어자]]></title>
            <link>https://velog.io/@aiden-goo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-public-private-protected-%EC%A0%91%EA%B7%BC%EC%A0%9C%EC%96%B4%EC%9E%90</link>
            <guid>https://velog.io/@aiden-goo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-public-private-protected-%EC%A0%91%EA%B7%BC%EC%A0%9C%EC%96%B4%EC%9E%90</guid>
            <pubDate>Wed, 30 Nov 2022 13:44:31 GMT</pubDate>
            <description><![CDATA[<p>클래스는 다른 클래스를 상속받아 새로운 클래스를 만들어 낼 수 있다. 클래스간 부모, 자식관계의 형성이다.</p>
<pre><code>class Parent {
    name = &#39;&#39;;
}

class Child extends Parent {
    count = 0;
}

const a = new Child()
a.name // 가능하다.</code></pre><p>이때 자식클래스는 상속받은 부모클래스의 멤버(변수, 메서드)에 접근해서 사용할 수가 있다. 이를 제한하는 것이 접근제어자 <code>public</code> <code>private</code> <code>protected</code> 이다.</p>
<p>위 예제에서 부모클래스의 멤버변수인 <code>name</code> 에 접근제어자 <code>private</code> 을 붙인다면 어떻게 될까?</p>
<pre><code>class Parent {
    private name = &#39;&#39;;
}

class Child extends Parent {
    count = 0;
}

const a = new Child()
a.name</code></pre><p>이렇게 <code>private</code>한 부모클래스의 멤버에 접근하게 될 때는</p>
<blockquote>
<p>Property &#39;name&#39; is private and only accessible within class &#39;Parent&#39;.</p>
</blockquote>
<p>이런 경고문구를 볼 수 있다. </p>
<ul>
<li><code>private</code>  Parant 클래스에서만 접근가능하다. </li>
<li><code>public</code> 타입스크립트의 기본적인 모든 멤버의 접근권한</li>
<li><code>protected</code> 자식클래스에서 접근이 가능하지만 인스턴스에서는 접근할 수 없다.</li>
</ul>
<h3 id="protected-예제">protected 예제</h3>
<pre><code>class Parent {
    protected name = &#39;&#39;;
}

class Child extends Parent {
    count = 0;
    changeName() {
        this.name = &#39;aaa&#39;
    }
}

const a = new Child()
a.name 
// 인스턴스에서 접근한 것이므로 protected에 접근불가
// Property &#39;name&#39; is protected and only accessible within class &#39;Parent&#39; and its subclasses. 라는 오류를 표시함



a.changeName() // name은 &#39;aaa&#39;로 바뀐다
</code></pre><blockquote>
<p>자바스크립트는 private, protected키워드가 없기 떄문에 컴파일 단계에서 이 키워드를 삭제한다. 접근제어자는 개발편의성의 목적이다.</p>
</blockquote>
<h3 id="클래스의-프로퍼티-선언-2가지-방식">클래스의 프로퍼티 선언 2가지 방식</h3>
<pre><code>// Version 1
class Person {
    name = &#39;&#39;;
    private age = 0;
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age
    }
}</code></pre><pre><code>// Version 2
class Person2 {
    constructor(public name:string, private age: number){}
}</code></pre><p>명식적으로 선언해주느냐 암시적으로 선언해주느냐의 차이인데 약간의 차이는 있는게 이 타입스크립트 코드를 컴파일해보면</p>
<pre><code>var Person = /** @class */ (function () {
    function Person(name, age) {
        this.name = &#39;&#39;;
        this.age = 0;
        this.name = name;
        this.age = age;
    }
    return Person;
}());
var Person2 = /** @class */ (function () {
    function Person2(name, age) {
        this.name = name;
        this.age = age;
    }
    return Person2;
}());</code></pre><p>명식적으로 프로퍼티를 선언해주는 부분은 초기화를 한후에 인스턴스 생성시의 인자를 재할당하는 부분이 다르다. 이 부분을 염두에 두고 코드를 짜면 좋을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트-클래스 사용]]></title>
            <link>https://velog.io/@aiden-goo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@aiden-goo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Wed, 30 Nov 2022 12:52:42 GMT</pubDate>
            <description><![CDATA[<ul>
<li>자바스크립트에서는 클래스 내 생성자를 선언할 수 있으며 인스턴스 생성 중 한 번만 호출된다.</li>
<li>컴파일 타겟이 <code>ES5</code>인 경우
타입스크립트 컴파일러 -&gt; 자바스크립트 생성자 함수로 컴파일</li>
<li>컴파일 타겟이 <code>ES6</code>인 경우
타입스크립트 컴파일러 -&gt; 자바스크립트 클래스로 컴파일</li>
<li>클래스 생성자의 파라미터를 <code>readonly</code>, <code>public</code>, <code>protected</code>, <code>private</code>키워드로 정의하면 타입스크립트는 각 파라미터에 대한 클래스 프로퍼티를 만든다. 키워드로 정의하지 않으면 클래스의 프로퍼티로 만들지 않는다. 아래 예시를 보면 좀 더 이해가 잘 될 것임.<pre><code>class Cat {
  constructor(pricate name: string){}
}
</code></pre></li>
</ul>
<p>class Dog {
    constructor(name: string){}
}</p>
<p>const c = new Cat(&#39;aiden&#39;)
const d = new Dog(&#39;aiden&#39;)</p>
<p>c.name // aiden
d.name // undefined</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트-유니온타입]]></title>
            <link>https://velog.io/@aiden-goo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9C%A0%EB%8B%88%EC%98%A8%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@aiden-goo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9C%A0%EB%8B%88%EC%98%A8%ED%83%80%EC%9E%85</guid>
            <pubDate>Wed, 30 Nov 2022 12:12:00 GMT</pubDate>
            <description><![CDATA[<pre><code>class Dog {
  constructor(readonly name: string){}
  sayHello():string{
    return `Dog says Hello`
  }
}

class Fish {
  constructor(readonly name:string){}
  dive(howDeep: number):string{
    return `deep number is ${howDeep}`
  }
}

type Pet = Dog | Fish

function talkToPet(pet: Pet):string {
  if(pet instanceof Dog) {
    return pet.sayHello()
  } else if(pet instanceof Fish) {
    return &#39;fish cannot talk&#39;
  } else {
    return &#39;nothing&#39;
  }
}

console.log(talkToPet(new Dog(&#39;진돗개&#39;)))
console.log(talkToPet(new Fish(&#39;광어&#39;)))</code></pre><ul>
<li>이미 선언된 타입들로 유니온을 선언해 Pet이라는 하나의 타입을 만들었다.</li>
<li><code>instanceof</code>를 사용해서 타입가드를 해주었고 <code>in</code>, <code>typeof</code> 를 이용해서 타입가드를 해 줄수도 있다.</li>
<li><code>type</code> <code>interface</code> <code>class</code> 연산자를 사용해서 새로운 타입을 선언 할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[번역] useEffect에 관하여]]></title>
            <link>https://velog.io/@aiden-goo/%EB%B2%88%EC%97%AD-useEffect%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@aiden-goo/%EB%B2%88%EC%97%AD-useEffect%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Fri, 25 Nov 2022 15:52:25 GMT</pubDate>
            <description><![CDATA[<h2 id="1-ueseffect를-바라보는-올바른-방법">1. uesEffect를 바라보는 올바른 방법</h2>
<p>클래스기반 컴포넌트에서 사용하는 라이프 사이클들이 있다.
<code>componentWillmount</code>, <code>componentDidMount</code> 등등
<code>useEffect</code> 훅의 아이디어는 이러한 라이프사이클 방법과 근본적으로 다르다.</p>
<p>우리는 함수형 컴포넌트에서 리액트 트리 바깥쪽에 있는 states와 props 변화를 동기화 하기 위해 useEffect를 사용한다. 
그게 무슨 의미냐면
컴포넌트의 상태값이 변할 때, 리액트는 구성요소를 다시 렌더링하여 이 변경사항을 DOM과 동기화하려고 한다.
마찬가지로 useEffect훅은 리액트 트리 외부의 state, prop 변경을 동기화하는데 사용된다.
예를들어, 페이지네이션이 있는 테이블의 경우 페이지네이션을 클릭할 때마다 api호출을 해서 해당 데이터를 가져와야 하는데 활성화된 페이지네이션의 숫자를 상태로 만들어서 활성화된 상태값이 바뀔때마다 api호출을 할 수 있다. 이때 useEffect 훅은 백엔드에서 올바른 데이터를 가져와 리액트 트리 외부의 상태변경을 동기화 한다.</p>
<pre><code>const TableView = () =&gt; {
    const [ activePage, setActivePage ] = useState(0);
    const [ data, setData ] = useState();

    useEffect(() =&gt; {
        setData(await getData(activePage);
    }, [ activePage ]);

    const onPageChange = (page) =&gt; {
        setActivePage(page);
    };

    return (
        &lt;div&gt;
            &lt;Table data={ data } /&gt;
            &lt;Pagination activePage={ activePage } onChange={ onPageChange } /&gt;
        &lt;/div&gt;
    );
}</code></pre><p>이 api요청은 컴포넌트 마운트 전인지 후인지 혹은 업데이트 후인지 신경쓰지않고 상태값이 변할 때 마다 요청한다. </p>
<blockquote>
<p>달리 말하면 위 예제에서 상태값의 변경은 백엔드의 데이터와 동기화 된다고도 말할 수 있다.</p>
</blockquote>
<p>이것이 uesEffect를 보는 올바른 방법이다.</p>
<p>컴포넌트가 마운트 되었을 때 api를 요청하는 것은 종속성 배열을 사용할 수 있다.
이 종속성 배열을 useEffect훅의 트리거로 생각하기 쉬운데 이는 잘못된 생각이다.</p>
<h2 id="2-의존성-배열은-useeffect의-트리거인가">2. 의존성 배열은 useEffect의 트리거인가</h2>
<p>클래스기반 컴포넌트에서 활성페이지 상태값이 변경되었을 경우 api요청은 componentDidUpdate에서 할 수 있다. 이전 활성페이지 상태값과 현재의 것과 비교해서 둘이 다르다면 api를 요청한다.
이런걸보면 의존성 배열은 활성페이지 상태가 변경될 때 리액트에게 useEffect훅을 트리거하도록 요청하는 방법으로 볼 수 있다. 이 시선이 정확하게 틀린건 아니지만 바람직하지않은 결과를 초래할 수 있다.
위의 예제에서는 잘 동작했지만 예제가 조금 바뀐다면 어떨까?</p>
<p>다음 페이지로 이동할 때, 가져올 수 있는 행 수를 지정할 수 있는 텍스트 상자가 있다고 가정해보자.
그리고 텍스트박스를 상태값으로 지정하자. api요청에 행 수를 지정해야 하므로 uesEffect훅 내부에서 이 상태를 사용해야한다. 그러나 활성페이지의 상태가 변경될 때만 후크를 트리거해야하기 때문에 종속성 배열에는 활성페이지 상태만 지정한다.</p>
<p>이것은 잘 동작하지만 exhaustive-deps eslint rule은 경고를 던진다.
왜냐하면 행 수에 대한 상태를 종속성 배열로 전달하지 않았기 때문이다.
잘 동작하는데 무슨문제야? 라고 할 수도 있지만 지금은 uesEffect훅을 남용하여 기능을 동작하게 한 것이다. 이는 해킹에 불과하고 적절한 해결책이 아니며 이러한 접근법은 버그코드를 생산한다.</p>
<h2 id="3-의존성-배열을-올바르게-보는-방법">3. 의존성 배열을 올바르게 보는 방법</h2>
<p>어떡하면 의존성 배열을 올바르게 쓰는 것일까?
사실 의존성 배열은 기능적 이점을 제공하지 않는다. 오히려 최적화 기능이다.
앞서봤듯이 useEffect훅은 리액트 트리 외부의 state, prop의 변경에 대해 동기화하는데 사용된다. 동기화는 모든 렌더중에 발생한다.</p>
<blockquote>
<p>즉, 리액트는 모든 렌더에 useEffect훅을 호출한다.</p>
</blockquote>
<p>그러나 모든 렌더에 api를 요청하는 것은 낭비이며 불필요하다. 의존성배열은 이 문제를 해결하는데 사용된다.
의존성 배열은 useEffeect훅 내부에서 어떤 states, props가 쓰이는지 리액트에게 알려주며 그 결과 리액트는 이러한 states, props가 변경될 때만 useEffect훅을 실행해야한다.</p>
<p>그 결과, 우리는 useEffect내부에서 사용하는 states, props, functions, variables, useRefs를 포함하여 함수컴포넌트의 스코프에 있는 모든것을 종속배열로 전달해야한다.</p>
<p>예제로 돌아가서 활성페이지 상태값과 행 수 상태값은 useEffect훅에서 사용하는 것이기 때문에 종속성 배열로 전달해야한다. 그렇게 했다면 리액트는 두 상태값이 변경될 때만 이 훅을 호출해서 동기화하며 위에서 발생했던 exhaustive-deps경고문도 사라질 것이다.</p>
<pre><code>onst TableView = () =&gt; {
    const [ activePage, setActivePage ] = useState(0);
    const [ numberOfRows, setNumberOfRows ] = useState(10);
    const [ data, setData ] = useState();

    const onPageChange = (page) =&gt; {
        setActivePage(page);

        // 이벤트에서 트리거되어야하는 요청은 이벤트 핸들러내에서 더 잘 처리된다.
        setData(await getData(page, numberOfRows);
    };

    const handleNumberOfRowsChange = (e) =&gt; {
        setNumberOfRows(e.target.value);
    };

    return (
        &lt;div&gt;
            &lt;Table data={ data } /&gt;
            &lt;input type=&quot;number&quot; onChange={ handleNumberOfRowsChange } value = { numberOfRows } /&gt;
            &lt;Pagination activePage={ activePage } onChange={ onPageChange } /&gt;
        &lt;/div&gt;
    );
}</code></pre><p>useEffect훅 남발하는게 능사가 아니라능...</p>
<h2 id="4-결론">4. 결론</h2>
<p>useEffect훅은 리액트 트리 외부에 있는 함수컴포넌트의 스코프에 선언된 어떠한 것들(states, props, functions, variables, useRefs)의 변경사항을 동기활 할 때 사용된다.
우리는 useEffect훅 내부에서 사용하는 의존성들을 리액트에게 알려줄 때 의존성배열을 사용하며 이 의존성 배열들이 변경되었을 때 리액트는 이 훅을 호출해야한다. 이것이 최적화 기술이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[react already included file name]]></title>
            <link>https://velog.io/@aiden-goo/react-already-included-file-name</link>
            <guid>https://velog.io/@aiden-goo/react-already-included-file-name</guid>
            <pubDate>Thu, 17 Nov 2022 05:32:05 GMT</pubDate>
            <description><![CDATA[<h2 id="에러메세지">에러메세지</h2>
<blockquote>
<p>Already included file name &#39;file path&#39; differs from file name &#39;file path&#39; only in casing</p>
</blockquote>
<p>본인의 경우는 파일명을 소문자에서 대문자로 바꿨을 경우 이 에러를 직면했다.
<code>tsconfig.json</code> 설정중 <code>forceConsistentCasingInFileNames</code>값을 <code>false</code>로 바꿔주고 vscode를 다시 실행하니 해당 에러는 사라졌다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[vscode에서 vim 사용하기]]></title>
            <link>https://velog.io/@aiden-goo/vscode%EC%97%90%EC%84%9C-vim-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@aiden-goo/vscode%EC%97%90%EC%84%9C-vim-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 17 Nov 2022 00:59:05 GMT</pubDate>
            <description><![CDATA[<p>해피해킹으로 키보드를 바꾸면서 이것저것 검색해보니 해피해킹배열이 vim에 좋다는 걸 보았다. 그래서 vscode에 적용해보았는데 좋은 경험이었고 이제는 vim이 없으면 살 수 없는 몸이 되어버렸다.</p>
<p>집의 맥북에서 vscode의 vim extention을 이용했을 때는 큰 문제는 없었는데 새로운 환경에서 셋팅을 했을때는 잘 동작하지 않았다.
검색해보니 vim보다는 neovim이 더 좋다는 걸 확인하였고 적용했다.
실제로 한글도 더 잘 써지고 좋았다.</p>
<h2 id="neovim을-설치해보자">neovim을 설치해보자.</h2>
<pre><code>$ brew install neovim
$ which neovim
opt/homebrew/bin/nvim</code></pre><p>neovim을 설치한 후 실행파일이 있는 경로를 확인하자.
경로는 설정할 때 필요하다.</p>
<h2 id="neovim-extention-설치">neovim extention 설치</h2>
<p>vscode extention에서 neovim을 검색해서 설치해준다.
말이 설치지 클릭 한 번이면 된다.</p>
<h2 id="vscode에-neovim-설정">vscode에 neovim 설정</h2>
<p>vscode settings탭을 열어보자.
단축키는 <strong>Command+,</strong>
neovim을 검색해서 선택 후 아래부분을 찾아보자.</p>
<blockquote>
<p>Vscode-neovim › Neovim Executable Paths: Darwin
Full path to Neovim executable that should be used by the extension if running VS Code on OSX.
nvim &lt;- 이 부분을 아까의 경로로 바꿔준다.</p>
</blockquote>
<p>본인은 맥북을 사용하기 때문에 네오빔의 실행경로를 Darwin으로 했다. 자신의 OS에 맞는 곳의 경로를 바꿔주면 된다.</p>
<h2 id="보너스">보너스</h2>
<p>네오빔에서는 hjkl 키를 이용해서 화살표를 이동하게 되는데 이때 키를 쭉 누르고 있을경우 이동이 안되는 경우가 있다.
이때는 이렇게 해결하자.</p>
<pre><code>$ defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[hook호출시 에러, Invalid hook call. Hooks can only be called inside of the body of a function component]]></title>
            <link>https://velog.io/@aiden-goo/hook%ED%98%B8%EC%B6%9C%EC%8B%9C-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@aiden-goo/hook%ED%98%B8%EC%B6%9C%EC%8B%9C-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Fri, 11 Nov 2022 06:48:38 GMT</pubDate>
            <description><![CDATA[<h2 id="mismatching-versions-of-react-and-react-dom">Mismatching Versions of React and React DOM</h2>
<p>hook을 사용할 수 있는 버전인 16.8.0보다 react, react-dom 패키지의 버전이 높아야함.</p>
<h2 id="breaking-the-rules-of-hooks">Breaking the rules of hooks</h2>
<p>hooks를 호출할 때는 함수형 컴포넌트의 최상위에서 호출해야한다.</p>
<ul>
<li>렌더함수안에서 호출하지 않기</li>
<li>이벤트 핸들러안에서 호출하지 않기</li>
<li>다른 훅 내부에서 호출하지 않기</li>
</ul>
<h2 id="duplicated-react">Duplicated React</h2>
<blockquote>
<p>hooks가 제대로 동작하려면 어플리케이션 코드의 import와 react-dom의 import가 같은 모듈로 해석되어야 한다.
만일 다른 두개의 export객체로 해석된다면 결국 두개의 react package가 복사된 경우가 발생 할 수 있다.</p>
</blockquote>
<p><code>yarn list react</code> 로 전체폴더 내부에서 react를 사용하는 곳을 찾아보니
프로젝트에서는 18버전, 패키지에서는 17버전을 사용하고 있었다.
패키지의 리액트 버전을 프로젝트와 동일하게 18버전으로 올려주고 난 후 에러는 없어졌다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Module parse failed: Unexpected token]]></title>
            <link>https://velog.io/@aiden-goo/Module-parse-failed-Unexpected-token</link>
            <guid>https://velog.io/@aiden-goo/Module-parse-failed-Unexpected-token</guid>
            <pubDate>Fri, 11 Nov 2022 06:34:57 GMT</pubDate>
            <description><![CDATA[<p><a href="https://minemanemo.tistory.com/168">https://minemanemo.tistory.com/168</a></p>
<p>withTM에 에러나는 패키지네임을 추가해주면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ec2 인스턴스 생성 후 접속하기]]></title>
            <link>https://velog.io/@aiden-goo/ec2-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1-%ED%9B%84-%EC%A0%91%EC%86%8D%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@aiden-goo/ec2-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1-%ED%9B%84-%EC%A0%91%EC%86%8D%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 28 Oct 2022 13:50:37 GMT</pubDate>
            <description><![CDATA[<p>ec2 인스턴스를 생성후 다운받은 pem키를 이용하여 ssh로 접속해보자.
다운받은 pem키는 Downloads폴더에 있다.
해당 폴더로 이동하여 </p>
<pre><code>ssh -i &quot;keyname.pem&quot; ubunto@[ec2인스턴스의 퍼블릭 IPv4주소]</code></pre><p>로 접속을 시도했으나 </p>
<pre><code>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@</code></pre><p>를 보게 되었고 pem파일 권한이 안좋다고 하니 권한을 바꿔주었다.</p>
<pre><code>chmod 400 keyname.pem</code></pre><p>위 명령어로 pem키의 권한을 수정해주고 난 후 다시 ssh접속하면 끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[vite] Internal server error: URI malformed]]></title>
            <link>https://velog.io/@aiden-goo/vite-Internal-server-error-URI-malformed</link>
            <guid>https://velog.io/@aiden-goo/vite-Internal-server-error-URI-malformed</guid>
            <pubDate>Thu, 28 Jul 2022 13:09:57 GMT</pubDate>
            <description><![CDATA[<p>로컬서버를 구동하려는데 다음과 같은 에러가 나왔음. </p>
<p>index.html에서</p>
<pre><code>&lt;link rel=&quot;icon&quot; href=&quot;&lt;%= BASE_URL %&gt;favicon.ico&quot;&gt;</code></pre><p>구문을 주석해주니 정상작동함. 
&lt;%= blah %&gt; 구문이 문제가 되는 거 같음.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Fix the upstream dependency conflict, or retry]]></title>
            <link>https://velog.io/@aiden-goo/Fix-the-upstream-dependency-conflict-or-retry</link>
            <guid>https://velog.io/@aiden-goo/Fix-the-upstream-dependency-conflict-or-retry</guid>
            <pubDate>Tue, 12 Jul 2022 01:13:41 GMT</pubDate>
            <description><![CDATA[<p>크롤링소스를 clone해서 npm i 로 모듈들을 설치하려는데 해당 에러가 나왔다.</p>
<pre><code>npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: puppeteer-api-examples@1.0.2
npm ERR! Found: tslint@6.1.3
npm ERR! node_modules/tslint
npm ERR!   dev tslint@&quot;^6.1.3&quot; from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer tslint@&quot;^5.11.0&quot; from tslint-config-airbnb@5.11.2
npm ERR! node_modules/tslint-config-airbnb
npm ERR!   dev tslint-config-airbnb@&quot;^5.11.2&quot; from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! See /Users/spacemonkey/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/spacemonkey/.npm/_logs/2022-07-12T00_54_58_392Z-debug-0.log</code></pre><p><a href="mailto:puppeteer-api-examples@1.0.2">puppeteer-api-examples@1.0.2</a> 를 설치하는데 root project에서 tslint@&quot;6.1.3 을 발견했는데 
<a href="mailto:tslint-config-airbnb@5.11.2">tslint-config-airbnb@5.11.2</a>에서는 tslint를 5.11.0버전에 의존하고 있는걸 발견했다. 그래서 의존성을 해결하지 못했으며 해결방법으로 기존 커맨드에 <code>--force</code>, <code>--legacy-peer-deps</code> 를 붙여서 실행하라고 제시하고있다. npm7버전부터는 에러를 내면서 설치를 종료한다.</p>
<h3 id="해결방법">해결방법</h3>
<ol>
<li>npm install --force
로컬에 다운로드 복제본이 있어도 온라인에서 다시 다운로드 받는다.</li>
<li>npm install --legacy-peer-deps
npm 6버전 이하에서 동작하던 것처럼 peerDependencies를 무시한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Proxy server]]></title>
            <link>https://velog.io/@aiden-goo/Proxy-server</link>
            <guid>https://velog.io/@aiden-goo/Proxy-server</guid>
            <pubDate>Thu, 30 Jun 2022 05:04:20 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>클라이언트가 다른 네트워크에 접속할 수 있도록 중간에서 대리해주는 서버
프록시: 서버와 클라이언트 사이에서 대리로 통신을 수행해주는 것
그 기능을 하는 서버가 프록시 서버</p>
</blockquote>
<p>즉, 프록시 서버는 서버와 클라이언트 사이에서 요청과 응답을 처리해준다.</p>
<ul>
<li>클라이언트 -&gt; 프록시 서버로 데이터 전송</li>
<li>프록시 서버 -&gt; 웹 서버로 요청</li>
<li>웹 서버 -&gt; 프록시 서버로 응답</li>
<li>프록시 서버 -&gt; 클라이언트 데이터 전송</li>
</ul>
<h2 id="why-use-a-proxy-server">why use a proxy server?</h2>
<h3 id="캐시데이터-사용">캐시데이터 사용</h3>
<p>프록시 서버 중 일부는 프록시 서버에 요청된 내용을 캐싱해둔다. 그럼 캐싱되어있는 내용에 대한 재요청은 서버에 따로 접속할 필요없이 캐싱된 내용을 그대로 돌려주면 되므로 <code>전송시간절약</code>하고 외부트래픽을 줄여서 네트워크 병목현상도 방지함.</p>
<h3 id="보안">보안</h3>
<p>프록시 서버를 중간에 경유하게 되면 ip를 숨기는 것이 가능하다. 또한 프록시 서버를 방화벽으로 사용하기도 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Reduce unused JavaScript]]></title>
            <link>https://velog.io/@aiden-goo/Reduce-unused-JavaScript</link>
            <guid>https://velog.io/@aiden-goo/Reduce-unused-JavaScript</guid>
            <pubDate>Wed, 29 Jun 2022 14:27:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>javascript는 브라우저 렌더링 블락요소이므로 사용되지 않는 javascript를 줄여서 속도향상을 기대할 수 있다. </p>
</blockquote>
<h2 id="사용하지-않는-javscript">사용하지 않는 javscript</h2>
<h3 id="1-중요하지-않은-javascript">1) 중요하지 않은 javascript</h3>
<p>above-the-fold 콘텐츠에는 필요하지 않지만 페이지나 다른 페이지에서 여전히 사용될 수 있는 javascript</p>
<h3 id="2-dead-javascript">2) dead javascript</h3>
<p>더 이상 사용되지 않는 코드. 
사용하지 않는 모듈이거나 테스트 코드 등</p>
<h2 id="사용하지-않는-javascript는-페이지-성능에-어떤-영향을-미치는가">사용하지 않는 javascript는 페이지 성능에 어떤 영향을 미치는가?</h2>
<p>페이지가 로드될 때마다 브라우저는 javascript를 다운로드, 구문 분석 및 실행해야 페이지를 렌더링 할 수 있다. (명시적으로 지연되거나 비동기 로드는 예외)
javascript파일이 페이지 내용에 어떻게 영향을 미치는지 알 수 없기 때문에 완전히 실행될때까지 브라우저가 대기하기 때문이다.</p>
<blockquote>
<p>사용하지 않는 javascript를 로드하면 대역폭이 불필요하게 증가하고 페이지의 첫 번째 페인트(FCP)가 지연되어 전체페이지 성능이 느려진다.</p>
</blockquote>
<ul>
<li>지연되거나 비동기적으로 로드되더라도 페이지 성능에 영향을 미칠 수 있다. </li>
<li>다운로드가 필요한 다른페이지 리소스와 경쟁하기 때문.</li>
<li>모바일의 경우는 셀룰러데이터 불필요하게 증가.</li>
</ul>
<h2 id="사용하지-않는-javascript줄이는법">사용하지 않는 javascript줄이는법</h2>
<h3 id="1-코드-분할">1) 코드 분할</h3>
<p>코드분할을 통해 번들된 자바스크립트를 <code>중요한 자바스크립트</code>와 <code>중요하지 않은 자바스크립트</code>로 나눈다.
이렇게함으로써 중요한 자바스크립트만 먼저 로드되므로 렌더차단을 줄일 수 있다.</p>
<h3 id="2-dead-code-걷어내기">2) dead code 걷어내기</h3>
<p>현재 페이지에서 사용하지 않는 자바스크립트 코드를 제거.
<a href="https://webpack.js.org/guides/tree-shaking/">트리쉐이킹</a>을 사용해보자.</p>
<h3 id="3-dead-imported-code-걷어내기">3) dead imported code 걷어내기</h3>
<p>대부분의  경우 dead code는 인기있는 라이브러리 또는 다른 자바스크립트 번들에서 가져오며 선택적으로 가져오기 어렵다. 그러나 <a href="https://github.com/GoogleChromeLabs/webpack-libs-optimizations">import후 사용하지 않는 라이브러리를 제거하는 플러그인</a>이 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[lodash 번들사이즈 경량화 그리고 rollup-plugin-visualizer]]></title>
            <link>https://velog.io/@aiden-goo/lodash-%EB%B2%88%EB%93%A4%EC%82%AC%EC%9D%B4%EC%A6%88-%EA%B2%BD%EB%9F%89%ED%99%94-%EA%B7%B8%EB%A6%AC%EA%B3%A0-rollup-plugin-visualizer</link>
            <guid>https://velog.io/@aiden-goo/lodash-%EB%B2%88%EB%93%A4%EC%82%AC%EC%9D%B4%EC%A6%88-%EA%B2%BD%EB%9F%89%ED%99%94-%EA%B7%B8%EB%A6%AC%EA%B3%A0-rollup-plugin-visualizer</guid>
            <pubDate>Thu, 23 Jun 2022 01:53:29 GMT</pubDate>
            <description><![CDATA[<p>빌드 후 파일용량을 줄이는 최적화에 필요한 라이브러리다.
프로젝트에서 차지하는 파일의 용량 및 의존성있는 라이브러리들의 용량을 계산하여 프로젝트 총용량이 어떻게 구성되어 있는지(?) 확인 할 수 있다.</p>
<pre><code>// 라이브러리 설치
npm i rollup-plugin-visualizer

- vite.config.js
import { visualizer } from &quot;rollup-plugin-visualizer&quot;;
 plugins: [
    visualizer({
      filename: &quot;./dist/report.html&quot;,
      open: true,
      brotliSize: true,
    }),
  ],</code></pre><p>이렇게 셋팅 해주고</p>
<pre><code>npm run build</code></pre><p>하면 dist/report.html 파일이 생성됌.
report.html 파일에 시각화정보가 있음.</p>
<h3 id="lodash-import-방식의-차이">lodash import 방식의 차이</h3>
<p><code>import { join } from lodash</code></p>
<p>보통 이렇게 많이 imoprt하는데 이렇게 할 경우에는 아래와 같다.
<img src="https://velog.velcdn.com/images/aiden-goo/post/6908ea88-08bc-4b20-b5ba-6d8a98dbc23b/image.png" alt=""></p>
<p>lodash전체가 전부다 들어가 있다.
이 용량을 좀 줄여보자.</p>
<p><code>import join from lodash/join</code>
이렇게 하나씩 import하면 lodash전체가 아닌 부분적으로 필요한 부분만 빌드에 포함돼고 용량이 훨씬 더 적어지며 다른 컴포넌트에서 <code>import join from lodash/join</code>을 사용하더라도 용량이 더 늘어나지는 않는다.</p>
<p><img src="https://velog.velcdn.com/images/aiden-goo/post/2ba5edc9-bfda-419a-ad14-caa2f93bb37a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Object 판별]]></title>
            <link>https://velog.io/@aiden-goo/Object-%ED%8C%90%EB%B3%84</link>
            <guid>https://velog.io/@aiden-goo/Object-%ED%8C%90%EB%B3%84</guid>
            <pubDate>Fri, 17 Jun 2022 01:25:55 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트의 최상위는 Object라서 {} 값인지 판별하기가 어려운데 constructor를 이용하면 판별이 가능하다.</p>
<pre><code>const obj = {}
const str = &#39;str&#39;
const num = 1
const arr = []
const nullish = null
const undefine = undefined
const map = new Map()
const set = new Set()

obj.constructor // ƒ Object() { [native code] }
str.constructor // ƒ String() { [native code] }
num.constructor // ƒ Number() { [native code] }
arr.constructor // ƒ Array() { [native code] }
nullish?.constructor // null은 constructor가 없으므로 옵셔널체이닝필요
undefine.constructor // undefined는 constructor가 없으므로 옵셔널체이닝 필요
map.constructor // ƒ Map() { [native code] }
set.constructor // ƒ Set() { [native code] }</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Chunk Load Error in JavaScript]]></title>
            <link>https://velog.io/@aiden-goo/Chunk-Load-Error-in-JavaScript</link>
            <guid>https://velog.io/@aiden-goo/Chunk-Load-Error-in-JavaScript</guid>
            <pubDate>Thu, 16 Jun 2022 01:39:42 GMT</pubDate>
            <description><![CDATA[<h3 id="chunk-load-error란">Chunk Load Error란?</h3>
<p>React, Vue, Angular 및 이와 유사한 프레임워크가 작동하는 방법은 자바스크립트 파일을 다운로드하여 브라우저에서 웹 사이트를 렌더링하는 것. 프로젝트를 빌드할 때마다 두 가지 유형의 파일이 생성.</p>
<ul>
<li><code>Main entry file</code> - index.html의 스크립트 태그가 가리키는 파일</li>
<li><code>Other helper files</code> - 필요에 의해 main entry file에 import된 파일</li>
</ul>
<p>대개 작은 프로젝트가 있는 경우 엔트리 파일 하나로 충분. 모든 JavaScript 코드를 동일한 파일에 저장할 수 있다. 그러나 더 빠른 로딩과 더 나은 사용자 경험을 위해 자바스크립트 코드를 빌드할 때 여러 파일로 분해할 수 있으며 이러한 파일들은 필요할 때마다 메인 엔트리 파일에 의해 동적으로 가져올 수 있다.
이런 <code>helper file을 fetching</code>하는 동안 에러가 관찰되고 <code>ChunkLoad Error</code>가 나온다.
사용되는 javascipt file들은 <code>chunks</code>라고한다.</p>
<h3 id="chunk-load-error-왜-발생하는데">Chunk load Error, 왜 발생하는데?</h3>
<p>브라우저가 동적으로 가져온 일부 javascript file을 가져오는 동안 오류가 발생할 때 발생.
오류가 발생하는 이유는?</p>
<blockquote>
<ol>
<li>체크섬 유효성의 실패
수신된 파일의 체크섬이 스크립트 태그의 무결성 특성와 일치하지 않을 때 발생할 수 있다.
바이러스 백신, 브라우저 확장명, 프록시 또는 광고 차단 소프트웨어가 파일 내용을 수정하고 있기 때문에 체크섬과 일치하지 않을 수 있다.</li>
</ol>
</blockquote>
<blockquote>
<ol start="2">
<li>관련 chunk파일을 찾을 수 없거나 오래된 파일
브라우저가 청크 파일을 찾거나 다운로드할 수 없기 때문에 이러한 문제가 발생할 수 있다. 파일이 존재하지 않기 때문이기도 하지만 인터넷 오류(프록시, 방화벽, 운영 중단 등)의 수백 가지 이유 때문일 수도 있다. 브라우저가 청크 파일을 다운로드할 수 있지만 다운로드된 파일이 오래된 파일인 경우에도 발생할 수 있다.</li>
</ol>
</blockquote>
<h3 id="chunk-load-error-예시">Chunk Load Error 예시</h3>
<p>예를들어 한 사용자가 main entry file을 다운받았고 이 entry file은 필요할때마다 chunk1.js, chunk2.js를 다운로드 한다. 그와 동시에 중요한 버그를 고쳐서 다시 배포를 했다.</p>
<p>이로인해 이전 chunk가 삭제되고 새로운 chunk로 대체된다. 사용자의 경우 chunk1.js 등을 다운로드하려고 할 때마다 해당 파일이 존재하지 않기 때문에 chunk load error가 발생한다.</p>
<p>이 케이스는 사용자와 서버간의 일부 캐싱으로 인해 애플리케이션이 오래 전에 다시 배포했더라도 이전 엔트리파일로인해 브라우저가 존재하지 않는 오래된 chunk파일을 가져오려고 시도해서 chunk load error가 발생한다.</p>
<h3 id="해결책">해결책</h3>
<h4 id="사용자">사용자</h4>
<p>캐시를 지워본다...</p>
<h4 id="개발자">개발자</h4>
<ol>
<li>빌드시에 package.json의 버전정보를 json파일로 남겨서 버전을 체크해주는 방법이 있다.
이 방법은 따로 구체적으로 포스팅할 것이지만 최선의 해결책은 아닌 것 같다.</li>
<li>chunk load error가 날 때 새로고침을 해주는 방법도 있다.</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>