<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>devlog</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sat, 03 Feb 2024 10:59:01 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. devlog. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/riley_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[JS에서 싱글톤 구현하기 - 싱글톤 패턴, Object.freeze()]]></title>
            <link>https://velog.io/@riley_dev/JS%EC%97%90%EC%84%9C-%EC%8B%B1%EA%B8%80%ED%86%A4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@riley_dev/JS%EC%97%90%EC%84%9C-%EC%8B%B1%EA%B8%80%ED%86%A4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 03 Feb 2024 10:59:01 GMT</pubDate>
            <description><![CDATA[<h1 id="싱글톤-패턴이란">싱글톤 패턴이란?</h1>
<p>싱글톤 패턴은 한번만 만들 수 있는 클래스면서 전역에서 접근할 수 있는 클래스를 말한다. 
<code>single instance</code>는 앱 내 어디에서든지 접근할 수 있기 때문에 전역상태를 관리하는데 유용하다</p>
<p>먼저 <code>Counter</code>라는 클래스를 만들면 다음과 같다</p>
<pre><code class="language-js">let counter = 0;

class Counter {
  getInstance() {
    return this;
  }

  getCount() {
    return counter;
  }

  increment() {
    return ++counter;
  }

  decrement() {
    return --counter;
  }
}</code></pre>
<p>이렇게 만들어진 Class의 인스턴스를 생성하면 다음과 같이 한 개가 아닌 여러개의 인스턴스를 만들 수 있고, <code>counter1, counter2</code>의 인스턴스는 동일하지도 않게 된다.</p>
<pre><code class="language-js">const counter1 = new Counter()
const counter2 = new Counter()</code></pre>
<p>그래서 인스턴스를 생성할 때 인스턴스가 하나 존재한다면 만들지 못하게 에러를 뱉도록 추가해주어야 한다.</p>
<pre><code class="language-js">let instance;
let counter = 0;

class Counter {
  constructor() {
    if (instance) {
      throw new Error(&quot;You can only create one instance!&quot;);
    }
    instance = this;
  }

  getInstance() {
    return this;
  }

  getCount() {
    return counter;
  }

  increment() {
    return ++counter;
  }

  decrement() {
    return --counter;
  }
}

const singletonCounter = Object.freeze(new Counter());
export default singletonCounter;</code></pre>
<p>그리고 만들어진 instance를 <code>Object.freeze()</code> 해서 모듈을 <code>export</code>한다.  <code>freeze()</code>를 통해  데이터를 변경하려는 실수등을 방지할 수 있다. </p>
<h2 id="주의할-점">주의할 점</h2>
<p>인스턴스를 한번만 만들 수 있게 제한하면 메모리 공간을 아낄 수 있지만, 사실 싱글톤은 <code>anti-pattern</code>으로 여겨진다.</p>
<p>java나 c++과 같은 객체지향 프로그래밍 언어에서는 js와 같이 바로 객체를 만들 수 없고, 객체를 만드는 클래스를 만들어야 한다. 그리고 만들어진 객체가 클래스의 인스턴스를 가지는데, 바로 이 클래스의 인스턴스가 자바스크립트에서의 <code>instance</code>가 된다. </p>
<p>사실 js에서 바로 객체를 만들 수 있기 때문에 위의 예제는 과하다 볼 수 있다.  다음과 같이 객체로 만들어도 똑같기 때문이다.</p>
<pre><code class="language-js">let count = 0;

const counter = {
  increment() {
    return ++count;
  },
  decrement() {
    return --count;
  }
};

Object.freeze(counter);
export { counter };</code></pre>
<h2 id="dependency-숨기기">dependency 숨기기</h2>
<p><code>counter.js</code> 모듈을 <code>import</code>할때 해당 모듈이 싱글톤인지 아닌지 명확하지 않을 수 있다.  이 경우 실수로 싱글톤의 <code>value</code> 를 변경할 수도 있는데 이런식으로 <code>superCounter</code>를 만들어서 dependency를 숨길 수도 있다</p>
<pre><code class="language-js">import Counter from &quot;.counter&quot;;

export default class superCounter {

  constructor() {
    this.count = 0;
  }

  increment(){
   Counter.increment();
    return (this.count+=100);
  }
  //...
}</code></pre>
<h3 id="objectfreeze의-한계">Object.freeze()의 한계</h3>
<p><code>Object.freeze()</code>는 객체를 불변하게 만들고 싶을때 쓸 수 있지만, 얕은 동결이기 때문에, 중첩된 객체까지 동결하게 만들고 싶다면 다음과 같이 함수를 구현하여 동결해야 한다.</p>
<pre><code class="language-js">function deepFreeze(object) {
  // 객체에 정의된 속성명을 추출
  var propNames = Object.getOwnPropertyNames(object);

  // 스스로를 동결하기 전에 속성을 동결

  for (let name of propNames) {
    let value = object[name];

    object[name] =
      value &amp;&amp; typeof value === &quot;object&quot; ? deepFreeze(value) : value;
  }

  return Object.freeze(object);
}</code></pre>
<p>출처: 
<a href="https://www.patterns.dev/vanilla/singleton-pattern">https://www.patterns.dev/vanilla/singleton-pattern</a>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze">https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Content-Disposition 헤더]]></title>
            <link>https://velog.io/@riley_dev/Content-Disposition-%ED%97%A4%EB%8D%94</link>
            <guid>https://velog.io/@riley_dev/Content-Disposition-%ED%97%A4%EB%8D%94</guid>
            <pubDate>Sat, 02 Dec 2023 10:11:53 GMT</pubDate>
            <description><![CDATA[<h1 id="response-header">response header</h1>
<p>일반적인 HTTP 응답에서, <code>Content-Disposition</code> 응답 헤더는 콘텐츠라 브라우저 내부에 보여질 것인지, 아니면 다운로드돼서 로컬에 저장될 것인지를 알려주는 헤더다.</p>
<pre><code>Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename=&quot;filename.jpg&quot;</code></pre><ol>
<li>inline - 기본값으로, 웹페이지 내부에 display</li>
<li>attachment - 다운로드 해야되는 거라고 명시</li>
<li>attchment; filename - &#39;다른 이름으로 저장&#39;을 하려면 filename 같이 넣어준다 </li>
</ol>
<p>브라우저가 해당 컨텐츠를 표시할 수 없는 형식이거나, 항상 다운로드하도록 유도하고 싶다면 헤더에 Content-Disposition을 꼭 넣어주어야 한다 </p>
<p>참고 : <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition">https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[formData, multipart/form-data]]></title>
            <link>https://velog.io/@riley_dev/multipart-form-data</link>
            <guid>https://velog.io/@riley_dev/multipart-form-data</guid>
            <pubDate>Sat, 02 Dec 2023 09:57:18 GMT</pubDate>
            <description><![CDATA[<p>데이터는 인코딩 과정을 거쳐 서버에 전송된다. 데이터 인코딩의 진짜 목적은 Standard format에 맞게 데이터를 만들어서 인터넷으로 전송하는 것이다. </p>
<h1 id="multipart-form-data">multipart form data</h1>
<p>멀티파트 폼 데이터는 text data, binary data를 서버에 함께 전송할떄 사용되는 인코딩 방식이다.</p>
<form>태그의 enctype (encoding type) 속성은 데이터를 어떻게 인코딩 할 것인지 알려준다. 특히 HTML 폼에서 파일 업로드시 사용되는데, 큰 파일을 여러개의 파트 (multiple parts)로 쪼개서 서버에 폼 데이터를 전송한다. 


<h2 id="form-data-로-전송">form data 로 전송</h2>
<p>  파일만 요청을 보내는 경우에는 폼을 만들어 간단하게 요청을 할 수 있다</p>
<p>  form 태그를 사용하는 경우 file upload 필드가 있어야 하고, submit 동작이 있어야 하는데 submit 시에 브라우저에 form에 입력된 데이터를 FormData로 묶어내고 서버에 전송한다.</p>
<p>  이때 submit 동작은 우리가 흔히 알고 있듯, 페이지를 새로고침하기 때문에 e.preventDefault()를 넣어주어야 할 수도 있다.</p>
<pre><code>   &lt;form action=`${url}` method=&quot;POST&quot; 
          enctype=&quot;multipart/form-data&quot;&gt; 
    &lt;input type=&quot;file&quot; name=&quot;uploadfile&quot; /&gt;
    &lt;input type=&quot;submit&quot; value=&quot;Upload File&quot;/&gt; 
   &lt;/form&gt; </code></pre><h2 id="함수를-사용하여-전송">함수를 사용하여 전송</h2>
<p>  함수를 사용하여 전송할때는 formData 객체를 만들어 주어야 한다.</p>
<ol>
<li><p>formData 객체 생성후 formData 객체에 파일을 추가함</p>
</li>
<li><p>서버에 전송하기 위해 XMLHttpRequest / fetch API 를 사용함</p>
<p>함수로 데이터를 전송하는 경우 페이지가 리로딩되는 문제는 걱정하지 않아도 된다 
나의 경우 axios를 사용했다</p>
</li>
</ol>
<pre><code class="language-js">  export const saveData = (form: any) =&gt; {
  // form 안에는 title, contents, file 등의 키들이 들어가 있다

  // form data 객체를 생성하고 
    const formData = new FormData()

  //form 객체를 돌면서 key, value를 formData에 붙인다 
    Object.entries(form).forEach(([key, value]) =&gt; {
        formData.append(key, value as any)
    })

    return axios
        .post(`${BASE_URL}`, formData, {
            headers: {
                &#39;Content-Type &#39;: &#39;multipart/form-data&#39;,
            },
        })
        .then((res) =&gt; {
            return res.data
        })
        .catch((err) =&gt; {
            return err
        })
}

</code></pre>
<p>  간단하게 바로 서버에 전송하는 경우는 form 태그로 전송하고, 컨트롤이 필요한 경우 자바스크립트 함수를 사용하여 보내면 되곘다 </p>
<p>참고: 
<a href="https://www.geeksforgeeks.org/define-multipart-form-data/">https://www.geeksforgeeks.org/define-multipart-form-data/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[원티드 챌린지]stale-while-revalidate란 무엇인가요? Server State, UI State란? ]]></title>
            <link>https://velog.io/@riley_dev/%EC%9B%90%ED%8B%B0%EB%93%9C-%EC%B1%8C%EB%A6%B0%EC%A7%80stale-while-revalidate%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80%EC%9A%94-Server-State-UI-State%EB%9E%80</link>
            <guid>https://velog.io/@riley_dev/%EC%9B%90%ED%8B%B0%EB%93%9C-%EC%B1%8C%EB%A6%B0%EC%A7%80stale-while-revalidate%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80%EC%9A%94-Server-State-UI-State%EB%9E%80</guid>
            <pubDate>Fri, 14 Oct 2022 06:16:08 GMT</pubDate>
            <description><![CDATA[<h2 id="stale-while-revalidate이란-">stale-while-revalidate이란 ?</h2>
<blockquote>
<p><code>stale-while-revalidate</code>를 사용하면 캐싱된 콘텐츠를 바로 확인하고, fresh하게 유지할 수 있습니다. </p>
</blockquote>
<p><code>stale-while-revalidate</code> 는 크롬 75, 파이어폭스 68에서 지원되며, 지원이 안되는 경우 해당 부분은 무시됩니다.</p>
<p><code>stale-while-revalidate</code>는 두 부분으로 나눌 수 있습니다.</p>
<ol>
<li>캐시된 응답이 stale할 수 있다.</li>
<li>재검증 프로세스</li>
</ol>
<h3 id="브라우저는-캐시된-응답이-stale한-것을-어떻게-알까요">브라우저는 캐시된 응답이 &#39;stale&#39;한 것을 어떻게 알까요?</h3>
<p><code>Cache-Control</code> 응답 해더는 <code>max-age</code> 를 포함해서 언제 stale하게 되는지를 포함해야 합니다. 해당 <code>max-age</code>보다 최신 응답이면 fresh, 아니면 stale한 것입니다.</p>
<p>캐싱 응답이 fresh인 경우에는 할일이 없지만, stale인 경우라면 또 다른 age 체크가 수행됩니다. 이렇게요 =&gt; <code>stale-while-revalidate</code>으로 설정한 추가 시간 내에 캐시 응답이 있나요?</p>
<p>만일, stale 응답이 해당 시간 내에 떨어지면, 브라우저 요청이 이뤄진 것으로 간주되고, 동시에 캐시 응답 사용을 지연시키지 않는 네트워크를 통해서 &quot;재확인&quot; 요청이 이루어집니다. 응답은 이전 캐시 응답과 동일할 수도 있고 다를 수도 있습니다. 두 경우 모두, 네트워크 응답은 로컬로 저장이 되어 이전의 캐시 응답을 대체하고, 이후에 있을 <code>max-age</code> 동안에 사용되는 &#39;fresh한 타이머&#39;를  재설정합니다.</p>
<p>하지만 stale 응답이 너무 오래되어 <code>stale-while-revalidate</code> 시간을 벗어나 버리면, 브라우저 요청이 이뤄지지 않은 것으로 간주될 것이고, 브라우저는 네트워크 응답을 다시 받아서, 초기 요청 수행 +  fresh 응답으로 로컬 캐시 생성을 할 것입니다.</p>
<h3 id="언제-사용할까요">언제 사용할까요?</h3>
<p>refresh 되어야 하는 정보를 제공하는 서비스지만, 조금 stale해도 되는 경우에 사용합니다. 
<code>stale-while-revalidate</code>와 <code>max-age</code>를 같이 사용하면 네트워크 응답을 block 하지않고, 캐시에서 최신 콘텐츠로 추후 요청이 이뤄질 가능성이 높아집니다.</p>
<h2 id="server-state-ui-state">Server State, UI State</h2>
<p>모든 타입의 상태는 다음 2가지로 나눌 수 있습니다. </p>
<ol>
<li><p>Server Cache - 서버에 실제로 저장되어 있는 상태로, 빨리 접근하기 위해(사용자 데이터처럼) 클라언트에 저장합니다.</p>
</li>
<li><p>UI State - UI에서만 유용한 상태로, 앱에서 interactive한 부분을 제어하는 상태입니다.</p>
</li>
</ol>
<p>이 2가지를 혼재하여 쓰게되면 실수가 발생합니다. 서버 캐시의 경우 UI 상태와는 처음부터 다른 성격을 띄고 있기 때문에 다르게 다루어져야 합니다. 내가 다루고 있는 것이 상태가 아니라 <code>state</code> 상태의 캐시<code>a cache of state</code> 라는 것을 받아들인다면, 제대로 생각하고 관리하고 있는 것입니다. </p>
<p>출처
<a href="https://web.dev/stale-while-revalidate/">https://web.dev/stale-while-revalidate/</a>
<a href="https://kentcdodds.com/blog/application-state-management-with-react">https://kentcdodds.com/blog/application-state-management-with-react</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ 아니 대체 .... 이벤트 루프는 뭔가요?]]></title>
            <link>https://velog.io/@riley_dev/%EC%95%84%EB%8B%88-%EB%8C%80%EC%B2%B4-....-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EB%8A%94-%EB%AD%94%EA%B0%80%EC%9A%94</link>
            <guid>https://velog.io/@riley_dev/%EC%95%84%EB%8B%88-%EB%8C%80%EC%B2%B4-....-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EB%8A%94-%EB%AD%94%EA%B0%80%EC%9A%94</guid>
            <pubDate>Wed, 12 Oct 2022 09:13:40 GMT</pubDate>
            <description><![CDATA[<p>너무 좋은 영상인 <a href="https://www.youtube.com/watch?v=8aGhZQkoFbQ&amp;t=13s">what is event loop anyway</a> 를 보고 정리하는 글입니다.</p>
<h2 id="자바스크립트는-싱글스레드-언어--콜스택-하나">자바스크립트는 싱글스레드 언어 = 콜스택 하나</h2>
<p>싱글스레드 언어라는 것은, call stack 콜 스택이 하나 있다는 것을 의미합니다. (싱글스레드란, 한번에 하나만 실행할 수 있다는 의미니까요)</p>
<p>함수가 있을 때 흐름은 다음과 같습니다. </p>
<ol>
<li>함수가 호출된다.</li>
<li>스택에 쌓인다.</li>
<li>함수가 리턴된다.</li>
<li>스택에서 빠진다.</li>
</ol>
<p>에러가 날 때도 스택 순서대로 에러가 나는 것을 확인할 수 있습니다(..솔직히 크게 신경쓴 적이 없었는데..ㅠ  신경쓴 사람은 손을 들어주세요 🙋‍♀️) </p>
<pre><code class="language-js">function foo(){
  throw new Error(&#39;Oops&#39;)
}

function bar(){
    foo()
}

function baz(){
     bar() 
}

baz()</code></pre>
<p><img src="https://velog.velcdn.com/images/riley_dev/post/e8cbc7a8-14db-4037-b85c-0ee414fc842e/image.png" alt="에러"></p>
<p>코드 흐름을 생각해보면 이렇습니다.</p>
<p>1.<code>baz()</code>가 호출된다. <code>baz()</code>가 콜스택에 쌓인다.
2.<code>bar()</code>이 호출된다. <code>bar()</code>이 콜스택에 쌓인다.
3.<code>foo()</code>가 호출된다. <code>foo()</code>가 콜스택에 쌓인다. 
4.<code>foo()</code>는 <code>Error</code>를 반환한다. </p>
<p>스택에 차곡차곡 쌓여있는 콘솔 메세지를 볼 수 있습니다. 마지막에 보이는 <code>anonymous function</code> 는 메인함수입니다. </p>
<p>만일 함수를 이렇게 호출한다면 우리가 가끔씩은 만났던 에러메시지를 볼 수가 있습니다. 그것은 바로 !! 
<code>RangeError : Maximum call stack size exceeded</code></p>
<pre><code class="language-js">function foo(){
    return foo()
}

foo()</code></pre>
<p>foo()는 또 foo()를 호출하고, 또 foo()를 호출하고... 결국 콜 스택의 사이즈를 벗어나게 되는 것이죠</p>
<h2 id="스택이-작업이-쌓여있다면-브라우저는-렌더링을-할까요">스택이 작업이 쌓여있다면.. 브라우저는 렌더링을 할까요?</h2>
<p>stack에 느린 작업이 있다면 시간이 오래걸리기 때문에 blocking이 됩니다. 이게 문제가 되는 이유는 우리가 브라우저를 사용하기 때문입니다. </p>
<p>콜스택에 작업이 쌓여있다면, 브라우저는 아무 작업을 할 수가 없습니다. 렌더링을 하지 못하게 됩니다. 
그럼 이것의 해결책으로는 무엇이 있을까요? 바로 async callback을 활용하는 것입니다.</p>
<p>다음의 경우 어떤식으로 흘러갈까요?</p>
<pre><code class="language-js">console.log(&#39;hi&#39;)

setTimeout(()=&gt; {
  console.log(&#39;there&#39;)
},5000)

console.log(&#39;JSConfEU&#39;)</code></pre>
<p>순서는 다음과 같습니다.</p>
<pre><code class="language-bash">console.log(&#39;hi&#39;)
setTimeout()이 Stack에 들어갔다가 사라짐
console.log(&#39;JSConfEU&#39;)
5초 뒤에 console.log(&#39;there&#39;)
</code></pre>
<p>자바스크립트는 런타임동안 한가지 일을 할 수 있지만, 브라우저는 Web API와 같은 다른 것들을 우리에게 제공해줍니다.</p>
<h2 id="이벤트-루프가-하는일은-무엇인가요">이벤트 루프가 하는일은 무엇인가요?</h2>
<p>이벤트 루프는 먼저 스택을 확인합니다. 그리고 스택이 비어있다면 테스크 큐의 가장 처음에 있는 작업을 꺼내어 스택에 넣어줍니다(push)</p>
<p><code>setTimeout</code>을 0초 뒤에 실행하더라도 결과는 같습니다. 왜냐면 테스크 큐의 작업은 결국 스택이 비어있어야 하나씩 스택에 들어가기 때문입니다. </p>
<p><img src="https://velog.velcdn.com/images/riley_dev/post/0300eaf0-29f1-488a-8898-c29375461d53/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 렌더링 살펴보기(1) - 언제 렌더링을 해야할까요? SSR을 사용해야 할까요 ? ]]></title>
            <link>https://velog.io/@riley_dev/%EC%9B%B9-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B01-%EC%96%B8%EC%A0%9C-%EB%A0%8C%EB%8D%94%EB%A7%81%EC%9D%84-%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C%EC%9A%94-SSR%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C%EC%9A%94</link>
            <guid>https://velog.io/@riley_dev/%EC%9B%B9-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B01-%EC%96%B8%EC%A0%9C-%EB%A0%8C%EB%8D%94%EB%A7%81%EC%9D%84-%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C%EC%9A%94-SSR%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C%EC%9A%94</guid>
            <pubDate>Mon, 10 Oct 2022 17:18:13 GMT</pubDate>
            <description><![CDATA[<p>아래 글을 읽으면서 정리한 글입니다.
<a href="https://web.dev/rendering-on-the-web/">https://web.dev/rendering-on-the-web/</a></p>
<p>개발하면서 앱 로직과 렌더링을 어디서 해야하는가에 대한 고민은 중요합니다. 어려운 부분이지만 웹사이트를 만드는 방법은 너무나도 다양합니다. 크롬에서는 개발자들에게 서버 렌더링이나 정적 렌더링을 권고하고 있습니다. 이를 제대로 이해하기 위해서는 각각의 방법을 이해해야 하고, 용어를 잘 알고 있어야 합니다.</p>
<h2 id="용어정리">용어정리</h2>
<h3 id="렌더링">렌더링</h3>
<ul>
<li><code>SSR</code>: server side rendering, 클라이언트 사이드나 유니버설 앱을 서버에서 HTML로 렌더링합니다.</li>
<li><code>CSR</code>: client side rendering, 브라우저에서 앱을 렌더링 합니다. 보통 DOM을 사용합니다.</li>
<li><code>Rehydration</code>: 클라이언트에서 JavaScript 뷰를 &quot;booting up&quot;하여,서버에서 렌더된 HTML 돔 트리와 데이터를 재사용할 수 있도록 합니다.</li>
<li><code>Prerendering</code>: 빌드타임에 클라이언트 사이드에서 실행되는 앱으로, 정적 HTML로 초기 상태를 캡쳐합니다. </li>
</ul>
<h3 id="성능">성능</h3>
<ul>
<li><code>TTFB</code>: <strong>Time to First Byte</strong>,  링크를 클릭해서 콘텐츠의 첫번째 비트가 들어오는 시간</li>
<li><code>FP</code>: <strong>First Paint</strong>,  사용자가 볼 수 있는 픽셀이 처음으로 보여지는 시간 </li>
<li><code>FCP</code>: <strong>First Contentful Paint</strong>, 요청된 콘텐츠(글 body 등)가 보이는 시간</li>
<li><code>TTI</code>: <strong>Time To Interactive</strong>, 페이지가 interactive(이벤트 연결 등)해지는 시간</li>
</ul>
<h2 id="서버-렌더링-server-rendering">서버 렌더링 Server Rendering</h2>
<blockquote>
<p>서버 렌더링은 네비게이션에 대한 응답으로, 서버에서 전체 HTML 페이지를 생성합니다.</p>
</blockquote>
<img src="https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WOL6PIpIQHpqsgtKwbJv.png?auto=format&w=1428" width="400"/>

<ul>
<li>보통 FP, FCP가 빠릅니다.</li>
<li>서버에서 페이지 로직을 실행하고 서버에서 렌더링하기 때문에 많은 양의 자바스크립트를 클라이언트에 보내지 않아도 되기 때문에 TTI가 빨라집니다. (서버 렌더링의 경우, 브라우저에 text, link 들만 보내기 때문입니다)</li>
<li>기기의 스펙트럼과 네트워크 환경이 다양한 경우 좋은 방법일 수 있습니다. </li>
<li>서버에서 페이지를 생성하는 시간이 걸리기 때문에 TTFB는 느려집니다. </li>
</ul>
<p>서버 렌더링 vs 클라이언트 사이드 렌더링에 대한 논쟁은 많지만, 서버 렌더링은 페이지에 따라 선택할 수 있다는 걸 기억해야 합니다. <code>Netflix</code>의 경우, 정적 랜딩 페이지를 서버 렌더링하고, interaction이 있는 무거운 페이지있는 JS는 prefecthing하여 클라이언트 렌더링 페이지가 빨리 로딩될 수 있도록 합니다.</p>
<p><a href="https://dev.to/addyosmani/speed-up-next-page-navigations-with-prefetching-4285">prefetching에 관해 읽어볼 글</a></p>
<p>리액트의 경우 <code>renderToString()</code>이나 <code>Next.js</code>를 사용해서 서버 렌더링을 할 수 있고, 뷰의 경우 서버렌더링 가이드나 <code>Nuxt</code>, 앵귤러의 경우 <code>Universal</code>이 있습니다. 많이 쓰이는 이러한 툴들은 <code>hydration</code>을 어떤 형태로든 쓰고 있기 때문에 사용시 잘 고려해보아야 합니다. </p>
<h2 id="정적-렌더링-static-rendering">정적 렌더링 Static Rendering</h2>
<p>정적 렌더링은 빌드타임에 일어나기 때문에, FP, FCP, TTI가 빠릅니다. 서버 렌더링과는 달리 TTFB가 빠른데, 그 이유는 HTML 페이지가 생성되지 않아도 되기 때문입니다. 보통, 정적 렌더링이라 함은, 각 URL 마다 HTML 파일을 생성하는 것을 의미합니다. HTML 응답으로 미리 만들어진 파일은 정적 렌더를 통해서 edge-caching의 이점을 취하기 위해 여러 CDN을 통해 배포될 수 있습니다.</p>
<img src="https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/SRsl2UcHyJquzuJkdTHR.png?auto=format&w=1428" width="400"/>

<p>정적 렌더링의 단점중 하나는 모든 URL에 맞는 HTML 파일이 있어야 한다는 것입니다. 미리 URL을 알기 어렵거나 색다른 페이지가 많은 큰 사이트의 경우 이는 어려운 문제일 수 있습니다.</p>
<h3 id="static-rendering-prerendering의-차이">static rendering, prerendering의 차이</h3>
<p>정적 렌더페이지는 클라이언트 JS를 실행하지 않고도 interactive할 수 있지만, SPA의 FP or FCP를 향상시켜주는 프리렌더링의 경우 클라이언트가 <code>booted</code>되어야만 페이지가 interactive 해집니다. </p>
<h4 id="정적렌더링인지-프리-렌더링인지-알아보는-방법">정적렌더링인지 프리 렌더링인지 알아보는 방법</h4>
<ol>
<li>JavaScript를 disable시키고 웹페이지를 로드해보면, 정적 페이지의 경우 대부분의 기능이 작동하지만, 프리렌더링 페이지의 경우 대부분의 페이지가 작동하지 않을 것입니다.</li>
<li>devTool을 확인해보면 자바스크립트가 얼마다 다운로드됐는지 확인할 수 있습니다. 프리렌더링의 경우 더 많은 자바스크립트가 필요하고, 더 복잡한 경우가 많습니다. </li>
</ol>
<h2 id="서버-렌더링-vs-정적-렌더링">서버 렌더링 vs 정적 렌더링</h2>
<p>서버렌더링만이 무조건 해결책인 것은 아닙니다. 서버 렌더링의 경우 flush가 일찍 일어나지 않고,  TTFB가 지연될 수 있고, 전송되는 데이터가 많아질 수 있습니다. 
리액트의 경우, <code>renderToString()</code> 는  동기적으로 실행되고 싱글스레드기 때문에 느릴 수 있습니다. 서버 렌더링을 &quot;올바르게&quot;한다는 것은, 컴포넌트 캐싱, 메모리 사용 관리, 메모이제이션 기술 등을 고려하는 것입니다. 서버 렌더링으로 화면이 더 빨리 보인다고 해서 신경을 덜 써도 된다는 것이 아닙니다.</p>
<p>서버 렌더링은 요청한 URL마다 HTML을 생성하지만, 정적 렌더링 콘텐츠보다는 느릴 수 있습니다. 좀 더 시간을 들여,<code>서버 렌더링 + HTML 캐싱</code> 작업을 한다면 서버 렌더리 시간이 엄청나게 감소할 수 있습니다. 
서버 렌더링의 좋은점은 더 많은 &quot;live&quot; 데이터를 끌어와서 정적 렌더링보다 더 많은 요청에 대응하는 응답을 한다는 것입니다. 사람마다 다른 페이지를 보여주는 페이지의 경우 정적 렌더링과 잘 맞지 않습니다.</p>
<h2 id="클라이언트-사이드-렌더링-client-side-rendering">클라이언트 사이드 렌더링 Client Side Rendering</h2>
<blockquote>
<p>CSR은 자바스크립트를 이용해서 브라우저에서 바로 페이지를 렌더링하는 것을 말합니다. 모든 로직, 데이터 fetching, 템플릿 구성, 라우팅이 모두 서버가 아닌 클라이언트에서 이루어집니다.</p>
</blockquote>
<p>클라이언트 사이드 렌더링의 경우 모바일에서 빠르게 만들기 어려울 수 있습니다. <code>&lt;linke rel=preload&gt;</code>를 사용해서 중요한 스크립트나 데이터를 빨리 전달할 수도 있는데, 그러면 파서parser가 더 빨리 일을 시작합니다. PRPL과 같은 패턴으로 빠르게 느껴질 수 있도록 할 수 있습니다.</p>
<img src="https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/JFxoxQe847ntctVOdn5u.png?auto=format&w=1600" width="400"/>

<p>CSR의 안좋은 점은, 앱이 커짐에 따라 필요한 JS또한 커진다는 것입니다. 특히 최신 JS 라이브러리의 경우, 폴리필,서드 파티 등으로 더 어려워질 수 있습니다.대용량 JS 번들에 의존하는 경우, code-splitting을 고려해야하고, lazy-load JavaScript(<code>필요한 것을, 필요할때 사용하기</code>)를 사용해야 합니다. 
interactivity가 거의 없는 애플리케이션의 경우, 서버 렌더링이 더 좋은 해결책이 될 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] state as snapshot, batching ]]></title>
            <link>https://velog.io/@riley_dev/Reactstate-as-snapshot-batching</link>
            <guid>https://velog.io/@riley_dev/Reactstate-as-snapshot-batching</guid>
            <pubDate>Tue, 04 Oct 2022 15:16:33 GMT</pubDate>
            <description><![CDATA[<h1 id="state-as-snapshot">state as snapshot</h1>
<p>(이벤트 핸들러가 비동기라고 해도) 상태 변수의 값은 렌더시 절대 변하지 않습니다. 렌더시 onClick 내부를 살펴보면, </p>
<pre><code class="language-jsx">const [number, setNumber] = useState(0);
// ...
return(
  &lt;button onClick={() =&gt; {
        setNumber(number + 5);
        setTimeout(() =&gt; {
          alert(number);
        }, 3000);
      }}&gt;+5&lt;/button&gt;
  )</code></pre>
<p>number의 value는 <code>setNumber(number + 5)</code>호출 뒤에도 <code>0</code> 입니다. 리액트가 컴포넌트를 호출하여 UI의<code>스냅샷을 찍을 때</code> value는 고정되어 있습니다(<code>fixed</code>)</p>
<h1 id="batching">batching</h1>
<blockquote>
<p>사전: 일괄 처리</p>
</blockquote>
<p>웨이터가 주문을 받을때를 생각해봅시다. 주문하나 받고 음식을 가져다주지 않고, 여러가지 주문을 한꺼번에 받고 나중에 음식을 가져다 줍니다.</p>
<p>이렇게 하면, 리렌더링을 계속하지 않고도 많은 컴포넌트의 많은 변수를 한꺼번에 업데이트 할 수 있습니다. 하지만, 이건 이벤트 핸들러나 안의 코드가 끝나지 않았다면 UI가 업데이트 되지 않는다는 것을 의미합니다. 이를 <code>batching</code> 이라고 합니다. </p>
<h2 id="다음-렌더-전에-같은-값을-여러번-업데이트-하고-싶다면">다음 렌더 전에 같은 값을 여러번 업데이트 하고 싶다면?</h2>
<p>다음 값을 전달하지 않고, 큐(queue)에 있던 이전 값을 기반으로 다음 상태를 계산할 수 있는 함수를 넣으면 됩니다.</p>
<pre><code class="language-jsx">//이전
 &lt;button onClick={() =&gt; {
        setNumber(number + 1);
        setNumber(number + 1);
        setNumber(number + 1);
      }}&gt;+3&lt;/button&gt;
//이후
 &lt;button onClick={() =&gt; {
        setNumber(n =&gt; n + 1);
        setNumber(n =&gt; n + 1);
        setNumber(n =&gt; n + 1);
      }}&gt;+3&lt;/button&gt;</code></pre>
<h3 id="동작">동작</h3>
<ol>
<li>리액트는 이벤트 핸들러에있는 다른 코드를 모두 실행하고, 해당 함수를 리액트 큐에 넣습니다.</li>
<li>다음 렌더링에, 리액트는 큐를 모두 확인하고, 마지막으로 업데이트된 상태를 줍니다.
참고</li>
</ol>
<h2 id="새로운-값을-넣고-업데이트를-한다면">새로운 값을 넣고 업데이트를 한다면?</h2>
<pre><code class="language-jsx">  &lt;button onClick={() =&gt; {
        setNumber(number + 5);
        setNumber(n =&gt; n + 1);
      }}&gt;Increase the number&lt;/button&gt;</code></pre>
<h3 id="동작-1">동작</h3>
<ol>
<li>setNumber(number + 5): number는 0이기 때문에, setNumber는 (0+5)가 되고, 리액트는 &quot;5로 바꾸기&quot;를 큐에 넣습니다. </li>
<li>setNumber(n =&gt; n + 1): n =&gt; n + 1 는 업데이트 함수입니다. 리액트는 이를 큐에 넣습니다.</li>
</ol>
<p>참고: 
<a href="https://beta.reactjs.org/learn/state-as-a-snapshot">https://beta.reactjs.org/learn/state-as-a-snapshot</a>
<a href="https://beta.reactjs.org/learn/queueing-a-series-of-state-updates">https://beta.reactjs.org/learn/queueing-a-series-of-state-updates</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js]CSR이란 무엇이며  SSR은 왜 필요한가요?]]></title>
            <link>https://velog.io/@riley_dev/Next.jsCSR%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-SSR%EC%9D%80-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80%EC%9A%94</link>
            <guid>https://velog.io/@riley_dev/Next.jsCSR%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-SSR%EC%9D%80-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80%EC%9A%94</guid>
            <pubDate>Fri, 30 Sep 2022 08:43:36 GMT</pubDate>
            <description><![CDATA[<h1 id="csrclient-side-rendring">CSR(Client Side Rendring)</h1>
<p>HTML 문서에서 모든 데이터를 받지않고, 기본적인 정보만 담고있는 뼈대를 사용하여 자바스크립트 파일과 연결한 뒤, 나머지 웹 페이지를 렌더링합니다.</p>
<p>작동원리 </p>
<ol>
<li><p>유저가 웹페이지에 접근요청</p>
</li>
<li><p>서버가 브라우저에 응답</p>
</li>
<li><p>브라우저가 Javascript 파일 다운로드</p>
</li>
<li><p>파일 콘텐츠 실행</p>
</li>
<li><p>visible and interactive contents</p>
<h2 id="장점">장점</h2>
<blockquote>
<p>높은 성능, 속도, 재사용 가능한 컴포넌트</p>
</blockquote>
</li>
<li><p>성능 - CSR은 요구한(on-demand) HTML을 생성합니다. 일반적인 HTML 페이지 처럼 전체 페이지를 리랜더링하지 않습니다. 개별 페이지로 동작하는 것처럼 보일 수 있지만, 사실 페이지의 콘텐츠를 렌더링합니다. 이러한 이유로 계산이나 RAM을 많이 아낄 수 있고, SSR 보다 빠릅니다.</p>
</li>
<li><p>속도 - CSR은 보여주는 HTML만 생성합니다. 즉, DOM은 보이는 HTML 부분의 콘텐츠를 포함한 코드만을 가지고 있습니다. 그렇기 때문에 DOM은 쉽게 요소들을 핸들링할 수 있습니다. DOM이 갖고있는 코드가 많다고 하더라도, 렌더 시간이 오래걸리지 않습니다. lazy loading 덕에 CSR은 SSR보다 빠릅니다.</p>
</li>
<li><p>재사용 가능한 컴포넌트 - CSR을 사용하면 서버에 매번 요청하지 않고도 여러 페이지나 루트에 사용되는 UI 컴포넌트를 재사용할 수 있습니다. 이로서 페이지의 사용성이 향상됩니다.  </p>
<h2 id="단점">단점</h2>
<blockquote>
<p>느린 초기속도, SEO 이슈, 캐시 이슈 </p>
</blockquote>
</li>
<li><p>느린 초기속도 - CSR은 초기에 모든 자바스크립트를 로딩한 후, API 호출을 통해 db에서 데이터를 받고, HTML을 생성합니다. 초기에 해당 데이터를 로딩하는 것은 SSR보다 시간이 더 걸릴 수 있습니다. </p>
</li>
<li><p>SEO(검색 엔진 최적화 ) 이슈 - CSR에서는 JS 렌더링, 브라우저 인덱싱시 보통 2번의 과정을 거쳐야 합니다. (two-wave process)</p>
<ul>
<li>(frist wave)처음에는 소스 코드를 요청하고,현존하는 HTML에 인덱싱을 합니다. 하지만 CSR의 경우, JS에서 HTML로 컨버팅하는 시간이 걸리기 때문에 HTML이 많지 않습니다.</li>
<li>(second wave)두번째에는 모든 리소스에 접근이 가능해지면, 브라우저는 추가적인 것들과 검색엔진에 맞는 인덱스를 반환합니다. SSR의 경우 HTML은 처음부터 이용할 수 있기 때문에 해당문제는 일어나지 않습니다.</li>
</ul>
</li>
<li><p>캐싱 이슈 - HTML은 첫 렌더에서 쓸 수 없기 때문에, 브라우저는 HTML 구조를 캐싱할 수 없습니다. 해당 이슈를 피하려면 JS를 캐싱할 수 있지만,JS는 브라우저 메모리의 공간을 많이 차지하기 때문에, 이는 비용이 드는 작업입니다.(costly)</p>
</li>
</ol>
<h1 id="ssr">SSR</h1>
<p>필요한 것만 담고 있는 작은 HTML 문서를 가지고 있는 게 아니라, 첫 페이지를 렌더링하는 과정을 서버가 담당합니다.그리고 브라우저는 자바스크립트를 처리하고, static HTML 에 렌더링을 합니다.</p>
<p>작동원리</p>
<ol>
<li>유저가 웹페이지에 접근요청</li>
<li>서버가 준비한 HTML 파일 전송</li>
<li>브라우저는 페이지를 렌더링하지만 interactive하진 않음</li>
<li>브라우저가 자바스크립트를 다운로드</li>
<li>자바스크립트 실행</li>
<li>interactive contents</li>
</ol>
<h2 id="spa-웹앱에서-왜-ssr이-필요한가요">SPA 웹앱에서 왜 SSR이 필요한가요?</h2>
<ul>
<li>초기 페이지 렌더링이 빠릅니다.</li>
<li>모든 HTML 페이지를 인덱싱할 수 있습니다.
➡️ 페이지가 처음 로딩될때 deep link들이 있는 콘텐츠가 많은 사이트에 좋을 수 있습니다.<h1 id="nextjs-처음으로-세팅하기">Next.js 처음으로 세팅하기</h1>
해당 명령어로 next 를 시작합니다.<pre><code>yarn create next-app</code></pre></li>
</ul>
<p><code>yarn start</code> 명령어 시 실행되는 파일: <a href="https://github.com/vercel/next.js/blob/canary/packages/next/cli/next-start.ts">https://github.com/vercel/next.js/blob/canary/packages/next/cli/next-start.ts</a>
cli에서 프로덕션 서버 실행을 위해 명령어를 입력하면 실행되는 파일인 것 같고, 빌드를 하지 않고 바로 실행하려고 하니, 앱을 먼저 빌드하는 에러가 뜹니다.</p>
<p>참고</p>
<ul>
<li><a href="https://dev.to/producthackers/explaining-ssr-csr-in-javascript-3iig">https://dev.to/producthackers/explaining-ssr-csr-in-javascript-3iig</a></li>
<li><a href="https://www.pluralsight.com/guides/pros-and-cons-of-client-side-rendering">https://www.pluralsight.com/guides/pros-and-cons-of-client-side-rendering</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 리스트 매핑(mapping)시 인덱스보다는 유니크한 키 값이 필요한 이유 ]]></title>
            <link>https://velog.io/@riley_dev/React-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%A7%A4%ED%95%91mapping%EC%8B%9C-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%B3%B4%EB%8B%A4%EB%8A%94-%EC%9C%A0%EB%8B%88%ED%81%AC%ED%95%9C-%ED%82%A4-%EA%B0%92%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@riley_dev/React-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%A7%A4%ED%95%91mapping%EC%8B%9C-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%B3%B4%EB%8B%A4%EB%8A%94-%EC%9C%A0%EB%8B%88%ED%81%AC%ED%95%9C-%ED%82%A4-%EA%B0%92%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Tue, 27 Sep 2022 07:33:38 GMT</pubDate>
            <description><![CDATA[<p>JSX 요소를 map() 안에 바로 사용하는 경우 항상 <code>key</code> 값이 필요하다.
해당 이유는 리액트 공식문서에 잘 나와있다. 이전에 읽고 넘어갔었는데 막상 면접에서 질문이 나오니 명확한 답변을 하기 어려워 이렇게 다시 정리해보게 되었다.</p>
<h2 id="key">KEY</h2>
<p><code>key</code> 는 각 어떤 아이템이 어떤 컴포넌트에 해당하는지 리액트에게 알려주는 역할을 하고, 이에 맞게 매칭시켜준다. 이는 배열 아이템을 움직이거나(정렬 등의 문제로) 삽입, 삭제를 하는 경우 중요한 부분이다. 그래서 키를 잘 선택해야 하는데, 데이터 안에 키를 포함시키는 것이 좋다.</p>
<h2 id="key-규칙">KEY 규칙</h2>
<ul>
<li>siblings 사이에서 유니크한 값이여야하지만, 다른 배열의 JSX 노드라면 같은 키를 사용해도 괜찮다.</li>
<li>키는 변경되면 안된다. 렌더시에 만들지 않아야 한다.</li>
</ul>
<h2 id="리액트는-왜-key가-필요한가">리액트는 왜 KEY가 필요한가?</h2>
<blockquote>
<p>컴퓨터에 이름없는 파일들이 여러가지 있다고 생각해보자. 첫번째 파일, 두번째 파일, 세번째 파일.... 
파일을 지우기 시작하면 어떤 파일이 무엇인지 헷갈리게 된다. 두번쨰 파일이 첫번째가 되고, 세번째가 두번째가 되고.. 이런식이다.</p>
</blockquote>
<p>폴더의 파일명과 배열에서의 JSK 키는 비슷한 목적을 가진다. sibling 사이에서 아이템을 식별하기 위함인 것이다. 렌더링중 재배열때문에 position이 변경되다고 해도, 리액트는 lifetime 동안 해당 아이템을 식별한다. </p>
<h2 id="함정">함정</h2>
<h3 id="인덱스-키">인덱스 키</h3>
<p>사실 key를 주지 않으면 리액트는 인덱스를 키값으로 기본으로 사용한다. 하지만 배열이 재배치 되는 경우 아이템의 순서는 변경된다. 인덱스 키는 버그를 만들 수도 있다.
중간에 아이템을 삭제, 삽입하는 경우, <code>key</code>가 이전과 동일하다면 리액트는 동일한 DOM element를 보여주게 된다(❗️bug)</p>
<h3 id="mathrandom을-사용한-키">Math.random()을 사용한 키</h3>
<p>이 경우, 렌더시 키 값이 절대 매칭되지 않아서, 매번 컴포넌트와 DOM이 새로 만들어진다. 속도도 느릴뿐 아니라 아이템의 사용자 입력값 또한 잃어버리게 된다.</p>
<p>참고: <a href="https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318">https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] vite에서 환경변수 .env 설정하기]]></title>
            <link>https://velog.io/@riley_dev/React-vite%EC%97%90%EC%84%9C-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-.env-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@riley_dev/React-vite%EC%97%90%EC%84%9C-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-.env-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 18 Aug 2022 07:49:00 GMT</pubDate>
            <description><![CDATA[<h2 id="env"><code>.env</code></h2>
<p>환경변수는 번들러에 따라 다르게 설정된다는 사실 (프로젝트에 맞는 설정이 필요한 것이었다)
알게된 계기는 프로젝트 실습이었다.(이래서 역시 이것저것 만들어봐야 하나 싶다)</p>
<ul>
<li>그리고 참고로, 환경변수는 빌드 타임에 embed 되기 때문에, 변경된다면 앱을 리로드 해주도록 하자.<h2 id="cra">CRA</h2>
가장 흔하게 리액트앱을 만드는 CRA (create-react-app)의 경우 <code>.env</code> 파일에 <code>REACT_APP_</code>을 prefix로 써야 리액트 앱이 인식할 수 있다.
그리고 앱에서 <code>process.env.</code>으로 접근하여 사용할 수 있었다.</li>
</ul>
<h2 id="vite">Vite</h2>
<p>하지만 Vite의 경우는 다르다.
일단 vite는 따로 dotenv 패키지를 설치해주지 않아도 되고, 공식 문서에 따르면 파일 prefix를 <code>VITE_</code>로 적어줘야 한다고 한다. 
그리고 앱에서 <code>import.meata.env.</code>로 접근할 수 있다.</p>
<p>이전에 CRA와 node.js에서도 (dotenv 설치했을때) <code>process.env</code>로 접근했었기 때문에 당연히 vite앱에서도 동일하게 쓰다가 계속 에러가 나서 확인해보니, 잘못된 방법을 시도하고 있었다.
공식문서 config 부분에 해당부분의 설명이 잘 나와있었고, 원한다면 prefix 또한 커스텀 또한 가능 한 것 같다.</p>
<p>나는 이런식으로 vite에서 환경변수에 접근했다.</p>
<pre><code>const {
    VITE_FIREBASE_API_KEY,
    VITE_FIREBASE_AUTH_DOMAIN,
    VITE_FIREBASE_PROJECT_ID,
    VITE_FIREBASE_STORAGE_BUCKET,
    VITE_FIREBASE_SENDER_ID,
    VITE_FIREBASE_APP_ID,
    VITE_FIREBASE_MEASUREMENT_ID,
} = import.meta.env

const firebaseConfig = {
    apiKey: VITE_FIREBASE_API_KEY,
    authDomain: VITE_FIREBASE_AUTH_DOMAIN,
    projectId: VITE_FIREBASE_PROJECT_ID,
    storageBucket: VITE_FIREBASE_STORAGE_BUCKET,
    messagingSenderId: VITE_FIREBASE_SENDER_ID,
    appId: VITE_FIREBASE_APP_ID,
    measurementId: VITE_FIREBASE_MEASUREMENT_ID,
}
</code></pre><p>그리고 내가했던 또 다른 실수는 <code>.env</code>파일을 <code>src</code> 하위 디렉토리에 넣었었다는 사실..
prefix 를 제대로 바꾼 뒤에도 왜 대체 안되는 걸까? 하고 끙끙대다가 <code>root</code> 하위에 있지 않다는 사실을 깨달았다.</p>
<p>누군가는 나와 같은 실수를 하지 않길 바라며 🥲</p>
]]></description>
        </item>
    </channel>
</rss>