<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Theo14</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 02 Dec 2024 09:08:28 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Theo14</title>
            <url>https://velog.velcdn.com/images/theo_jin/profile/ec5638a9-9d63-497c-8f6b-d5d8c951e0b8/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Theo14. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/theo_jin" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[무한 스크롤에서 마지막 페이지 처리하기]]></title>
            <link>https://velog.io/@theo_jin/%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4%EC%97%90%EC%84%9C-%EB%A7%88%EC%A7%80%EB%A7%89-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@theo_jin/%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4%EC%97%90%EC%84%9C-%EB%A7%88%EC%A7%80%EB%A7%89-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 02 Dec 2024 09:08:28 GMT</pubDate>
            <description><![CDATA[<p><a href="https://github.com/theo-jin/show-me-the-recipes">SHOW ME THE RECIPES 프로젝트</a>에서<code>useInfiniteQuery</code>와 <code>react-intersection-observer</code>를 활용한 무한 스크롤 구현했다.
<code>useInfiniteQuery</code>를 사용해 데이터를 페이징하여 가져오고, <code>react-intersection-observer</code>를 활용해 사용자가 스크롤할 때 다음 페이지를 자동으로 로드하는 무한 스크롤 기능을 구현하였다.실질적으로 페칭되는 getRecipes는 action처리 하였다. </p>
<pre><code class="language-ts">//useInfiniteRecipes.ts
const useInfiniteRecipes = () =&gt; {
  return useInfiniteQuery({
    queryKey: [&#39;recipes&#39;],
    queryFn: ({ pageParam = 1 }) =&gt; getRecipes(pageParam),
    getNextPageParam: (lastPage, allPages) =&gt; {
      const nextPage = allPages.length + 1
      const totalFetched = allPages.length * lastPage.limit
      if (totalFetched &lt; lastPage.total) {
        return nextPage
      }
      return undefined
    },
    initialPageParam: 1,
  })
}</code></pre>
<p>그리고 RecipesList페이지에서 <code>isAllDataLoaded</code>를 구하기위해 다음과 같은 연산을 사용했다.</p>
<pre><code class="language-tsx">//RecipesList.tsx    
  const {
    data,
    isLoading,
    error,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuotes()
  const { ref, inView } = useInView()

  useEffect(() =&gt; {
    if (inView &amp;&amp; hasNextPage &amp;&amp; !isFetchingNextPage) {
      fetchNextPage()
    }
  }, [inView, fetchNextPage, hasNextPage, isFetchingNextPage])

const totalRecipes =
    data?.pages?.reduce((sum, { recipes }) =&gt; sum + recipes.length, 0) ?? 0
  const lastPage = data?.pages?.[data.pages.length - 1]
  const isAllDataLoaded = lastPage?.total === totalrecipes 

  // &lt;--중간 생략--&gt;
   &lt;div ref={ref}&gt;
        {isAllDataLoaded ? &#39;No more quotes to display.&#39; : &#39;Loading more...&#39;}
      &lt;/div&gt;
</code></pre>
<p>하지만 이러한 접근 방식이 매번 RecipesList페이지에서 <code>isAllDataLoaded</code>를 구해야하는 불필요한 연산 로직이 반복되는 문제 발생시킨다는 것을 발견했다.
문제를 해결하기위해 <a href="https://tanstack.com/query/latest/docs/framework/react/reference/useInfiniteQuery">tanstackQuery 공식문서 useInfiniteQuery</a> 다시 살펴 보았다. 
공식문서에 getNextPageParam 옵션이 존재했고 getNextPageParam함수의 특징은 이러하다. </p>
<blockquote>
</blockquote>
<p>getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =&gt; TPageParam | undefined | null</p>
<ul>
<li><p>Required </p>
</li>
<li><p>When new data is received for this query, this function receives both the last page of the infinite list of data and the full array of all pages, as well as pageParam information. </p>
</li>
<li><p>It should return a single variable that will be passed as the last optional parameter to your query function. </p>
</li>
<li><p>Return undefined or null to indicate there is no next page available.</p>
</li>
<li><p>이 쿼리에서 새로운 데이터가 수신되면, 이 함수는 무한 데이터 목록의 마지막 페이지와 전체 페이지 배열, 그리고 pageParam 정보를 인수로 받는다.</p>
</li>
<li><p>쿼리 함수의 마지막 선택적 매개변수로 전달될 단일 변수를 반환해야 한다. </p>
</li>
<li><p>다음 페이지가 없음을 나타내려면 undefined 또는 null을 반환한다.</p>
</li>
</ul>
<p>getNextPageParam 옵션을 통해 다음 페이지가 가져올 수 있는지 여부를 알아 낼 수 있음으로, 다음 페이지가 있으면 true로 설정할수 있는 값이었다. 이 값을 통해 nextPage가 있으면 true, 없으면 undefined를 리턴한다는 특징을 이용해 <code>isAllDataLoaded</code>연산을 삭제하고 진행했다. </p>
<pre><code class="language-ts">//useInfiniteRecipes.ts
const useInfiniteRecipes = () =&gt; {
  return useInfiniteQuery({
    queryKey: [&#39;recipes&#39;],
    queryFn: ({ pageParam }) =&gt; getRecipes(pageParam),
    getNextPageParam: (lastPage, _, lastPageParam) =&gt; {
      return lastPage.limit === 0 ? undefined : lastPageParam + 1;
    },
    initialPageParam: 1,
  });
};</code></pre>
<p>그리고 마지막에 <code>isAllDataLoaded</code>를 지우고 <code>isAllDataLoaded</code>대신 <code>isFetchingNextPage</code>와<code>hasNextPage</code>를 통해 마지막 데이터 인지 표시하였다. </p>
<pre><code class="language-tsx">//RecipesList.tsx
      &lt;div ref={ref}&gt;
          {isFetchingNextPage
            ? &#39;Loading more...&#39;
            : hasNextPage
            ? &#39;Load More&#39;
            : &#39;No more recipes to display.&#39;}
        &lt;/div&gt;</code></pre>
<h2 id="reference">Reference</h2>
<p><a href="https://tanstack.com/query/latest/docs/framework/react/reference/useInfiniteQuery">https://tanstack.com/query/latest/docs/framework/react/reference/useInfiniteQuery</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] "use client"는 CSR인가요 SSR인가요?]]></title>
            <link>https://velog.io/@theo_jin/ReferenceErrorwindow-is-not-defined-at-XXX-6nnpndmb</link>
            <guid>https://velog.io/@theo_jin/ReferenceErrorwindow-is-not-defined-at-XXX-6nnpndmb</guid>
            <pubDate>Wed, 23 Oct 2024 09:21:04 GMT</pubDate>
            <description><![CDATA[<p>앞서 <a href="https://velog.io/@theo_jin/Funnel%ED%8C%A8%ED%84%B4%EC%9C%BC%EB%A1%9C-%ED%80%B4%EC%A6%88-%EB%A7%8C%EB%93%A4%EA%B8%B0All-That-Arsenal">Funnel패턴으로 퀴즈 만들기(All That Arsenal)-1</a> 에서 Funnel.tsx 를 개발하던 와중 다음과 같은 에러가 떴다. </p>
<p><img src="https://velog.velcdn.com/images/theo_jin/post/969d0e07-6328-4c85-90a1-3b036fa60e88/image.png" alt=""></p>
<p><strong>ReferenceError:window is not defined</strong> 오류가 왜나왔는지 어떻게 해결하는지 알아보자.</p>
<h2 id="referenceerrorwindow-is-not-defined가-나온-이유">ReferenceError:window is not defined가 나온 이유?</h2>
<p>해당 오류 코드가 나오는 이유는 코드가 window객체를 정의 되지 않은 컨텍스트에서 엑세스하려고 시도할 때 발생한다. 이 오류는 주로 react.js어플리케이션에서 ssr을사용하는 경우 혹은 코드가 웹브라우저 환경 외부에서 실행되는 경우에 나타난다.</p>
<p><strong>어???Funnel은 &quot;use client&quot;환경에서 개발해서SSR이 아닐텐데??</strong></p>
<p>내가 알고 있는 지식으로는 &quot;use client&quot; 상에서 이용한다면 해당 페이지는 CSR(Client Side Rendering)이기 때문에 굳이 SSR을 false로 지정하지 않아도 된다고 생각했다. 하지만, stackoverflow에서 <a href="https://stackoverflow.com/questions/75692116/next-js-13-window-is-not-defined">Next.js 13 &quot;window is not defined&quot;</a>의 첫번째 답변을 통해 &quot;use client&quot;를 지정하더라도 페이지를 전적으로 클라이언트에서 렌더링하지 않는다라는 답변이 있었다. 서버에서 컴포넌트 코드를 실행하고 그래서 서버에서 사용할 수 없는 window와 같은 것들을 사용할 시에 이 점을 고려해야 한다라고 설명하고 있었다. </p>
<h2 id="use-client는-csr인가요-ssr인가요">use client는 CSR인가요 SSR인가요?</h2>
<p>&quot;use client&quot;를 사용하면 무조건 CSR이라고 생각하여 Next.js에서의 렌더링을 다시 알아봐야했다. Next.js는 프리렌더링 기능을 가진 리액트 프레임워크이다. 이건 페이지마다 Next.js가 HTML을 생성하여 SEO와 성능을 향상시키려 한다는 것을 의미한다. </p>
<h3 id="nextjs에서-프리렌더링과-하이드레이션-개념을-다시-알아보자">Next.js에서 프리렌더링과 하이드레이션 개념을 다시 알아보자</h3>
<p> Next.js는 모든 페이지를 미리 렌더링(pre-render)한다. 서버에서 HTML을 문자열로 가져온 후에, 클라이언트에서 서버에서 보내준 HTML을 hydrate() 혹은 render()하여 브라우저에 렌더링된다</p>
<p>Next.js는 서버에서 보여줄 HTML 컨텐츠를 가져오기 때문에 재차 render() 함수로 HTML을 생성하여 DOM을 그리는 일은 비효율적이다.</p>
<p>따라서 hydrate() 함수로 서버에서 받아온 HTML에 유저가 상호작용할 수 있는 이벤트 리스너만 연결한다. Next.js가 모든 일을 클라이언트 측에서 모든 작업을 수행하는 것이 아니라, 각 페이지의 HTML을 미리 생성하는 것이다.</p>
<p>생성된 HTML은 해당 페이지에 필요한 최소한의 자바스크립트 코드와 연결된다. 그 후 브라우저에 의해 페이지가 로드되면, 자바스크립트 코드가 실행되어 페이지와 유저가 상호작용할 수 있게 된다.</p>
<p>Next.js에서 미리 렌더링 하는 방식은 두 가지로 나뉘며, HTML이 생성되는 시점이 다르다. 하나는 빌드 타임에 HTML에 생성되어 매 요청마다 이를 재사용하게 해주는 SSG(Static-site Generation)이고, 다른 하나는 매 요청마다 HTML을 생성하는 SSR(Server-side Rendering)이다.</p>
<p><strong>그래서 &quot;use client는 CSR인가요 SSR인가요?&quot;라는 질문의 답</strong>은 처음에는 서버에서 렌더링되고 이후 클라이언트에서 하이드레이션되어 실행된다. 그리고 상태 업데이트나 상호작용은 클라이언트에서 처리되기때문에 <strong>&quot;둘다&quot;</strong>라고 할수 있을 것 같다.</p>
<h2 id="해결방법">해결방법</h2>
<p>다시 돌아와서 ReferenceError:window is not defined라는 오류를 해결해보자. 
<a href="https://dev.to/vvo/how-to-solve-window-is-not-defined-errors-in-react-and-next-js-5f97">How to solve &quot;window is not defined&quot; errors in React and Next.js</a>를 참고했다.</p>
<h3 id="1-dynamic-loading사용하기">1. dynamic loading사용하기.</h3>
<p>Funnel컴포넌트를 동적 임포트를 사용하여 ssr: false 옵션으로 불러오는 방법을 사용했다.이렇게 하면 컴포넌트가 서버 측에서 렌더링되지 않게 된다.
특히 window를 사용하는 외부 모듈을 임포트할 때 이 방법이 효과적이라고 한다.</p>
<p>이번 프로젝트에서 사용한 방법이다.</p>
<pre><code class="language-ts">import dynamic from &quot;next/dynamic&quot;;

// 컴포넌트를 dynamic import로 감싸고 ssr을 false로 설정
const FunnelComponent = dynamic(() =&gt; Promise.resolve(FunnelContent), {
    ssr: false,
});
//....

// 메인 export 컴포넌트
export default function Funnel() {
    return &lt;FunnelComponent /&gt;;
}
</code></pre>
<h3 id="2-type-of">2. type of</h3>
<pre><code class="language-ts">if (typeof window !== &quot;undefined&quot;) {
  // 브라우저 코드
}</code></pre>
<p>typeof는 window를 평가하지 않고 단순히 그 타입만 확인하기 때문에 Node.js 환경에서는 &quot;undefined&quot;로 처리된다.그러다가 Node.js 환경이 아닌(!==&quot;undefined&quot;)window를 참조 할 수 있는 시점이되면 브라우저 코드를 실행하는 코드이다.</p>
<h3 id="3-useeffect-hook-사용하기">3. useEffect hook 사용하기</h3>
<p>마지막 방식은  useEffect React 훅을 사용하는 것이다.useEffect는 렌더링 단계에서만 실행되기 때문에 서버에서는 실행되지 않는다.</p>
<pre><code class="language-ts">
// components/Scroll.js

import React, { useEffect } from &quot;react&quot;;

export default function Scroll() {
  useEffect(function mount() {
    function onScroll() {
      console.log(&quot;scroll!&quot;);
    }

    window.addEventListener(&quot;scroll&quot;, onScroll);

    return function unMount() {
      window.removeEventListener(&quot;scroll&quot;, onScroll);
    };
  });

  return null;
}
</code></pre>
<pre><code class="language-ts">// pages/index.js

import Scroll from &quot;../components/Scroll&quot;;

export default function Home() {
  return (
    &lt;div style={{ minHeight: &quot;1000px&quot; }}&gt;
      &lt;h1&gt;Home&lt;/h1&gt;
      &lt;Scroll /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>위의 예제 처럼 useEffect를 사용하는 방식은 컴포넌트가 마운트/언마운트될 때 리스너를 등록하고 해제하는 것이다. 하지만 마운트 시에만 리스너를 등록하고 이후 렌더링 이벤트는 무시하고 싶다면 다음과 같이 사용 할 수도 있다.</p>
<pre><code class="language-ts">
// components/Scroll.js

import React, { useEffect } from &quot;react&quot;;

export default function Scroll() {
  useEffect(function onFirstMount() {
    function onScroll() {
      console.log(&quot;scroll!&quot;);
    }

    window.addEventListener(&quot;scroll&quot;, onScroll);
  }, []); // empty dependencies array means &quot;run this once on first mount&quot;

  return null;
}
</code></pre>
<h3 id="마무리">마무리</h3>
<p>이번 에러를 겪으며 가장 크게 배운 점은 &quot;use client는 CSR인가요, SSR인가요?&quot;라는 내 질문에 대해 망설임 없이 &quot;CSR&quot;이라고 답했던 순간이었다. Next.js에서 사용되는 용어였고, 그에 따라 pre-rendering과 hydration을 공부했었지만, 학습과 실제 개발에서의 사용을 분리해서 이해하고 있었다는 점이 의외로 실망스러웠다. CS 스터디에서 얻은 지식들이 많지만, 이를 실제 개발 환경에 연결하는 공부가 더 필요하다는 생각이 들었다.</p>
<h3 id="reference">Reference</h3>
<p><a href="https://dev.to/vvo/how-to-solve-window-is-not-defined-errors-in-react-and-next-js-5f97">https://dev.to/vvo/how-to-solve-window-is-not-defined-errors-in-react-and-next-js-5f97</a></p>
<p><a href="https://pusha.tistory.com/entry/Nextjs-Reactjs-window-is-not-defined-%EC%9B%90%EC%9D%B8-%EB%B0%8F-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95">https://pusha.tistory.com/entry/Nextjs-Reactjs-window-is-not-defined-%EC%9B%90%EC%9D%B8-%EB%B0%8F-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</a></p>
<p><a href="https://velog.io/@yunsungyang-omc/Next.js-%EC%97%90%EB%9F%AC%EB%A1%9C%EA%B7%B8-window-is-not-defined">https://velog.io/@yunsungyang-omc/Next.js-%EC%97%90%EB%9F%AC%EB%A1%9C%EA%B7%B8-window-is-not-defined</a></p>
<p><a href="https://stackoverflow.com/questions/75692116/next-js-13-window-is-not-defined">https://stackoverflow.com/questions/75692116/next-js-13-window-is-not-defined</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Funnel패턴으로 퀴즈 만들기(All That Arsenal)-1]]></title>
            <link>https://velog.io/@theo_jin/Funnel%ED%8C%A8%ED%84%B4%EC%9C%BC%EB%A1%9C-%ED%80%B4%EC%A6%88-%EB%A7%8C%EB%93%A4%EA%B8%B0All-That-Arsenal</link>
            <guid>https://velog.io/@theo_jin/Funnel%ED%8C%A8%ED%84%B4%EC%9C%BC%EB%A1%9C-%ED%80%B4%EC%A6%88-%EB%A7%8C%EB%93%A4%EA%B8%B0All-That-Arsenal</guid>
            <pubDate>Wed, 09 Oct 2024 08:53:53 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기전에">들어가기전에...</h2>
<p>All That Arsenal 프로젝트에 새로운 기능을 추가하는 과정에서 퀴즈 기능이 있으면 재미있겠다는 생각이 들었다. 각 페이지마다 서로 다른 퀴즈 문제를 제시하고, 사용자는 각 페이지에서 문제를 풀며 그 결과를 전역 상태로 저장한다. 이후 최종적으로 정답과 사용자의 답안을 비교하는 방식으로 기능을 구현하려 했다.</p>
<p>이 아이디어를 스터디원과 공유했는데, 스터디원이 제안한 방식은 조금 달랐다. 스터디원은 <strong>Funnel 패턴</strong>이라는 것이 있고, 해당 라이브러리가 있어 그것을 적용해보는 게 어떻겠냐는 의견을 주었다. Funnel패턴에 대해 알아본 후,이 방식을 고려해보기로 했다. 이 포스트는 Funnel 패턴을 본격적으로 도입하기 전에 내가 구상한 방식과 Funnel 패턴의 차이점을 이해하기 위한 학습 기록이다.</p>
<h2 id="funnel패턴을-고려하지-않은-초기-설계">Funnel패턴을 고려하지 않은 초기 설계</h2>
<p><img src="https://velog.velcdn.com/images/theo_jin/post/dfcffbf1-28c9-44e6-bb00-3fc76d0ad944/image.png" alt="">
퀴즈 기능을 진행하지 않았지만 대략적으로 기능을 만들기전에 구상했던 것은 위와 같이 </p>
<p>*<em>1. quiz를 dynamic routes 페이지에서 문제를 내고 페이지간에 router.push(&quot;/이동할 페이지&quot;)나 link를 이용하여 이동하고, *</em></p>
<p><strong>2. 사용자가 기입한 답들은 전역상태관리툴로 관리하여 최종적으로 정답을 데이터 페칭하여 비교</strong></p>
<p>하는 방식으로 진행하려고 했었다. 큰 문제점이 없다고 생각했지만 다음과 같은 패턴에서는 유지보수적 관점에서 몇가지 아쉬운 점이 있다는 것을 알게 되었다.</p>
<p><a href="https://www.youtube.com/watch?v=NwLWX2RNVcw">토스ㅣSLASH 23 - 퍼널: 쏟아지는 페이지 한 방에 관리하기</a>라는 영상에서도 도입부에 위와 비슷한 설계를 소개하였다.</p>
<p>간단하게 설명하자면
<strong>1. 페이지를 위한 파일 4개를 만들고 버튼을 누르면 router.push로 이동.</strong>
<strong>2. api는 집주소 페이지에서 호출 전역상태를 사용하여 상태유지한다.</strong></p>
<p>내가 초기에 설계한 것과 유사했다.</p>
<p><img src="https://velog.velcdn.com/images/theo_jin/post/4f39c71a-e29a-49c2-b593-511ce4f6f5d5/image.png" alt="">
하지만 영상에서는 몇가지 아쉬운점이 있었다고 설명하고있다.</p>
<h3 id="아쉬운점-1-페이지-흐름이-흩어져-있다">아쉬운점 1. 페이지 흐름이 흩어져 있다.</h3>
<p>가입방식을 선택한 뒤 주민번호 -&gt; 집주소 -&gt; 가입완료 순으로 흐름을 파학하기 위해 3개 파일을 넘나들며 코드를 따라가야 한다는 어려움이 있다.</p>
<h3 id="아쉬운점-2-한가지-목적을-위한-상태가-흩어져있다">아쉬운점 2. 한가지 목적을 위한 상태가 흩어져있다.</h3>
<p>집주소 페이지에서 호출되는 고객 등록 API에서 쓰이는 데이터는 서로 다른 세 페이지에서 전역상태를 통해 수집하고 있다. 여기서 문제가 상태를 사용하는 곳과 수집하는 곳이 달라 추후에 API기능을 추가하거나 버그를 수정할때 상태를 이용한 집주소 페이지뿐만아니라 나머지 세 페이지 전체를 대상으로 흐름을 추적해야한다. </p>
<p>기존에 생각했던 설계에서는 위와같이 유지보수적으로 아쉬운점을 발견하였다. 이렇게 흩어져있는 페이지 흐름과 상태를 개선하여 응집도를 높이기 위해 Funnel에 대해서 알아보기로 했다.</p>
<h1 id="funnel">Funnel</h1>
<p>Funnel은 마케팅 업계에서 사용하는 용어이고,유저가 서비스에 들어와서 최종 목표지점에 이르기까지 조금씩 이탈해서 <strong>&#39;깔대기&#39;</strong> 와 같은 모양을 띠기때문에 붙여진 이름이다.
프론트엔드에서 퍼널 패턴은 사용자가 애플리케이션에서 최종단계에 도달하기 까지 거치는 단계를 의미하기도 한다. </p>
<blockquote>
<ul>
<li>퍼널 이미지 예시 : 토스ㅣSLASH 23 - 퍼널: 쏟아지는 페이지 한 방에 관리하기에서
<img src="https://velog.velcdn.com/images/theo_jin/post/24914d77-9798-4728-9203-22c17705b190/image.png" alt=""></li>
</ul>
</blockquote>
<p>Slash에서 소개된 useFunnel은 서비스 종료로 인해 <a href="https://use-funnel.slash.page/ko">@use-funnel</a>을 사용하였다.</p>
<h2 id="use-funnel을-사용한-이유">@use-funnel을 사용한 이유</h2>
<p><code>@use-funnel</code>을 사용하게 된 가장 큰 이유는 코드의 응집도를 개선하기 위함이었다. 초기 설계에서는 Funnel 패턴을 고려하지 않아 페이지 흐름과 상태 관리가 분산되어 있었고, 이로 인해 코드의 유지보수성이 떨어지는 문제가 있었다. <code>@use-funnel</code>을 적용함으로써 관련된 코드를 한 곳에 모아 관리할 수 있었고, 이는 자연스럽게 코드의 응집도 개선으로 이어졌다. 결과적으로 <strong>관련된 코드들이 가까운 위치에 배치</strong>되어 가독성과 유지보수성이 향상되었기 때문에 <code>@use-funnel</code>을 도입하게 되었다.</p>
<h2 id="개념">개념</h2>
<p><code>@use-funnel</code>에서 사용하는 용어</p>
<ul>
<li>step : 사용자가 하나의 목표를 위해 여러 화면에 걸쳐서 필요한 값을 입력할 때, 각 화면이 step이다. </li>
<li>context : 각 step에서 입력한 값의 상태.</li>
<li>history : 전체 step의 이동과 각 step에서 입력한 context의 변경 기록을 가지고 있는 배열.</li>
</ul>
<h2 id="설계">설계</h2>
<p><img src="https://velog.velcdn.com/images/theo_jin/post/0f5da79c-59ec-4037-94c9-39a2c754780d/image.png" alt=""></p>
<p>설계는 다음과 같이 하였다.</p>
<p><strong>1. 사용자가 각 단계에서 문제를 푼다면 각 <code>step</code>에서의 답들은 <code>context</code>로 관리된다</strong>.</p>
<p>*<em>2. 마지막 <code>Result</code>단계에서 사용자의 대답들(Context)을 API통신으로 <code>API Routes</code>에 보낸다. *</em></p>
<p><strong>3. <code>API Routes</code>에서는 DB에서 정답들을 가져와서 대답들(Context)과 비교하여 채점을 하여 다시 <code>Result</code>페이지에 보여주는 방식으로 진행하였다.</strong></p>
<h3 id="채점-기능을-프론트에서-처리하지-않은-이유-보안의-관점">채점 기능을 프론트에서 처리하지 않은 이유: 보안의 관점</h3>
<p>채점을 프론트엔드에서 처리할 경우, 정답 데이터와 사용자의 응답이 클라이언트 측 코드에 노출될 가능성이 크다.이것은 다음과 같은 보안 문제로 이어질 수도 있다.</p>
<ul>
<li><p><strong>데이터 탈취 가능성</strong>
클라이언트 측 코드에 저장된 정답 데이터는 브라우저 개발자 도구를 통해 쉽게 접근할 수 있다.  채점 과정에서 정답을 확인하여 점수 결과를 다르게 만들 수도 있다.</p>
</li>
<li><p><strong>코드 변조</strong>
사용자는 자신의 로컬 환경에서 자바스크립트 코드를 쉽게 변조할 수 있다. 예를 들어, 채점 로직을 직접 수정해 실제로는 틀린 답을 맞다고 처리하도록 조작할 수 있다. 이러한 조작을 방지하기 위해서는 채점 로직을 서버 측에서 실행하는 것이 맞다고 판단하였다.</p>
</li>
</ul>
<p>뒤에 나오는 <strong>API통신 및 백엔드에서의 채점</strong>에서 API Routes에 채점 로직을 넣은 이유에 대해서 설명한다. </p>
<h2 id="기본-세팅">기본 세팅</h2>
<p>app router에서 패키지 명령어:</p>
<pre><code>npm install @use-funnel/browser --save</code></pre><h3 id="step별--context-정의하기">step별  context 정의하기</h3>
<p>step1은 아무것도 입력되지 않은 상태이기에 ? (optional)로 정의 하였고 단계가 진행될수록 앞의 필드가 필수적으로 입력되어 있어야 한다 그래서 step이 진행될 수록 optional도 줄어들게 된다.<br>이렇게 각 step의 상태를 타입으로 정의하면 코드에서 타입 안전성을 유지할 수 있고, step별로 필요한 정보를 쉽게 추적할 수 있다.</p>
<pre><code class="language-ts">//_type/index.ts
export interface step1 {
    step1?: string;
    step2?: string;
    step3?: string;
    step4?: string;
    step5?: string;
}
export interface step2 {
    step1: string;
    step2?: string;
    step3?: string;
    step4?: string;
    step5?: string;
}
export interface step3 {
    step1: string;
    step2: string;
    step3?: string;
    step4?: string;
    step5?: string;
}
export interface step4 {
    step1: string;
    step2: string;
    step3: string;
    step4?: string;
    step5?: string;
}
export interface step5 {
    step1: string;
    step2: string;
    step3: string;
    step4: string;
    step5?: string;
}
export interface result {
    step1: string;
    step2: string;
    step3: string;
    step4: string;
    step5: string;
}</code></pre>
<h3 id="초기-단계-설정하기">초기 단계 설정하기</h3>
<p>다음은 <code>useFunnel()</code>을 사용해 초기 단계를 설정하는 과정이다. 먼저 <code>step</code>을 key로 한 <code>context</code> 객체를 <code>useFunnel()</code>의 제네릭으로 지정한다. 앞 단계에서 각 단계의 상태를 정의한 타입을 <code>useFunnel()</code>에 전달해서 사용한다.그리고 해당 컴포넌트에 진입했을 때 사용할 <code>step</code>과 <code>context</code> 객체를 initial 프로퍼티에 지정한다.</p>
<p> 본 프로젝트는 초기단계인 &quot;step1&quot;과 해당단계에서 사용할 빈 context객체를 설정했다.id는 한 컴포넌트에 여러 퍼널이 있을 때 구분하기 위한 고유 식별자이다. </p>
<pre><code class="language-ts">//Funnel.tsx
import { useFunnel } from &quot;@use-funnel/browser&quot;;
import type { result, step1, step2, step3, step4, step5 } from &quot;@/app/_types&quot;;


function FunnelContent() {
    const funnel = useFunnel&lt;{
        step1: step1;
        step2: step2;
        step3: step3;
        step4: step4;
        step5: step5;
        result: result;
    }&gt;({
        id: &quot;my-funnel-app&quot;,
        initial: {
            step: &quot;step1&quot;,
            context: {},
        },
    });
    // ...
}</code></pre>
<h3 id="단계별-context와-history-사용하기">단계별 context와 history 사용하기</h3>
<p>useFunnel()에서 반환된 step에 따라 context와 history를 사용한다. 각 단계별로 UI를 구성하고, 필요한 상태와 이벤트를 처리할 수 있다.여기서 useFunnel()이 반환하는 <code>&lt;funnel.Render /&gt;</code> 컴포넌트를 사용하였다.이 컴포넌트를 사용하여 단계별 UI를 더 간편하게 관리하였다.</p>
<p>주의) funnel안의 모든 context객체들을 작성하지 않으면 funnel.Render쪽에서 해당하는 속성이 없다는 오류가 발생한다.</p>
<pre><code class="language-ts">//Funnel.tsx
    return (
        &lt;div className=&quot;&quot;&gt;
            &lt;div className=&quot;my-5&quot;&gt;
                &lt;FunnelProgress step={funnel.step} /&gt;
            &lt;/div&gt;
            &lt;div&gt;
                &lt;funnel.Render
                    step1={({ history }) =&gt; (
                        &lt;Step1 onNext={(step1) =&gt; history.push(&quot;step2&quot;, { step1 })} /&gt;
                    )}
                    step2={({ history }) =&gt; (
                        &lt;Step2
                            onNext={(step2) =&gt; history.push(&quot;step3&quot;, { step2 })}
                            onPrev={() =&gt; history.push(&quot;step1&quot;)}
                        /&gt;
                    )}
                    step3={({ history }) =&gt; (
                        &lt;Step3
                            onNext={(step3) =&gt; history.push(&quot;step4&quot;, { step3 })}
                            onPrev={() =&gt; history.push(&quot;step2&quot;)}
                        /&gt;
                    )}
                    step4={({ history }) =&gt; (
                        &lt;Step4
                            onNext={(step4) =&gt; history.push(&quot;step5&quot;, { step4 })}
                            onPrev={() =&gt; history.push(&quot;step3&quot;)}
                        /&gt;
                    )}
                    step5={({ history }) =&gt; (
                        &lt;Step5
                            onNext={(step5) =&gt; history.push(&quot;result&quot;, { step5 })}
                            onPrev={() =&gt; history.push(&quot;step4&quot;)}
                        /&gt;
                    )}
                    result={({ context }) =&gt; &lt;QuizResult context={context} /&gt;}
                /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );</code></pre>
<h3 id="api통신-및-백엔드에서의-채점">API통신 및 백엔드에서의 채점</h3>
<p>앞서 설계를 설명할때에 클라이언트 사이드에서 채점을 진행할 때의 문제점에 대해서 설명했다. 다음은 채점 기능을 백엔드(API Routes)에서 처리한 이유에 대해서 설명한다. </p>
<ul>
<li><p><strong>서버에서의 안전한 처리</strong>
사용자의 대답은 클라이언트를 통해 API 요청으로 서버(API Routes)에 전달되며, 서버는 이를 안전하게 처리한다. 정답 데이터는 DB와 서버 내부에서만 존재하며, 외부에 노출되지 않으므로 데이터 유출 위험을 줄일 수 있다.</p>
</li>
<li><p><strong>로직의 무결성 유지</strong>
채점 로직을 서버에서 실행하므로, 사용자가 이를 변조할 가능성이 없다. 그래서 채점 시스템의 신뢰성을 크게 향상 시킬 수 있다.</p>
</li>
</ul>
<pre><code class="language-ts">//quizResult.ts
import { connectDB } from &quot;@/utils/database&quot;;
import { NextApiRequest, NextApiResponse } from &quot;next&quot;;
import { result } from &quot;@/app/_types&quot;;

export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse,
) {
    if (req.method !== &quot;POST&quot;) {
        return res.status(405).json({ message: &quot;Failed (Code:405)&quot; });
    }

    try {
        const { context } = req.body as { context: result };
        const client = await connectDB;
        const db = client.db(&quot;arsenal&quot;);

        // DB에서 정답 가져오기
        let [quizAnswer] = await db.collection(&quot;quizResult&quot;).find().toArray();

        const results = {
            step1: context.step1 === quizAnswer.step1,
            step2: context.step2 === quizAnswer.step2,
            step3: context.step3 === quizAnswer.step3,
            step4: context.step4 === quizAnswer.step4,
            step5: context.step5 === quizAnswer.step5,
        };

        // 정답 개수 계산
        const correctCount = Object.values(results).filter(Boolean).length;

        // 결과 반환
        return res.status(200).json({
            success: true,
            correctCount,
            results,
            totalQuestions: 5,
        });
    } catch (error) {
        console.error(&quot;Error on quiz results:&quot;, error);
        return res.status(500).json({
            success: false,
            message: &quot;Error on quiz results (Code:500)&quot;,
        });
    }
}
</code></pre>
<h3 id="번외-에러--에러-해결">번외: 에러 &amp; 에러 해결</h3>
<p><img src="https://velog.velcdn.com/images/theo_jin/post/ebe10d02-064e-4087-b601-9d22938900a6/image.png" alt="">
<strong>ReferenceError:window is not defined at Funnel</strong> 에러가 떴다. 해당 에러는 Next.js위에서 일부 custom훅을 사용할때 발생하는 에러로, Funnel패턴 학습과 무관하여 <a href="https://velog.io/@theo_jin/ReferenceErrorwindow-is-not-defined-at-XXX-6nnpndmb">다른
포스트</a>에서 이 에러에 대해서 다루었다. </p>
<h2 id="마무리-및-추후-개선사항">마무리 및 추후 개선사항.</h2>
<p><code>@use-funnel</code>을 사용해보니, Slah영상에서 소개된 <code>useFunnel</code>보다 훨씬 사용하기 편하다고 느꼈다. useFunnel 소개처럼 흩어져있는 페이지 흐름과 상태를 관리하기에 적합했고, 조금 더 로직에 집중할 수 있는 라이브러리 같았다. 다만, 내가 만든 프로젝트 경우 데이터 페칭을 서버컴포넌트에서 진행하여 <code>Funnel.tsx</code>에서 데이터 페칭을 하지 않고 API통신을 <code>QuizResult컴포넌트</code> 쪽에서 진행되고 있어 해당 라이브러리를 반쪽만 활용하고 있다는 생각이 들어 아쉬움이 남는다.(번외: 에러 &amp; 에러 해결에서 그 이유가 밝혀질 예정이다.)추후에 이것을 개선을 한다면, 후속포스트로 작성할 예정이다.</p>
<h3 id="추후-개선사항">추후 개선사항</h3>
<p>추후 개선사항으로 예정된 것은 의도하지 않은 종료나 새로고침이 발생 시 이를 해결하기 위해서 Local Storage나 클라이언트쪽의 스토리지를 통해 작성중인 답변을 임시 저장하는 기능을 추가하려고 계획중에 있다. 또한 admin 계정에서 문제와 답변을 작성하는 페이지도 계획에 있다. </p>
<h2 id="reference">Reference</h2>
<p><a href="https://www.youtube.com/watch?v=NwLWX2RNVcw">https://www.youtube.com/watch?v=NwLWX2RNVcw</a>
<a href="https://use-funnel.slash.page/ko/docs/overview">https://use-funnel.slash.page/ko/docs/overview</a>
<a href="https://toss.tech/article/engineering-note-1">https://toss.tech/article/engineering-note-1</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TailwindCSS & NEXT UI를 사용(All That Arsenal)]]></title>
            <link>https://velog.io/@theo_jin/TailwindCSS-NEXT-UI%EB%A5%BC-%EC%82%AC%EC%9A%A9All-That-Arsenal</link>
            <guid>https://velog.io/@theo_jin/TailwindCSS-NEXT-UI%EB%A5%BC-%EC%82%AC%EC%9A%A9All-That-Arsenal</guid>
            <pubDate>Thu, 26 Sep 2024 05:09:14 GMT</pubDate>
            <description><![CDATA[<h2 id="tailwindcss--next-ui를-사용한-이유">TailwindCSS &amp; NEXT UI를 사용한 이유</h2>
<h3 id="tailwindcss">TailwindCSS</h3>
<p>TailwindCSS는 제로 런타임으로 (런타임에 CSS를 계산할 필요가 없으므로 페이지 로딩 속도가 향상됩니다.)
동작하며 빠르다는 장점이 있었습니다.</p>
<blockquote>
<p>제로런타임:브라우저가 페이지를 렌더링할 때 실제로 사용되는 스타일만 포함하는 최적화된 CSS 파일을 빌드 타임에 생성</p>
</blockquote>
<ul>
<li>CSS 번들 크기를 줄여준다: 실제로 사용되는 스타일만 포함되므로 CSS 번들 크기가 줄어듭니다.</li>
</ul>
<p>물론 tailwind를 쓰는 데 단점들도 존재했으나 위와 같은 장점과 상대적으로 쓰기 쉽고, 반응형을 구성하는 데에 편하기에 사용하였습니다.</p>
<h3 id="nextui">NextUI</h3>
<p>NextUI는 TailwindCSS를 스타일 엔진으로 사용하는데, NextUI 컴포넌트 내에서 모든 TailwindCSS 클래스를 사용할 수 있어서 tailwind TailwindCSS와 연계가 좋았습니다.
NextUI는 tailwind-variants라는 TailwindCSS 유틸리티 라이브러리를 만들어 TailwindCSS 클래스 충돌을 자동으로 처리하고 관리하고있고, 그것이 TailwindCSS와 같이 사용 하기 좋다고 판단하였습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next/image를 쓰는 이유(All That Arsenal)]]></title>
            <link>https://velog.io/@theo_jin/Nextimage%EB%A5%BC-%EC%93%B0%EB%8A%94-%EC%9D%B4%EC%9C%A0All-That-Arsenal</link>
            <guid>https://velog.io/@theo_jin/Nextimage%EB%A5%BC-%EC%93%B0%EB%8A%94-%EC%9D%B4%EC%9C%A0All-That-Arsenal</guid>
            <pubDate>Thu, 26 Sep 2024 05:02:59 GMT</pubDate>
            <description><![CDATA[<h1 id="nextimage를-쓰는-이유-nextimage로-lcplargest-contentful-paint를-33-개선">Next/image를 쓰는 이유: Next/image로 LCP(Largest Contentful Paint)를 33% 개선</h1>
<p>프로젝트 초기에는 Next.js의 Next/Image 대신 img 태그를 사용했습니다. 당시에는 Next/Image의 이미지 최적화 기능까지 Next서버에서 처리하는 것이 서버리소스의 낭비라 생각했기 때문입니다. 그러나 프로젝트를 빌드하는 과정에서 img 태그를 사용할 경우 웹 페이지의 LCP 성능 저하와 대역폭 증가에 대한 경고가 나타났습니다. 이에 따라, 본 프로젝트에 img 태그가 적합한지, 아니면 Next/Image를 사용하는 것이 더 나을지에 대해 고민하게 되었습니다.</p>
<h2 id="nextimage가-가지는-장단점-파악">Next/Image가 가지는 장단점 파악.</h2>
<p>공식문서에 따르면 Next/Image는 아래와 같은 장점을 가집니다.</p>
<h3 id="nextimage의-장점">Next/Image의 장점.</h3>
<ul>
<li><p>크기 최적화: 각 장치에 적합한 크기의 이미지를 자동으로 제공하고, WebP 및 AVIF와 같은 최신 이미지 형식을 사용합니다.</p>
</li>
<li><p>시각적 안정성: 이미지가 로드될 때 레이아웃 이동을 자동으로 방지합니다.</p>
</li>
<li><p>빠른 페이지 로드: 네이티브 브라우저 지연 로딩을 사용하여 이미지가 뷰포트에 들어올 때만 로드되며, 선택적 블러 업 플레이스홀더를 제공합니다.</p>
</li>
<li><p>자산 유연성: 원격 서버에 저장된 이미지를 포함하여 온디맨드로 이미지 크기를 조정합니다.</p>
</li>
</ul>
<h3 id="nextimage의-단점">Next/Image의 단점.</h3>
<ul>
<li>서버 부하: next/image의 자동 이미지 최적화 기능은 수많은 이미지를 처리 시 서버 리소스 사용량을 증가시킬 수 있습니다.</li>
<li>기능 중복: 이미 외부 CDN을 통해 이미지 최적화와 캐싱하고 있을 시, Next/Image가 제공하는 기능(자동 최적화)과 중복될 수도 있습니다.</li>
</ul>
<h3 id="본-프로젝트에서-nextimage의-의미">본 프로젝트에서 Next/Image의 의미.</h3>
<p>Next/Image의 장단점들을 본 프로젝트에 고려하여 판단하였습니다.</p>
<p>본 프로젝트에서는 Firebase Storage(단순 Firebase Storage는 명시적으로 CDN기능을 제공하지 않습니다.) 에서 이미지를 가져오고 있으며, 이미지 원본은 WebP 형식입니다. Next/Image에서 제공하는 이미지 최적화 기능 중 파일 형식을 변경하는 옵션을 사용하지 않았기 때문에, 서버에 큰 부담이 없을 것으로 판단했습니다. 외부 CDN 도입 시 추가 비용이 발생할 수 있어 이를 도입하지 않았고, Next/Image의 기능 중복 문제는 고려하지 않아도 되었습니다.</p>
<p>또한  Next/Image장점 중 “크기 최적화”와 “빠른 페이지 로드” 기능이 LCP기능 개선에 도움이 된다는 것을 알게 되었습니다. 크기 최적화기능은 최적화된 이미지 크기와 더 효율적인 이미지 형식은 이미지 로딩 속도를 개선하여 LCP 시간을 단축시킵니다. 또한 빠른 페이지 로드기능도  뷰포트에 필요한 이미지만 로드함으로써 초기 페이지 로딩 시간을 줄이고, 블러 업 플레이스홀더는 사용자 경험을 개선하면서 LCP 메트릭을 최적화하는 데 도움을 줍니다. </p>
<p>그 결과 Next/Image를 사용하기로 하였고 사용한 결과, LCP(최대 콘텐츠 표시 시간)가 30% 개선되었습니다.</p>
<h3 id="성능비교">성능비교</h3>
<p>빌드 상황에서 Lighthouse 측정결과 LCP를 30% 개선했습니다.</p>
<p><strong>img태그:</strong>
<img src="https://velog.velcdn.com/images/theo_jin/post/1bae131a-1b7a-4868-8599-076d177e60ea/image.png" alt="">
<strong>Next/Image:</strong>
<img src="https://velog.velcdn.com/images/theo_jin/post/5923b7c9-ffeb-4850-aba9-9b6881cc023f/image.png" alt="">
또한 데이터나 개발 조건 변경에 따라 다시 img태그로 변경 상황에서도 리팩토링이 비교적 용이했습니다.</p>
<p>본 프로젝트에서는 Next/Image의 장점이 단점보다 훨씬 크다고 판단하여, 최종적으로 Next/Image를 사용하기로 결정했습니다. 이를 통해 Lighthouse 점수를 개선하고 사용자 경험을 향상시킬 수 있었습니다.</p>
<h2 id="reference">Reference</h2>
<p><a href="https://firebase.google.com/docs/storage?hl=ko">https://firebase.google.com/docs/storage?hl=ko</a>
<a href="https://nextjs.org/docs/app/building-your-application/optimizing/images">https://nextjs.org/docs/app/building-your-application/optimizing/images</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터 페치 이원화의 이유: Authorization을 포함한 데이터요청이냐 아니냐]]></title>
            <link>https://velog.io/@theo_jin/%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8E%98%EC%B9%AD</link>
            <guid>https://velog.io/@theo_jin/%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%8E%98%EC%B9%AD</guid>
            <pubDate>Thu, 26 Sep 2024 04:58:11 GMT</pubDate>
            <description><![CDATA[<h2 id="데이터-페치-이원화의-이유-authorization을-포함한-데이터요청이냐-아니냐">데이터 페치 이원화의 이유: Authorization을 포함한 데이터요청이냐 아니냐</h2>
<p>All That Arsenal 프로젝트에서는 비즈니스 로직에 따라 데이터 페치 방식을 두 가지로 분리하여 효율성을 높였습니다. Next.js 의 데이터 페치 방식 중 하나인 서버 컴포넌트에서의 페치와 클라이언트 컴포넌트에서의 TanStack Query 를 조합하여 사용했습니다</p>
<h3 id="nextjs-fetch를-단독으로-사용시-나타나는-문제점">Next.js Fetch를 단독으로 사용시 나타나는 문제점</h3>
<p>Next.js의 기본 데이터 페치 방식은 서버 캐시를 사용하여 모든 데이터 요청의 응답을 캐시합니다. 이 방식은 개인화된 요청에서도 모든 사용자에게 동일한 응답이 반환되는 문제가 발생했습니다.
또한 개인적인 요청과 그에 대한 응답까지 서버컴포넌트에서 캐싱한다는 것은 서버자원을 낭비하는 것이라고 생각했습니다. 
그래서 개인화된 요청과 공통된 요청을 구분하여 데이터 페치 방식을 이원화했습니다
<img src="https://velog.velcdn.com/images/theo_jin/post/c5bcfe24-8444-4e9e-a840-61ae95e1fb18/image.png" alt=""></p>
<h3 id="1-1-authorization을-포함한-데이터-요청-tanstack-query">1-1) Authorization을 포함한 데이터 요청: TanStack Query</h3>
<p>Authorization을 포함한 데이터 요청을 처리하기 위해 TanStack Query를 사용했습니다. Next.js fetch에서 no-store 옵션을 설정하여 캐싱을 방지할 수도 있었지만, 이 경우 새로 고침이나 캐시 만료 시마다 API 호출이 발생하여 비용이 증가하는 문제가 있었습니다. 이를 해결하기 위해, 개인화된 요청은 클라이언트 컴포넌트에서 TanStack Query로 브라우저 메모리에 캐시하고, queryKey와 staleTime으로 캐시를 관리했습니다. 검색 엔진 노출이 필요 없는 개인화된 요청은 클라이언트 컴포넌트에서 처리하여 서버 부하를 줄였습니다.</p>
<p>본 프로젝트에서는 선수별 댓글 및 즐겨찾기와 같은 Authorization이 필요한 요청을 클라이언트 컴포넌트에서 TanStack Query를 사용하여 관리했습니다.</p>
<h3 id="1-2-공통된-요청-nextjs-fetch">1-2) 공통된 요청: Next.js Fetch</h3>
<p>개인화되지 않은 데이터는 Next.js 서버 컴포넌트에서 Next.js fetch를 사용하여 관리했습니다. 서버 컴포넌트(RSC, React Server Component)는 데이터 페치 로직을 서버로 옮겨 데이터베이스와 더 가깝게 처리할 수 있는 장점이 있습니다. Next.js 서버 컴포넌트에서 데이터를 페치하면, 서버 요청과 배포 전반에 걸쳐 캐싱된 데이터를 유지할 수 있습니다. 이를 통해, 개인화되지 않은 정적 데이터를 효율적으로 캐싱하여 사용할 수 있습니다. 본 프로젝트에서는 선수 정보, 경기 일정 등의 개인화되지 않은 정적 데이터를 효율적으로 캐싱하여 사용하기 위해 Next.js의 서버 컴포넌트에서 data fetch를 하여 관리하였습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[번역]TailwindCSS vs NextUI – How to Choose a UI Framework]]></title>
            <link>https://velog.io/@theo_jin/%EB%B2%88%EC%97%ADTailwindCSS-vs-NextUI-How-to-Choose-a-UI-Framework</link>
            <guid>https://velog.io/@theo_jin/%EB%B2%88%EC%97%ADTailwindCSS-vs-NextUI-How-to-Choose-a-UI-Framework</guid>
            <pubDate>Wed, 05 Jul 2023 10:55:08 GMT</pubDate>
            <description><![CDATA[<p>최근에 NEXT로 개인프로젝트를 하려고 하는데 첫 관문부터가 쉽지 않았다. </p>
<p>&#39;스타일링으로 무엇을 쓸것인가!&#39; </p>
<p>리액트를 사용했다면 쓰던걸 썼겠지만 새로 배우는 기술엔<br>뭐가 가장 적절한 기술인지 고려해야한다고 생각하기에 이것 저것 찾아보다가 흥미로운 블로그글이 있어 알아보았다. </p>
<h3 id="번역">번역)</h3>
<h2 id="제목-tailwindcss-vs-nextui---ui-프레임-워크를-선택하는-방법">제목-Tailwindcss vs Nextui - UI 프레임 워크를 선택하는 방법</h2>
<p>당신이 개발자라면 적절한 UI 프레임 워크를 선택하는 것이 어려울 수 있다.<br/> </p>
<p>이것은 부분적으로 선택할 수있는 옵션이 너무 많아서 각각의 강점과 약점이 있기 때문이다.<br/>
이 가이드에서 필자는 두가지 있기 있는 프레임워크인 Tailwindcss 와 Nextui에 대해 다룰것이다. </p>
<p> Tailwindcss는 단순성, 유연성 그리고 사용하기 편리하다는 점때문에 인기가 많은 프레임워크이고,<br/>
 반면에 Nextui는 확장성, 성능 및 유연성때문에 인기가 있다. </p>
<p> 이 글은 두 프레임 워크의 중요한 기능, 장단점과 다양한 프로젝트 유형에 대한 적합성을 비교한다.</p>
<p>당신이 당신프로젝트에 가장 적합한 프레임워크를 찾는 사람이든, 그냥 이러한 툴들에 대해서 궁금한사람이든 이 글은 당신을 위한 글이다. </p>
<h4 id="여기서-다룰-내용은-이러합니다">여기서 다룰 내용은 이러합니다.</h4>
<ul>
<li>Tailwindcss 뭐고 무슨 장단점이 있?</li>
<li>Nextui 뭐고 무슨 장단점이 있죠?</li>
<li>그래서 둘 중 어떤걸 고를까</li>
</ul>
<p>그러면 Let’s dive in! 🚀</p>
<h3 id="tailwind-css가-뭔가요">Tailwind CSS가 뭔가요?</h3>
<p>Tailwind CSS는 유틸리티 우선 CSS프레임워크(utility-first CSS framework)입니다.즉, 외관이나 UI보다는 기능에 중점을 둔 프레임 워크입니다.<br/>
캐나다의 개발자이자 기업가인 Adam Wathan에 의해 개발됐습니다. </p>
<p>Tailwind를 사용하면 HTML을 떠나지 않고도 당신의 유틸리티 클래스들을 완전히 커스터마이징 할 수 있습니다. Tailwind CSS는 웹 사이트를 스타일링하는 빠르고 쉬운 방법을 제공합니다.</p>
<p>Tailwind CSS는 많은 부분에 있어 커스터마이징 할 수 있습니다. 이는 사용자로서,당신의 웹 디자인 선호도에 맞게 글꼴, 색상 및 기타 디자인 요소를 수정할 수 있음을 의미합니다.</p>
<p>이 프레임 워크는 또한 PurgeCSS (최종 빌드에서 사용하지 않거나 원치 않는 CSS 스타일을 제거하여 파일 크기를 줄여줌)로 인해 로 시간이 더 빠릅니다.</p>
<h3 id="tailwind-css의-장점은-이러합니다">Tailwind CSS의 장점은 이러합니다:</h3>
<ul>
<li>Tailwind CSS 초보 개발자들에게 배우기 쉽고 사용하기 쉽다.</li>
<li>Tailwind CSS는 단 몇 줄의 코드로 유연한 디자인을 만든다.</li>
<li>Tailwind는 유틸리티 우선 프레임 워크(utility-first CSS framework)이므로 디자인을 재사용 가능한 구성 요소로 쉽게 변환 할 수 있습니다.</li>
<li>Tailwind는 가이드이며 CSS 클래스 및 ID들에 익숙하지 않은 이름을 사용하지 않습니다.</li>
<li>Tailwind는 개발자의 요구에 맞게 쉽게 커스터마이징 할 수 있습니다</li>
</ul>
<h3 id="tailwind-css의-단점은-이러합니다">Tailwind CSS의 단점은 이러합니다:</h3>
<ul>
<li>Tailwind는 낮은 수준의 CSS 클래스들을 제공하기 때문에 큰 CSS 파일을 포함하는데,그래서 스타일링 할때 파일이 너무 크고 복잡하여 시간이 지남에 따라 유지 관리할 수 없다. 이는 웹 사이트 또는 앱의 성능에도 영향을 미친다.</li>
<li>Tailwind에는 많은 낮은 수준의 클래스들이 있을 수 있지만, 디자인을 변경하거나 특정 요소를 스타일링 할 때, 특정요소의 스타일을 무시하기는 어렵습니다.</li>
<li>Tailwind는 일부 프로젝트에서 항상 가장 적합한 프레임워크는 아닙니다.의심할 여지 없이 강력한 도구이지만, 프로젝트 작업 시 사용 가능한 클래스를 사용하여 특정 설계 요구 사항을 달성할 수 없습니다.Tailwind 는 Prototyping, Dashboards, E-commerce등과 같이 많은 스타일링 UI와 디자인 요소를 사용자 커스터마이징하는 프로젝트에 가장 적합합니다.</li>
</ul>
<h3 id="nextui-뭔가요">NextUI 뭔가요?</h3>
<p>Nextui는 component 기반 CSS 프레임 워크입니다. 즉, 언제든지 사용할 수있는 미리 디자인된 component들을 제공합니다. 예로는 버튼, 양식, 내비게이션 바, 내비게이션 메뉴 등이 있습니다.</p>
<p>Nextui는 Vercel 팀에 의해 구축되고 유지됩니다. Nextui의 아름다움은 커스터마이징 가능한 템플릿을 제공하므로 반응 형 웹 사이트를 만들기 위해 많은 CSS 코드를 작성할 필요가 없다는 것입니다.</p>
<p>이 프레임 워크를 사용하는 단점은  component들이 Nextui에 의해 제한되어 있기 때문에 개발자는 디자인을 100% 제어하지 않는다는 것입다.</p>
<p>NextUI는 핵심 UI component들 외에 추가로 자동 코드 분할, SSR 및 GraphQL 및 Apollo와 같은 라이브러리에 대한 내장 지원과 같은 개발자 도구를 제공합니다.</p>
<h3 id="nextui의-장점은-이러합니다">NextUI의 장점은 이러합니다:</h3>
<ul>
<li>Nextui는 응용 프로그램이 커지면 크고 복잡한 앱을 처리 할 때 대단히 확장 가능합니다. 큰 파일을 처리 할 때 속도가 느려지지 않습니다.</li>
<li>Nextui는 매우 유연하며 사용자의 요구나 취향에 맞게 built됩니다.</li>
<li>Nextui는 크고 신뢰할 수있는 커뮤니티가 있으므로 도구를 사용할 때 도움을받는 데 스트레스가 없습니다.</li>
<li>Nextui는 광범위한 pre-built된 component 및 특징들을 보유하고있어 구축 할 때 개발자의 시간을 절약 할 수 있습니다.</li>
<li>Nextui는 React를 기반으로하여 JavaScript 응용 프로그램을 구축하는 것이 사용자 친화적입니다.</li>
</ul>
<h3 id="nextui의-단점은-이러합니다">NextUI의 단점은 이러합니다:</h3>
<ul>
<li>NextUI는 ReactJS를 기반으로 구축되었음에도 불구하고 다른 프레임워크에 비해 설치 공간이 큽니다. 개발자가 Next UI 탐색 방법을 완전히 이해하지 못하는 경우에도 성능에 영향을 미칠 수 있습니다.</li>
<li>Nextui는 ReactJS를 포함하거나 사용하지 않는 응용 프로그램을 작성할 때 최선의 선택이 아닐 수 있습니다.</li>
<li>Nextui는 비교적 사용하기 쉽지만 RECT를 사용하지 않는 사람들에게는 이해하기 어려운 학습 곡선이 있습니다.</li>
<li>Nextui는 커스텀을 위한 다양한 개발자 도구를 제공하기 때문에 세팅 및 구성에 더 많은 주의가 필요합니다.</li>
</ul>
<h3 id="how-to-decide-between-nextui-and-tailwind">How to Decide Between NextUI and Tailwind</h3>
<p>대규모 프로젝트를 위해 React 응용 프로그램을 작업하는 경우 CSS Framework로서 Nextui가 최선의 선택입니다.<br/>
그러나 특정 기능 또는 설계 요구 사항에 집중 해야하는 응용 프로그램을 작업하는 경우 Tailwind가 최선의 선택입니다.</p>
<h3 id="결론">결론</h3>
<p>결론적으로 Tailwind CSS 및 Next UI는 웹 및 모바일 애플리케이션을 개발하기위한 다른 도구입니다.</p>
<p>Tailwind CSS는 개발자가 커스텀 UI를 빠르게 구축 할 수 있도록 설계된 utility-first CSS 프레임워크 입니다.<br/>
NextUI는 개발자가 어플리케이션의 퍼포먼스와 확장성을 향상 시키도록 디자인된   component-based 프레임워크 입니다.</p>
<p>Tailwind와 Nextui는 각각 강점과 약점을 가지고 있으며 프로젝트에 가장 적합한 선택은 전적으로 애플리케이션의 설계 요구 사항, 요구 및 목표에 달려 있습니다.
Tailwind CSS는 커스텀 UI를 구축하기 위한 간단한 솔루션을 찾는 개발자에게 가장 적합합니다. 반대로 NextUI는 CSS 템플릿을 찾는 개발자에게 가장 적합하므로 확장성과 같은 애플리케이션의 다른 부분에 집중할 수 있습니다.</p>
<p>마지막으로 개발자로서의 선호도와 기술을 바탕으로 자신에게 가장 적합한 프레임워크를 선택합니다. 선택하기 전에 각 도구의 장단점을 신중하게 평가하여 사용자에게 가장 적합한 애플리케이션을 구축할 때 충분한 정보를 바탕으로 한 결정을 내립시다.</p>
<p>원문:TailwindCSS vs NextUI – How to Choose a UI Framework
주소:<a href="https://www.freecodecamp.org/news/tailwindcss-vs-nextui-how-to-choose-a-ui-framework/">https://www.freecodecamp.org/news/tailwindcss-vs-nextui-how-to-choose-a-ui-framework/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS VS FIREBASE]]></title>
            <link>https://velog.io/@theo_jin/AWS-VS-FIREBASE</link>
            <guid>https://velog.io/@theo_jin/AWS-VS-FIREBASE</guid>
            <pubDate>Thu, 13 Apr 2023 15:40:31 GMT</pubDate>
            <description><![CDATA[<h1 id="aws-vs-firebase">AWS VS FIREBASE</h1>
<p><img src="https://velog.velcdn.com/images/theo_jin/post/2709753d-7a0b-4336-b4f4-75d05246e45c/image.png" alt=""></p>
<h2 id="aws">AWS</h2>
<p>아마존 웹 서비스(영어: Amazon Web Services, 약칭: AWS)는 아마존닷컴의 클라우드 컴퓨팅 사업부이다.</p>
<p>아마존 웹 서비스는 다른 웹 사이트나 클라이언트측 응용 프로그램에 대해 온라인 서비스를 제공하고 있다. 이러한 서비스의 상당수는 최종 사용자에 직접 공개되는 것이 아니고, 다른 개발자가 사용 가능한 기능을 제공하는 플랫폼을 제공하는 PaaS이다.
또한 다음과 같은 특징을 가지고 있다. </p>
<ul>
<li>웹인프라와 서비스 합친 서비스. </li>
<li>새로운 기술에 적응성이 높다. </li>
<li>백엔드 서버와 관련된 거의 모든 서비스를 제공해준다. </li>
<li>AWS에서는 일반적으로 사용되는 거의 모든 인터넷 기술을 제공한다 만약 없다면 직접 올려서 쓸수있게 해준다. </li>
<li>클라우드나 확장성이 좋다.</li>
</ul>
<h4 id="paas란">Paas란?</h4>
<p>서비스형 플랫폼(Platform as a Service, PaaS)은 클라우드 컴퓨팅 서비스 분류 중 하나다. 일반적으로 앱을 개발하거나 구현할 때, 관련 인프라를 만들고 유지보수하는 복잡함 없이 애플리케이션을 개발, 실행, 관리할 수 있게 하는 플랫폼을 제공한다.
SaaS의 개념을 개발 플랫폼에도 확장한 방식으로, 개발을 위한 플랫폼을 구축할 필요 없이, 필요한 개발 요소를 웹에서 쉽게 빌려쓸 수 있게 하는 모델이다.</p>
<h2 id="firebase">Firebase</h2>
<p>백엔드 플랫폼 서비스만을 지원한다.비교적 할수있는 일이 제한적이며, 로그인 인증이나 애널리틱스(사용자 정보나 사용통계), 크래시리틱스(사용자가 앱을 사용하거나 오류가 생겼을 때 자동으로 보고해서 문제해결에 도움을 준다.), 클라우드 메시징(안드로이드나 ios,모바일 기기에 보낼 수 있 푸시메세지전송등을 도와줌) 등에 강점이있다.</p>
<p>파이어베이스는 업계표준이 아니고 자체 규격의 기술을 쓴다. 파이어베이스에서 백엔드 로직을 실행하는데 쓰이는 클라우드 펑션은 노드JS기반이다.그래서 JS와 TS만 지원함.데이터베이스인 파이어스토어는 몽고DB와 비슷한 document 방식의 NoSQLDB사용함 RDB에 익숙한사람들이 쓰기 어려움</p>
<h3 id="firebase의-장점">Firebase의 장점</h3>
<p>신속한 구현
관리되는 서버
머신러닝
포괄적인 보안과 인증
Real-time Database
사용자 추적이 쉬움
빠르고 안전한 호스팅
개발자 친화적이다</p>
<h3 id="firebase의-단점">Firebase의 단점</h3>
<p>Vendor Lock
실시간 데이터 스토리지에 대한 제약
iOS 지원 감소
일부 국가에서는 구입할 수 없음
불편한 데이터 저장
휴대성 감소.
파이어베이스 비용</p>
<p>출처:AWS와 파이어베이스, 대표적인 클라우드 백엔드 소개!by 작은 개발자 <a href="https://medium.com/mqos-technologies/firebase-vs-aws-which-one-to-choose-in-2022-dce230ab44dd">https://medium.com/mqos-technologies/firebase-vs-aws-which-one-to-choose-in-2022-dce230ab44dd</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로토타입]]></title>
            <link>https://velog.io/@theo_jin/%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@theo_jin/%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85</guid>
            <pubDate>Sun, 04 Dec 2022 06:08:33 GMT</pubDate>
            <description><![CDATA[<h2 id="프로토타입">프로토타입</h2>
<h3 id="추상화">추상화</h3>
<p>다양한 속성 중에서 프로그램에 필요한 속성만 간추려 내어 표현하는 것</p>
<h3 id="객체">객체</h3>
<p>속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료구조,상태데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조</p>
<h3 id="객체-생성방식">객체 생성방식</h3>
<ul>
<li>객체리터럴</li>
<li>Object생성자함수</li>
<li>생성자 함수</li>
<li>Object.create 메서드</li>
<li>클래스(ES6)</li>
</ul>
<h3 id="상속">상속</h3>
<p>어떤객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수있는것. 자바스크립트에서는 상속을 구현하여 불필요한 중복 제거하며 코드를 적극적으로 <strong>재사용</strong>함. </p>
<h3 id="프로토-타입">프로토 타입</h3>
<ul>
<li>프로토 타입 객체는 상속을 구현하기위해 사용</li>
<li>프로토타입은 어떤 객체의 상위 객체의 역할을 하는 객체, 다른객체에 공유 프로퍼티를 제공.</li>
<li>프로토 타입 상속받은 하위 객체는 상위 객체의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 사용.</li>
<li>모든 객체는 하나의 프로퍼티 타입을 가지며, 또한 모든 프로토타입은 생성자 함수와 연결되어있음.</li>
<li>모든 객체는 <strong>proto</strong>접근자 프로퍼티를 통해 자신의 프로토타입,즉[[Prototype]]내부슬롯에 간접적으로 접근.</li>
</ul>
<h3 id="프로토타입-체인">프로토타입 체인</h3>
<p>자바스크립트는 객체의 프로퍼티에 접근하려고 할때 해당객체에 접근하려는 프로퍼티가 없다면[[Prototype]]내부슬롯의 참조를 따라 자신의 부모역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색 이를 프로토타입 체인이라 한다. 프로토타입 체인은 자바스크립트가 객체지향 프로그래밍의 상속을 구현하는 메커니즘. 즉,<strong>프로토타입 체인은 상속과 프로퍼티 검색을 위한 메커니즘.</strong>  </p>
<ul>
<li><p>프로토타입 체인은 단방향 링크드 리스트로 구현되어야,프로토타입 체인의 종점은 Object.prototype이며, 이객체의 프로퍼티와 메서드는 모든 객체에 상속됨.</p>
</li>
<li><p>프로토타입 체인vs 스코프 체인</p>
<p>  프로토타입 체인:상속과 프로퍼티 검색을 위한 메커니즘.  </p>
<p>  스코프 체인: 식별자 검색을 위한 메커니즘.</p>
</li>
</ul>
<h3 id="proto접근자-프로퍼티-vs-prototype-프로퍼티"><strong>proto</strong>접근자 프로퍼티 vs prototype 프로퍼티</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>소유</th>
<th>값</th>
<th>사용주체</th>
<th>사용목적</th>
</tr>
</thead>
<tbody><tr>
<td><strong>proto</strong>접근자 프로퍼티</td>
<td>모든객체</td>
<td>프로토타입 참조</td>
<td>모든객체</td>
<td>객체자신의 프로토타입에 접근 또는 교체하기 위해 사용.</td>
</tr>
<tr>
<td>prototype 프로퍼티</td>
<td>constructor</td>
<td>프로토타입 참조</td>
<td>생성자 함수</td>
<td>생성자 함수가 자신이 생성할 객체(인스턴스)의 프로토타입을 할당하기 위해 사용.</td>
</tr>
</tbody></table>
<h3 id="오버라이딩">오버라이딩</h3>
<p>상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의 해서 사용하는 방식</p>
<h3 id="오버로딩">오버로딩</h3>
<p>함수의 이름은 동일하지만 매개변수의 타입 또는 개수가 다른 메서드를 구현하고 매개변수에 의해 메서드를 구별하여 호출하는 방식. 자바스크립트는 오버로딩을 지원하지 않지만 arguments객체를 사용하여 구현할 수는 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 deepdive(비교연산자 주의사항)]]></title>
            <link>https://velog.io/@theo_jin/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-deepdive%EB%B9%84%EA%B5%90%EC%97%B0%EC%82%B0%EC%9E%90-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD</link>
            <guid>https://velog.io/@theo_jin/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-deepdive%EB%B9%84%EA%B5%90%EC%97%B0%EC%82%B0%EC%9E%90-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD</guid>
            <pubDate>Sat, 26 Nov 2022 17:51:08 GMT</pubDate>
            <description><![CDATA[<h3 id="비교연산자-주의사항">비교연산자 주의사항</h3>
<pre><code class="language-jsx">//NaN은 자신과 일치하지 않는 유일한 값이다. 
NaN === NaN;// false

//따라서 숫자가 NaN인지 조사하려면 빌트인 함수 Number.isNaN을 사용한다.
// Number.isNaN 함수는 지정한 값이 NaN인지 확인하고 그 결과를 불리언 값으로 반환한다.
Number.isNaN(NaN); //true
Number.isNaN(10); //true
Number.isNaN(1+undefined); //true</code></pre>
<pre><code class="language-jsx">//자바스크립트에서 양의 0과 음의 0이 있는데 이들을 비교하면 true를 반환함.
0 === -0// true
0 == -0// true

//Object.is메서드예측가능한 정확한 비교결과를 반환한다. 
//그 외에는 일치비교 연산자와 동일하게 동작.
-0 === +0// true
Object.is(-0,0) //false

NaN === NaN;   //false
Object.is(NaN,NaN) //true</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[220817 자바스크립트딥다이브 질문.]]></title>
            <link>https://velog.io/@theo_jin/220817-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-%EC%A7%88%EB%AC%B8</link>
            <guid>https://velog.io/@theo_jin/220817-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-%EC%A7%88%EB%AC%B8</guid>
            <pubDate>Wed, 17 Aug 2022 09:47:06 GMT</pubDate>
            <description><![CDATA[<p>부동소수점 이란?</p>
<blockquote>
<p>컴퓨터에서 실수를 표시하는 방법으로, 소수점의 위치를 고정시키지 않으며 가수와 지수를 사용하여 실수를 표현한다. 가수는 유효숫자를 나타내며 지수는 소수점의 위치를 나타낸다.</p>
</blockquote>
<p>var 키워드의 단점은?</p>
<blockquote>
<p>1.변수 중복 선언 허용
2. 함수 레벨 스코프
3.변수 호이스팅 </p>
</blockquote>
<p>변수선언시 생기는 두단계에 대해 말해주세요.</p>
<blockquote>
<p>선언 단계:변수 이름을 등록해서 자바스크립트 엔진에 변수의 존재 알림.
 초기화 단계:값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined를 할당해 초기화한다.</p>
</blockquote>
<pre><code>console.log(score);

var score=80;

console.log(score);</code></pre><p>변수 선언 없이 식별자 접근한다면 어떤일이 발생하나요?</p>
<blockquote>
<p>ReferenrceError발생한다. 이것은 식별자를 통해 값을 참조하려 했지만 자바스크립트 엔진이 등록된 식별자를 찾을 수 없을때 발생하는 에러.</p>
</blockquote>
<p>데이터 타입의 필요성 3가지</p>
<blockquote>
<p>값을 저장할 때 확보해야하는 메모리 공간의 크기를 결정하기 위해
값을 참조할때 한번에 읽어 들여야할 메모리 공간의 크기를 결정하기 위해
메모리에서 읽어들인 2진수를 어떻게 해설할지 결정하기 위해</p>
</blockquote>
<p>매니지드 언어와 언매니지드 언어의 차이점을 설명하시오.</p>
<blockquote>
<p>언매니지드 언어-개발자가 명시적으로 메모리 할당하고 해제하기 위해 malloc()과 free()같은 저수준 메모리 제어기능 제공. 메모리 제어를 개발자가 주도. 최적의 성능 확보 가능하지만 오류도 생산할 가능성 존재.</p>
</blockquote>
<blockquote>
<p>매니지드 언어-메모리의 할당 및 해제를 위한 메모리 관리기능을 언어차원에서 담당하고 개발자의 직접적인 메모리 제어 허용하지 않음.  개발자가 메모리를 명시적으로 할당하고 해제 못함.  더이상 사용하지 않는 메모리 해제는 가비지 콜렉터가 수행. 개발자가 관여 못함. </p>
</blockquote>
<p>세미콜론 자동삽입기능에 대해 간단히 설명해주세요. </p>
<blockquote>
<p>자바스크립트 엔진이 소스코드를 해석할때 문의 끝이라고 예측되는 지점에 세미콜론을 자동으로 붙여주는 기능</p>
</blockquote>
<p>정적 타입과 동적 타입의 차이점. </p>
<blockquote>
<p>정적타입-변수타입을 변경할 수없음. 변수에 선언한 타입에 맞는 값만 할당가능.컴파일시점에 타입체크를 수행함. 타입체크 통과하지 못하면 에러발생.&gt;&gt;&gt; 이때문에 일관성 강제 안정적인 구현을 통해 런타임에 발생하는 에러 줄여줌. </p>
</blockquote>
<blockquote>
<p>동적타입-변수에 어떤 데이터 타입의 값이라도 자유롭게 할당. 하지만 변수 값은 언제든지변경될 수 있기 때문에 복잡한 프로그램에서 변화하는 변수 값 추적하기 어려울 수도...
또한 변수의 타입이 고정되어 있지 않고 동적으로 변하는 동적 타입 언어의 변수는 값의변경에 의해 타입도 언제든지 변경가능 그래서 동적타입의 변수는 값을 확인하기전에 타입을 확신하지 못함. </p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>