<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>taehong0-0.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 08 Dec 2022 08:49:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>taehong0-0.log</title>
            <url>https://images.velog.io/images/taehong0-0/profile/8c7f1a9a-0668-4646-af2d-398211a93d54/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. taehong0-0.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/taehong0-0" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[React Query] 리액트 쿼리 시작하기]]></title>
            <link>https://velog.io/@taehong0-0/React-Query-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BF%BC%EB%A6%AC-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@taehong0-0/React-Query-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BF%BC%EB%A6%AC-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 08 Dec 2022 08:49:07 GMT</pubDate>
            <description><![CDATA[<h2 id="overview">Overview</h2>
<h3 id="react-query란-">React-Query란 ?</h3>
<p>React Query는 데이터 Fetching, 캐싱, 동기화, 서버 쪽 데이터 업데이트 등을 쉽게 만들어 주는 React 라이브러리입니다.</p>
<h3 id="사용-이유-">사용 이유 ?</h3>
<p>그렇다면 사용하는 이유는 무엇일까요 ?
먼저 프론트엔드 개발을 하는 사람들이 가장 많이 하는 고민중에 하나는 바로 <strong>&#39;상태관리&#39;</strong> 입니다. 프론트엔드 개발자라면 <strong>상태관리</strong>와  뗄 수 없는 인연을 가지고 있습니다. 그리고 많은 사람들이 <strong>상태관리</strong>를 위해 <strong>Redux</strong>를 사용합니다. 
그리고 Redux를 이용하여 서버 데이터를 활용하기 위해서는 Redux-saga와 같은 다른 미들웨어를 사용해야 합니다. 하지만 프로젝트를 계속 진행하면서 API가 계속 추가되고 API마다 액션과 액션 타입, Saga 파일 등으로 인해 프로젝트의 구성이 복잡해지는 문제성을 갖고 있습니다. </p>
<p>이 뿐만 아니라 서버로 부터 값을 가져오거나 업데이트 하는 로직을 store 내부에 개발하는 경우가 많습니다. 그렇다보니 store는 클라이언트 state를 유지해야하는데 어느 순간부터 store에 클라이언트 데이터와 서버 데이터가 공존 하게 됩니다. 
서버 데이터를 위한 로직이 과도하게 커지고, 그로 인해서 Redux 를 활용하기 위한 보일러 플레이트가 비대해 진다는 점이 문제점이 됩니다. </p>
<p>그래서 React Query를 활용해 클라이언트와 서버의 데이터를 분리하여 사용합니다.</p>
<h2 id="react-query">React Query</h2>
<h3 id="장점">장점</h3>
<ul>
<li><p>데이터를 캐싱한다. (캐싱 된 데이터로 인해서 API 콜을 줄여주며 서버에 대한 부담을 줄여준다)</p>
</li>
<li><p>client와 server의 데이터를 분리해준다.</p>
</li>
<li><p>get을 한 데이터에 대해 update를 하면 자동으로 get을 다시 수행한다. (예를 들면 게시판의 글을 가져왔을 때 게시판의 글을 생성하면 게시판 글을 get하는 api를 자동으로 실행 )</p>
</li>
<li><p>데이터가 오래 되었다고 판단되면 다시 get 요청을 보낸다. (invalidateQueries)</p>
</li>
<li><p>동일 데이터 여러번 요청하면 한번만 요청한다. (옵션에 따라 중복 호출 허용 시간 조절 가능)</p>
</li>
<li><p>무한 스크롤 (Infinite Queries (opens new window))</p>
</li>
<li><p>비동기 과정을 선언적으로 관리할 수 있다.</p>
</li>
<li><p>react hook과 사용하는 구조가 비슷하다.</p>
<h3 id="사용법">사용법</h3>
<h4 id="react-query-설치">react-query 설치</h4>
<pre><code class="language-console">$ yarn add react-query
$ npm i react-query</code></pre>
<h4 id="queryclientprovider">QueryClientProvider</h4>
<p>먼저 react의 가장 기본이 되는 곳에 react-query를 사용하도록 세팅합니다.</p>
<pre><code class="language-js">/* index.js */
import { QueryClient, QueryClientProvider } from &#39;react-query&#39;

const queryClient = new QueryClient()

export default function App() {
 return (
   &lt;QueryClientProvider client={queryClient}&gt;
     &lt;Example /&gt;
   &lt;/QueryClientProvider&gt;
 )
}</code></pre>
<h4 id="react-query-맛보기">react-query 맛보기</h4>
<pre><code class="language-js">import { useQuery, useMutation, useQueryClient, QueryClient, QueryClientProvider } from &quot;react-query&quot;;
import { getTodos, postTodo } from &quot;../my-api&quot;;
</code></pre>
</li>
</ul>
<p>// Create a client
const queryClient = new QueryClient();</p>
<p>function App() {
    return (
        // Provide the client to your App
        <QueryClientProvider client={queryClient}>
            <Todos />
        </QueryClientProvider>
    );
}</p>
<p>function Todos() {
    // Access the client
    const queryClient = useQueryClient();</p>
<pre><code>// Queries
const query = useQuery(&quot;todos&quot;, getTodos);

// Mutations
const mutation = useMutation(postTodo, {
    onSuccess: () =&gt; {
        // Invalidate and refetch
        queryClient.invalidateQueries(&quot;todos&quot;);
    },
});

return (
    &lt;div&gt;
        &lt;ul&gt;
            {query.data.map((todo) =&gt; (
                &lt;li key={todo.id}&gt;{todo.title}&lt;/li&gt;
            ))}
        &lt;/ul&gt;

        &lt;button
            onClick={() =&gt; {
                mutation.mutate({
                    id: Date.now(),
                    title: &quot;Do Laundry&quot;,
                });
            }}
        &gt;
            Add Todo
        &lt;/button&gt;
    &lt;/div&gt;
);</code></pre><p>}</p>
<p> render(<App />, document.getElementById(&#39;root&#39;))</p>
<pre><code>위의 예제는 공식문서에 나와있는 예제입니다. 이 예제는 react-query의 3가지 중요한 컨셉을 보여줍니다. 

- Queries
- Mutations
- Query Invalidation

#### Quesries
```js
import { useQuery } from &#39;react-query&#39;

 function App() {
   const info = useQuery(&#39;todos&#39;, fetchTodoList, options)
 }</code></pre><p>먼저 Queries의 사용법은 위와 같습니다.
useQuery는 서버에서 데이터를 가져오기(get) 위해 사용하는 hook 입니다. unique key, promise 기반의 함수, 옵션 값을 파라미터로 받아서 동작합니다. unique key는 애플리케이션 전역에서 해당 쿼리를 refetching, caching, sharing 하는 용도로 사용되며, 쿼리의 리턴 값으로는 status, data, error와 같은 템플릿을 포함하여 데이터 사용에 필요한 정보가 제공됩니다.</p>
<ol>
<li>첫번째 인자로, unique key를 명시해줍니다.
✔️ 해당 key는 내부적으로 데이터 재요청, 캐싱, 쿼리를 공유하기 위해 사용된다.</li>
<li>두번째 인자에는 우리가 요청할 비동기 함수를 넣어주는데, 데이터와 error를 return해줍니다.</li>
<li>세번째 인자에는 성공과 실패와 같은 경우에 옵션을 넣어줄 수 있습니다.</li>
</ol>
<h5 id="status">status</h5>
<ul>
<li>isLoading
쿼리에 데이터가 없고 fetching 하는 상태.</li>
<li>isError
쿼리에 에러가 발생한 상태.</li>
<li>isSuccess
쿼리가 성공적으로 실행되었고 데이터를 사용가능한 상태.</li>
<li>isIdle
쿼리를 사용할 수 없는 상태. (disabled)</li>
<li>error
쿼리가 isError 상태인 경우 에러 정보 확인을 위해 사용하는 프로퍼티.</li>
<li>data
쿼리가 isSucess 상태인 경우 데이터 사용을 위해 사용하는 프로퍼티.</li>
<li>isFetching
쿼리의 fetching/refetching 여부에 대한 boolean 값.</li>
</ul>
<pre><code class="language-js">const {
  isSuccess,
  isError,
  isLoading,
  isFetching,
  data,
  error
} = useQuery(
  &#39;todos&#39;,
  fetchTodoList,
  {
    onSuccess: (data) =&gt; {
      console.log(&#39;onSuccess&#39;, data);
    },
    onError: (error) =&gt; {
      console.log(&#39;onError&#39;, error);
    }
  }
);

if (isFetching) {
  console.log(&#39;fetching...&#39;);
}

if (isLoading) {
  console.log(&#39;loading...&#39;);
}

if (isError) {
  console.log(&#39;error&#39;, error);
}

if (isSuccess) {
  console.log(&#39;success&#39;, data);
}</code></pre>
<p>위와 같이 상태값에 따라 다른 로직을 실행하도록 useQuery를 활용할 수 있으며 isLoading, isError, isSuccess는 status로 통일해서 사용할 수 있습니다.</p>
<pre><code class="language-js">if (status === &#39;loading&#39;) {
  console.log(&#39;loading...&#39;);
}

if (status === &#39;error&#39;) {
  console.log(&#39;error&#39;, error);
}

if (status === &#39;success&#39;) {
  console.log(&#39;success&#39;, data);
}
</code></pre>
<h5 id="query-keys">Query Keys</h5>
<ul>
<li><p>useQuery에서 파라미터로 사용되는 Query Key는 React Query에서 쿼리 캐싱을 관리하기 위한 unique key로 사용됩니다.</p>
</li>
<li><p>문자열, 배열 , 중첩된 객체등 어떤 형태로든 가능하다.</p>
</li>
<li><p>Query key가 순차적 진행을 보장하는 직렬화 기법으로 쿼리의 데이터는 유일하다.
✔️ Query key가 문자열일 때</p>
<pre><code class="language-js">// A list of todos
useQuery(&#39;todos&#39;, ...) // queryKey === [&#39;todos&#39;]

// Something else, whatever!
useQuery(&#39;somethingSpecial&#39;, ...) // queryKey === [&#39;somethingSpecial&#39;]</code></pre>
<p>✔️ Query key가 배열일 때</p>
<pre><code class="language-js">// An individual todo
useQuery([&#39;todo&#39;, 5], ...)
// queryKey === [&#39;todo&#39;, 5]

// An individual todo in a &quot;preview&quot; format
useQuery([&#39;todo&#39;, 5, { preview: true }], ...)
// queryKey === [&#39;todo&#39;, 5, { preview: true }]

// A list of todos that are &quot;done&quot;
useQuery([&#39;todos&#39;, { type: &#39;done&#39; }], ...)
// queryKey === [&#39;todos&#39;, { type: &#39;done&#39; }]</code></pre>
<p>✔️ Query key에 있는 object들의 순서는 중요하지 않다.</p>
</li>
</ul>
<p>동일한 Query key -&gt; array의 object내 에 있는 값들의 순서는 중요하지 않음 (동일함)</p>
<pre><code class="language-js">useQuery([&#39;todos&#39;, { status, page }], ...)
useQuery([&#39;todos&#39;, { page, status }], ...)
useQuery([&#39;todos&#39;, { page, status, other: undefined }], ...)
//동일 하지 않은 Query key. -&gt; array 값의 순서는 중요하다
useQuery([&#39;todos&#39;, status, page], ...)
useQuery([&#39;todos&#39;, page, status], ...)
useQuery([&#39;todos&#39;, undefined, page, status], ...)</code></pre>
<h5 id="query-functions">Query Functions</h5>
<ul>
<li>Query Function은 promise를 return하는 함수이다.</li>
<li>Promise는 data를 return하거나 에러가 나면 에러를 return한다.<pre><code class="language-js">useQuery([&#39;todos&#39;], fetchAllTodos)
useQuery([&#39;todos&#39;, todoId], () =&gt; fetchTodoById(todoId))
useQuery([&#39;todos&#39;, todoId], async () =&gt; {
 const data = await fetchTodoById(todoId)
 return data
})
useQuery([&#39;todos&#39;, todoId], ({ queryKey }) =&gt; fetchTodoById(queryKey[1]))</code></pre>
아래의 코드는 에러를 핸들링해서 throw해주는 예제이다.<pre><code class="language-js">const { error } = useQuery([&#39;todos&#39;, todoId], async () =&gt; {
 if (somethingGoesWrong) {
   throw new Error(&#39;Oh no!&#39;)
 }
 return data
})</code></pre>
<h5 id="usequery-비동기적으로-사용하기">useQuery 비동기적으로 사용하기</h5>
useQuery는 기본적으로 비동기로 동작합니다. useQuery에 다음과 같이 enabled 옵션을 false로 사용하면 동기적으로 사용할 수 있습니다.</li>
</ul>
<p>enabled 옵션을 false로 사용하게 되면 컴포넌트가 mount 되거나 window focus 되어도 쿼리가 자동으로 실행되지 않습니다. 또한 queryClient에서 invalidateQueries 또는 refetchQueries 함수를 호출해도 refetching 되지 않습니다. 쿼리는 캐싱되지 않은 idle 상태이며 fetching을 위해서는 refetch 함수를 트리거로 사용해야 합니다.</p>
<pre><code class="language-js">const {
  isSuccess,
  isError,
  isLoading,
  isFetching,
  data,
  error
} = useQuery(
  &#39;todos&#39;,
  fetchTodoList,
  {
    enabled: false,
    onSuccess: (data) =&gt; {
      console.log(&#39;onSuccess&#39;, data);
    },
    onError: (error) =&gt; {
      console.log(&#39;onError&#39;, error);
    }
  }
);</code></pre>
<h5 id="usequeries를-이용하여-usequery-여러개-사용하기">useQueries를 이용하여 useQuery 여러개 사용하기</h5>
<p>useQuery는 기본적으로 비동기로 동작하기 때문에 컴포넌트 내에 useQuery가 여러 개 있다면 순서대로 실행되지 않고 동시에 실행됩니다.</p>
<p>이러한 경우에 다음과 같이 useQueries를 이용하면 여러개의 쿼리를 하나로 묶어서 사용할 수 있습니다. 아래 예제를 실행하면 UseQueryResult가 배열로 반환됩니다.</p>
<pre><code class="language-js">const users = [1,2,3,4,5]
const userQueries = useQueries(
     users.map(user =&gt; {
       return {
         queryKey: [&#39;user&#39;, user],
         queryFn: () =&gt; fetchUserById(user),
       }
     })
   )</code></pre>
<h4 id="usequery의-옵션">useQuery의 옵션</h4>
<p>useQuery에는 다양한 옵션을 사용할 수 있는데 이 중에서 몇가지 유용한 옵션에 대해 정리하면 다음과 같습니다.</p>
<ul>
<li>enabled
false로 설정하면 쿼리가 자동으로 실행되지 않음.</li>
<li>retry
쿼리가 실패한 경우에 대한 재시도 횟수</li>
<li>staleTime
데이터가 stale state로 변경되는 시간 (Infinity로 설정하면 stale state로 변경되지 않음)</li>
<li>cacheTime
inactive state의 캐시 데이터가 메모리에 남아있는 시간</li>
<li>refetchInterval
설정한 시간(밀리초)에 따라 주기적으로 fetching 실행.</li>
<li>refetchOnWindowFocus
창에 포커스가 된 경우에 대한 refetch 여부.</li>
<li>initialData
쿼리의 초기값 설정</li>
</ul>
<h4 id="usemutation">useMutation</h4>
<p>useMutation은 서버를 대상으로 데이터를 수정 (create, update, delete) 하기 위해 사용하는 hook 입니다.
리턴값과 사용하는 방법은 useQuery와 비슷합니다. </p>
<ul>
<li>isIdle
mutation이 실행 되지 않아 아직 캐싱되지 않은 상태.</li>
<li>isLoading
mutation이 실행중인 상태.</li>
<li>isError
mutation에 에러가 발생한 상태.</li>
<li>isSuccess
mutation이 성공적으로 실행되었고 데이터를 사용 가능한 상태.</li>
<li>error
mutation이 isError 상태인 경우 에러 정보 확인을 위해 사용하는 프로퍼티.</li>
<li>data
mutation이 isSucess 상태인 경우 데이터 사용을 위해 사용하는 프로퍼티.</li>
</ul>
<p>다음 예제를 통해 useMutation의 사용 방법에 대해 알아보겠습니다. useMutation에 사용한 파라미터는 순서대로 다음과 같은데 파라미터의 구성도 useQuery와 동일합니다.</p>
<ul>
<li>mutationKey
mutation에 사용할 unique key 값.</li>
<li>mutationFn
mutation에 사용할 promise 기반의 비동기 API 함수.</li>
<li>options
mutation에 사용할 옵션 값.</li>
</ul>
<pre><code class="language-js">const mutation = useMutation(
  &#39;addUser&#39;,
  addUserFuc,
  {
    onMutate: (variables) =&gt; {
      console.log(&#39;onMutate&#39;, variables);
    },
    onError: (error, variables, context) =&gt; {
      console.log(&#39;onError&#39;, context);
    },
    onSuccess: (data, variables, context) =&gt; {
      console.log(&#39;onSuccess&#39;, data);
    },
    onSettled: (data, error, variables, context) =&gt; {
      console.log(&#39;onSettled&#39;, data);
    }
  }
);</code></pre>
<p>위에 사용된 옵션은 다음과 같습니다. </p>
<ul>
<li>onMutate
mutation이 실행되기 전에 실행되는 함수.
mutationFn에 전달되는 파라미터를 동일하게 받음.
mutation 실패시 onError, onSettled 함수에 return 값을 전달함.
rollback 처리가 필요한 경우에 사용되며 return 값은 context 파라미터로 사용 가능.</li>
<li>onError
mutation 실행과정에서 에러가 발생했을때</li>
<li>onSuccess
mutation이 성공 했을 때</li>
<li>onSettled
mutation 성공 또는 실패시 데이터나 에러를 전달. <h5 id="usemutation에서-쿼리-invalidation-처리하기">useMutation에서 쿼리 Invalidation 처리하기</h5>
일반적으로 mutation이 성공적으로 동작한 이후에는 다른 관련된 쿼리의 refetch를 필요로 할 가능성이 높습니다. 이러한 경우엔 다음과 같이 QueryClient의 invalidQueries 함수를 사용해줍니다. 이렇게 하면 mutation 성공 이후에 해당 쿼리가 stale 상태로 변경 되어 캐시에서 삭제되고 refetch가 실행되게 됩니다.<pre><code class="language-js">const mutation = useMutation(postTodo, {
onSuccess: () =&gt; {
  // postTodo가 성공하면 todos로 맵핑된 useQuery api 함수를 실행합니다.
  queryClient.invalidateQueries(&quot;todos&quot;);
}
});</code></pre>
만약 mutation에서 return된 값을 이용해서 get 함수의 파라미터를 변경해야할 경우 setQueryData를 사용합니다.<pre><code class="language-js">const queryClient = useQueryClient();
</code></pre>
</li>
</ul>
<p>const mutation = useMutation(editTodo, {
  onSuccess: data =&gt; {
    // data가 fetchTodoById로 들어간다
    queryClient.setQueryData([&quot;todo&quot;, { id: 5 }], data);
  }
});</p>
<p>const { status, data, error } = useQuery([&quot;todo&quot;, { id: 5 }], fetchTodoById);</p>
<p>mutation.mutate({
  id: 5,
  name: &quot;nkh&quot;
});</p>
<pre><code>

#### react Suspense와 react-query 사용하기
react-query를 사용하는 또 하나의 이유는 비동기를 좀 더 선언적 사용할 수 있어서 인 것 같습니다.
Suspense (opens new window)를 사용하며 loading을, Error buundary (opens new window)를 사용하여 에러 핸들링을 더욱 직관적으로 할 수 있습니다.
suspense를 사용하기 위해 QueryClient에 옵션을 하나 추가합니다. 아래 방법은 global하게 suspense를 사용한다고 정의할 때 예시입니다.
```js
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 0,
      suspense: true
    }
  }
});

ReactDOM.render(
  &lt;React.StrictMode&gt;
    &lt;QueryClientProvider client={queryClient}&gt;
      &lt;App /&gt;
    &lt;/QueryClientProvider&gt;
  &lt;/React.StrictMode&gt;,
  document.getElementById(&quot;root&quot;)
);</code></pre><p>아래는 suspense를 사용하는 예제입니다.</p>
<pre><code class="language-js">const { data } = useQurey(&quot;test&quot;, testApi, { suspense: true });</code></pre>
<p>위처럼 세팅을 완료 했을 경우 react에서 제공하는 Suspense를 사용하면 됩니다.</p>
<pre><code class="language-js">const { data } = useQurey(&quot;test&quot;, testApi, { suspense: true });

return (
  // isLoading이 true이면 Suspense의 fallback 내부 컴포넌트가 보여집니다.
  // isError가 true이면 ErrorBoundary의 fallback 내부 컴포넌트가 보여집니다.
  &lt;Suspense fallback={&lt;div&gt;loading&lt;/div&gt;}&gt;
    &lt;ErrorBoundary fallback={&lt;div&gt;에러 발생&lt;/div&gt;}&gt;
      &lt;div&gt;{data}&lt;/div&gt;
    &lt;/ErrorBoundary&gt;
  &lt;/Supense&gt;
);</code></pre>
<p>위와 같이 react query를 왜 사용하는지 장점은 무엇이 있는지, 사용법은 어떻게 되는지와 관련한 글이었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자료구조 정리 ]]></title>
            <link>https://velog.io/@taehong0-0/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@taehong0-0/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Fri, 25 Nov 2022 12:18:05 GMT</pubDate>
            <description><![CDATA[<h2 id="자료구조란">자료구조란</h2>
<blockquote>
<p>자료구조는 데이터를 원하는 규칙 또는 목적에 맞게 저장하기 위한 구조이고, 알고리즘이란 자료구조에 쌓인 데이터를 활용해 어떠한 문제를 해결하기 위한 여러 동작들의 모임이다.</p>
</blockquote>
<h3 id="arraylist--linkedlist">ArrayList / LinkedList</h3>
<h4 id="배열-arraylist">배열 (ArrayList)</h4>
<ul>
<li>여러 데이터를 하나의 이름으로 그룹핑해서 관리 하기 위한 자료구조로 index와 값의 쌍으로 구성되어있다.</li>
<li>연속된 메모리의 공간으로 이루어져 있다</li>
<li>배열은 정의와 동시에 길이를 지정하며 길이를 바꿀 수 없다.</li>
<li>크기가 고정되어 있기 때문에 어떤 엘리먼트가 삭제되면, 삭제된 상태를 빈 공간으로 남겨두어야 한다. =&gt; 메모리 낭비</li>
<li>ArrayList는 Random Access를 지원한다. 인덱스로 바로 접근할 수 있기 때문에 특정 요소를 접근하기 위해서는 O(1)의 시간이 필요하다.</li>
<li>삽입과 삭제에 대해서는 요소를 모두 옮겨주어야 하기 때문에 O(N)의 시간이 필요하다.</li>
<li>스택을 구현하기 좋다. LinkedList로 구현하게 되면 객체를 제거하는 작업이 필요하지만 ArrayList로 구현하게 되면 index를 줄이고 초기화만 하면 된다.</li>
</ul>
<h4 id="연결리스트linkedlist">연결리스트(LinkedList)</h4>
<ul>
<li>연결리스트란 먼저 각 노드가 데이터와 포인터를 가지고 한 줄로 연결되어 있는 방식으로 데이터를 저장하는 자료구조이다.</li>
<li>각 노드는 해당하는 데이트를 갖고 있고 포인터를 통해 다음 노드의 주소값을 저장하고 있다. </li>
<li>링크드 리스트는 Sequential Access를 지원한다. 각 노드가 다음 포인터의 주소를 갖고 있기 때문에 특정 요소에 접근하기 위해서는 O(N)의 시간이 필요하다.</li>
<li>링크드 리스트는 노드가 저장하고 있는 주소값을 바꿔주면 되기 때문에 삽입과 삭제에는 O(1)의 시간이 소요된다.<blockquote>
<p>배열에서 메모리는 선언 시 컴파일 타임에 할당이 된다.(정적 메모리 할당) 반면 Linkedlist에서는 새로운 요소가 추가될 때 런타임에 메모리를 할당한다.(동적 메모리 할당)</p>
</blockquote>
</li>
<li>한 개의 Node는 다른 Node에 대한 참조만 가지고 있다. 따라서 공간적 제약을 ArrayList에 비해 받지 않는다</li>
<li>배열은 Stack 섹션에 메모리 할당이 이루어 진다. 반면 Linkedlist는 Heap 섹션에 메모리 할당이 이루어진다.</li>
<li>큐를 구현하기 좋다. 삽입과 삭제가 용이하기 때문에 삭제하게 되면 모든 요소를 앞으로 이동시켜야 하는 ArrayList보다 LinkedList가 좋다.<h3 id="스택--큐">스택 &amp; 큐</h3>
<h4 id="스택">스택</h4>
</li>
<li>LIFO(Last In First Out)으로 가장 나중에 들어온 것이 가장 먼저 나간다. 세로로 된 통이라고 생각하면 된다.</li>
<li>스택은 정해진 방향으로만 쌓을수 있고, top으로 정한 곳을 통해서만 접근할 수 있다.</li>
<li>스택에서 top을 통해 삽입하는 연산을 &#39;push&#39; , top을 통한 삭제하는 연산을 &#39;pop&#39;이라고 한다.</li>
<li>비어있는 스택에서 원소를 추출하려고 할 때 stack underflow라고 하며, 스택이 넘치는 경우 stack overflow라고 한다.<h4 id="큐">큐</h4>
</li>
<li>FIFO(First In First Out)으로 가장 먼저 들어온 것이 먼저 나간다. 가로로 된 통과 같은 구조라고 생각하면 된다.</li>
<li>삭제연산만 수행되는 곳을 프론트(front), 삽입연산만 이루어지는 곳을 리어(rear)로 정하여 각각의 연산작업만 수행된다.</li>
<li>큐의 리어에서 이루어지는 삽입연산을 인큐(enQueue) 프론트에서 이루어지는 삭제연산을 디큐(dnQueue)라고 부른다.</li>
</ul>
<h4 id="스택-두개로-큐를-구현하는-방법은-">스택 두개로 큐를 구현하는 방법은 ??</h4>
<p>스택 하나는 입력용 하나는 출력용으로 두면 된다.
하나의 스택에 계속 입력을 받다가 pop()을 하게 되면 출력용 스택이 비어있을 경우에만 입력용 스택의 모든 원소를 두번째 출력용 스택으로 거꾸로 넣고 하나씩 빼주면 된다. 만약 출력용 스택이 비어있지 않다면 그대로 빼주면 된다.</p>
<h3 id="그래프-트리-트라이">그래프, 트리, 트라이</h3>
<h4 id="그래프">그래프</h4>
<ul>
<li>그래프는 노드(하나의 점)와 노드 간을 연결하는 간선으로 구성된 자료 구조이다. 이를 통해 연결된 노드 간의 관계를 표현할 수 있는 자료구조이다.</li>
<li>그래프는 순환 혹은 비순환 구조를 이룬다</li>
<li>그래프는 방향이 있는 그래프와 방향이 없는 그래프가 있다.</li>
<li>루트 노드의 개념이 없다 / 부모-자식 관계라는 개념이 없다.</li>
<li>2개 이상의 경로가 가능하다.(무방향, 방향, 양방향 가능)</li>
<li>그래프는 네트워크 모델이다.<h4 id="트리">트리</h4>
</li>
<li>트리는 그래프와 같이 노드와 노드간을 연결하는 간선으로 구성된 자료구조이다.</li>
<li>트리는 두 개의 노드 사이에 반드시 1개의 경로만을 가지며 사이클이 존재하지 않는 방향 그래프이다.</li>
<li>부모-자식 관계가 성립하기 때문에 계층형 모델이라고도 한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/taehong0-0/post/f9a6afd5-86d9-44fb-8ce7-9bc01e59ae32/image.png" alt=""></p>
<h4 id="트라이">트라이</h4>
<ul>
<li><p>트라이(Trie)는 문자열을 저장하고 효율적으로 탐색하기 위한 트리 형태의 자료구조이다.</p>
</li>
<li><p>우리가 검색할 때 볼 수 있는 자동완성 기능, 사전 검색 등 문자열을 탐색하는데 특화되어있는 자료구조라고 한다.</p>
</li>
<li><p>래딕스 트리(radix tree) or 접두사 트리(prefix tree) or 탐색 트리(retrieval tree)라고도 한다. 트라이는 retrieval tree에서 나온 단어이다.</p>
</li>
<li><p>예를 들어 &#39;Datastructure&#39;라는 단어를 검색하기 위해서는 제일 먼저 &#39;D&#39;를 찾고, 다음에 &#39;a&#39;, &#39;t&#39;, ... 의 순서로 찾으면 된다. 이러한 개념을 적용한 것이 트라이(Trie)이다.</p>
</li>
<li><p>트라이(Trie)는 문자열 검색을 빠르게 한다.</p>
</li>
<li><p>문자열을 탐색할 때, 하나하나씩 전부 비교하면서 탐색을 하는 것보다 시간 복잡도 측면에서 훨씬 더 효율적이다.</p>
</li>
<li><p>각 노드에서 자식들에 대한 포인터들을 배열로 모두 저장하고 있다는 점에서 저장 공간의 크기가 크다는 단점도 있다. (메모리 측면에서 비효율적일 수 있음!)</p>
</li>
</ul>
<p>자세한 참고 : <a href="https://velog.io/@kimdukbae/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%ED%8A%B8%EB%9D%BC%EC%9D%B4-Trie">트라이 관련 벨로그</a></p>
<h3 id="힙heaps">힙(Heaps)</h3>
<ul>
<li>완전 이진 트리를 기초로 하고 우선순위 큐를 위하여 만들어진 자료구조.<ul>
<li>완전 이진트리는 마지막을 제외한 모든 노드에서 자식들이 꽉 채워진 이진트리를 말한다.</li>
</ul>
</li>
<li>여러 개의 값들 중에서 최댓값이나 최솟값을 빠르게 찾아내도록 만들어진 자료구조</li>
<li>힙에서의 부모 노드와 자식 노드의 관계<ul>
<li>왼쪽 자식의 인덱스 = (부모의 인덱스) * 2</li>
<li>오른쪽 자식의 인덱스 = (부모의 인덱스) * 2 + 1</li>
<li>부모의 인덱스 = (자식의 인덱스) / 2</li>
</ul>
</li>
</ul>
<h3 id="해시테이블">해시테이블</h3>
<ul>
<li>해시 테이블은 (Key, Value)로 데이터를 저장하는 자료구조 중 하나로 빠르게 데이터를 검색할 수 있는 자료구조</li>
<li>해시 테이블이 빠른 검색속도를 제공하는 이유는 내부적으로 배열(버킷)을 사용하여 데이터를 저장하기 때문이다. </li>
<li>해시 테이블은 각각의 Key값에 해시함수를 적용해 배열의 고유한 index를 생성하고, 이 index를 활용해 값을 저장하거나 검색하게 된다. 여기서 실제 값이 저장되는 장소를 버킷 또는 슬롯이라고 한다.</li>
</ul>
<p>ex) (&#39;taehong&#39;,&#39;software&#39;)라는 (Key,Value)가 들어오면 taehong을 해시함수에 돌린 결과값이 index에 들어가게 된다. index=hashFuc(&#39;taehong&#39;)이 되고 이 index를 통해 buckets[index]=&#39;software&#39; 로 정보를 저장하게 된다. 따라서 해시함수를 통해 나온 결과값을 index로 접근하기 때문에 시간복잡도는 O(1)이 된다.</p>
<h4 id="해시-값이-충돌할-경우">해시 값이 충돌할 경우</h4>
<p>결국 데이터가 많아지면, 다른 데이터가 같은 해시 값으로 충돌나는 현상이 발생함 &#39;collision&#39; 현상
서로 다른 인풋에 대해 해시 함수를 돌려서 나온 결과값이 동일하다면 해당하는 인덱스에 값이 충돌하게 된다. 이러한 경우에는 분리 연결법(Separate Chaining)과 개방 주소법(Open Addressing) 두가지로 크게 해결하고 있다.</p>
<h5 id="분리-연결법separate-chaining">분리 연결법(Separate Chaining)</h5>
<ul>
<li>Separate Chaining이란 동일한 버킷의 데이터에 대해 링크드 리스트를 활용하여 메모리를 사용하여 다음 데이터의 주소를 저장하는 것이다. </li>
<li>이 방법을 사용하게 되면 테이블을 확장할 필요 없이 간단하게 구현 가능하며 위에서 말했듯이 링크드 리스트를 사용하였기 때문에 삽입과 삭제가 편하다는 장접이 있다.</li>
<li>하지만 충돌되는 데이터가 많아지면 chaning되는 데이터가 많아져 캐시의 효율이 감소한다. 연결 리스트로 인해 최악의 경우 수행 시간이 O(n)이 된다. 해시를 사용하는 이유는 O(1)이라는 장점으로 사용하는 것인데 O(n)이 된다면 문제가 된다. <h5 id="개방-주소법open-addressing">개방 주소법(Open Addressing)</h5>
추가적인 메모리를 사용하는 Chaining 방식과 다르게 비어있는 해시 테이블의 공간을 활용하는 방법이다. 대표적인 방법은 3가지가 있다.</li>
<li><em>1. 선형 프로빙(Linear probing)*</em>
현재의 버킷 index로부터 고정폭 만큼씩 이동하여 차례대로 검색해 비어 있는 버킷에 데이터를 저장한다.
primary clustering는 한 번 충돌이 나면 집중적으로 충돌이 발생하는 것을 의미한다. 이로 인하여 평균 검색 시간이 증가한다.</li>
<li><em>2. 이차식 프로빙(Quadratic probing)*</em>
시의 저장순서 폭을 제곱으로 저장하는 방식이다. 예를 들어 처음 충돌이 발생한 경우에는 1만큼 이동하고 그 다음 계속 충돌이 발생하면 2^2, 3^2 칸씩 옮기는 방식이다.
secondary clustering은 처음 충돌한 위치가 같다면 다음 충돌할 위치도 반복적으로 계속 충돌이 나게 된다는 의미이다.</li>
<li><em>3. 이중 해시(Double hasing)*</em>
해시된 값을 한번 더 해싱하여 해시의 규칙성을 없애버리는 방식이다. 해시된 값을 한번 더 해싱하여 새로운 주소를 할당하기 때문에 다른 방법들보다 많은 연산을 하게 된다.<h3 id="참고할만한-자료">참고할만한 자료</h3>
</li>
<li><a href="https://velog.io/@humblechoi/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EB%A9%B4%EC%A0%91%EC%A7%88%EB%AC%B8-%EB%AA%A8%EC%9D%8C">[자료구조] 면접질문 모음</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[구름톤 회고 2]]></title>
            <link>https://velog.io/@taehong0-0/%EA%B5%AC%EB%A6%84%ED%86%A4-%ED%9A%8C%EA%B3%A0-2</link>
            <guid>https://velog.io/@taehong0-0/%EA%B5%AC%EB%A6%84%ED%86%A4-%ED%9A%8C%EA%B3%A0-2</guid>
            <pubDate>Wed, 19 Oct 2022 16:53:13 GMT</pubDate>
            <description><![CDATA[<h2 id="개발-과정">개발 과정</h2>
<h3 id="프로젝트-세팅-및-룰-정하기">프로젝트 세팅 및 룰 정하기</h3>
<p>프로젝트 세팅 부분에 있어서 같은 FE 개발자 분 께서 next 를 사용해 보는 것이 어떻냐고 여쭤보아 이번 기회에 한번 사용해보고 생각해서 OK 했다 ! 프로젝트 기본 세팅과 같은 경우에는 뛰어나신 다른 <strong>프론트엔드 개발자님</strong> 께서 먼저 준비를 해와 주셔서 그걸 토대로 Organization 을 만들고 프로젝트 기초 세팅을 했다. 생각보다 뚝딱뚝딱 진행이 된 것 같아 기분이 좋았다 &gt;&lt;</p>
<p>그리고 이전에 했던 프로젝트에서 사용했던 커밋 룰이나 브랜치 룰, 폴더 구조와 같은 것들을 서로 이야기 하고 맞추는 과정을 진행했다. 이를 노션에 정리하고 서로 양보하며 룰을 정했다. 다행히 비슷한 룰을 사용하고 있어 조금씩 지우고 추가하며 룰을 잘 정했던 것 같다 !</p>
<h2 id="개발-시작-">개발 시작 !</h2>
<h3 id="개발-1일차">개발 1일차</h3>
<p>먼저 <strong>기획자 분</strong>이 와이어 프레임을 만들어 와주셨고 <strong>디자이너 분</strong>이 이를 토대로 귀염뽀짝 이쁘장한 디자인을 만들어 주셔서 즐겁게 개발을 준비했다 !</p>
<p>먼저 FE 개발자가 두명이었기 때문에 디자인을 보고 서로 원하거나 이전에 개발 경험이 있는 페이지를 맡아 개발을 시작했다 ! 물론 시간이 많았다면 해본적 없는 페이지를 해보고 싶었지만,, 해커톤 특성상 빨리 빨리 만들어야 했기 때문에 경험 있는 것을 택했다 !</p>
<p>그렇게 1일차에는 프로젝트 세팅과 룰을 정하는데 많은 시간을 투자했고 간단하게 사진 업로드 및 대표이미지 등록하는 부분을 개발했다 !</p>
<p>이미지 업로드에서는 <code>react-dropzone</code> 라이브러리를 사용하였다. 이 전에 프로젝트를 하며 사용해본적이 있는 라이브러리이기 때문에 금방 적용했다 ! ㅎㅎ </p>
<p>아래와 같이 DropZone 컴포넌트를 따로 분리하고 다른 곳에서 불러와서 사용할 수 있도록 했다 ! </p>
<pre><code class="language-javascript">import { Dispatch, SetStateAction } from &#39;react&#39;;
import { useCallback } from &#39;react&#39;;
import { useDropzone } from &#39;react-dropzone&#39;;

interface IProps {
  setFiles: Dispatch&lt;SetStateAction&lt;Blob[]&gt;&gt;;
  setImages: Dispatch&lt;SetStateAction&lt;string[]&gt;&gt;;
  children: React.ReactNode;
}
const DropZone: React.FC&lt;IProps&gt; = ({ setFiles, setImages, children }) =&gt; {
  const onDrop = useCallback((acceptedFiles: Blob[]) =&gt; {
    acceptedFiles.forEach((file: Blob) =&gt; {
      const reader = new FileReader();
      setFiles((prev) =&gt; [...prev, file]);
      const bloburl = URL.createObjectURL(file);
      setImages((prev) =&gt; [...prev, bloburl]);
      reader.onabort = () =&gt; console.log(&#39;file reading was aborted&#39;);
      reader.onerror = () =&gt; console.log(&#39;file reading has failed&#39;);
      reader.onload = () =&gt; {
        // Do whatever you want with the file contents
        const binaryStr = reader.result;
      };
      reader.readAsArrayBuffer(file);
    });
  }, []);
  const { getRootProps, getInputProps } = useDropzone({ onDrop });
  return (
    &lt;div {...getRootProps()} id=&quot;dropzone&quot;&gt;
      &lt;input {...getInputProps()} /&gt;
      {children}
    &lt;/div&gt;
  );
};

export default DropZone;
</code></pre>
<p>이렇게 해서 아래와 같이 만들어 졌다 ! </p>
<p><img src="https://velog.velcdn.com/images/taehong0-0/post/0824ca24-236c-4e8d-8d7a-f1860cec42d6/image.gif" alt=""></p>
<p>1일차는 우선 간단하게 이미지 업로드와 대표이미지 등록, 그리고 등록 페이지의 기초 레이아웃을 잡고 끝이 났다 ! ㅎㅎ</p>
<h3 id="개발-2일차">개발 2일차</h3>
<p>2일차 부터는 정말 많은 일들이 있었던 것 같다,,,
일단,,, 컴포넌트 분리하고 생각하면서 코드를 짜기엔 너무 시간이 부족한 것 같았다,, </p>
<p>그리고 해커톤을 진행하며 설계 및 기획의 중요성을 다시한번 깨달았다.
해커톤 특성상 빠르게 개발해야 했고 이로 인해 기본적인 룰만 정한채로 깊게 설계를 하지 않고 개발을 시작했었다,,</p>
<p>그 결과 View는 다 만들었지만 api를 연결할 수가 없는 상황이 발생하였다. 
가장 문제가 생겼던 부분은 집 등록하는 부분이었다. </p>
<p>api 연결까지 생각해서 코드를 작성했어야 햇는데 상태관리를 컴포넌트 내부에서 하고 이것들의 depth가 깊어지기도 하고 달라지면서 원하는 데이터 형태를 만들기가 어려워졌다. 물론 리팩토링을 하고 시간이 된다면 할 수 있었을 것 같지만 그러기엔 다른 것들을 하느라 바빠 미뤄두고 결국 해결하지 못했다,,, 🥲 </p>
<p>위의 과정을 거치면서 다시 한번 설계의 중요성을 깨닫게 되었고 물론 생각하고 코드를 짜긴 했지만 조금 더 깊게 생각하고 코드를 짜는 습관을 들이도록 해야겠다고 다짐했다. 그리고 코드를 분리하는 습관도 잘 들여놔야 급한 순간에도 좋은 코드가 나올 것 같다고 생각했다,, 더 열심히 공부하고 코드 많이 짜봐야겠다,,</p>
<p>개발 경험과 프로젝트 경험에서 다양한 것을 접해봐야 한다고 생각했다 ! 이번에 개발하면서 이전에 프로젝트에서 사용했던 것들을 가져와 적용하면서 개발 시간을 단축할 수 있었다 ! 처음 사용했을 때는 시간도 많이 들고 오류도 많이 발생했지만 잘(?) 만들어 두니 이번엔 가져와서 사용하기 좋았다 ! 그래서 프로젝트를 하며 다양한 라이브러리도 사용해보고 한번 만들 때 재사용 할 수 있도록 잘 만들어야 겠다고 다짐했다 !</p>
<p><strong>잠와서 뭐라고 썼는지 모르겠지만 아무튼 재밌었다</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[구름톤 회고]]></title>
            <link>https://velog.io/@taehong0-0/%EA%B5%AC%EB%A6%84%ED%86%A4-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@taehong0-0/%EA%B5%AC%EB%A6%84%ED%86%A4-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 19 Oct 2022 14:29:26 GMT</pubDate>
            <description><![CDATA[<h1 id="🍊구름톤-참가🍊">🍊구름톤 참가🍊</h1>
<h3 id="kakao-x-goorm-에서-주최한-해커톤">kakao x goorm 에서 주최한 해커톤</h3>
<blockquote>
<p>구름톤은 마음껏 몰입하고 스스로 성장하는 IT 인재로 거듭나고 싶은 분들을 위해 카카오와 구름이 함께합니다. 구름톤(9oormthon)은 카카오 클라우드 플랫폼의 이름인 &#39;9rum&#39;과 구름의 영문명 &#39;goorm&#39;, &#39;Hackathon&#39;의 합성어로 두 구름이 만나 열리는 해커톤이라는 의미를 담고 있습니다. 구름톤을 통해 카카오 클라우드의 강력함과 구름의 효과적인 개발 프로세스를 경험해 보세요! </p>
</blockquote>
<h2 id="서론">서론</h2>
<p>이 전에 해커톤 경험이 없어 이 전부터 한번 해보고 싶다는 생각은 있었다.
하지만 내가 부족하다고 생각했었고 해커톤을 진행하면 혹시나 기획과 디자인이 다 되었지만 개발이 부족해서 피해를 줄까봐 솔직히 무서웠다,,🥺</p>
<p>그렇게 여러 프로젝트를 해보던 도중 주변에서 구름톤에 관해 이야기를 들었는데 제주도에서 한다는 말을 듣고 제주도에 가고싶던 나는 <strong>해커톤 + 제주도</strong> 조합을 참지못하고 신청을 해버렸다. 지원서를 열심히 작성해서 제출한 결과 다행히 합격 문자를 받았다 ! 😊 
그래서 기분좋게 비행기 표 도 예매하고  숙소도 예약했다 ㅎㅎ </p>
<p>(물론 비행기 일정 변경하다가 뱅기표 취소한 거 까먹고 공항가서 홍들짝 하고 다시 예매했다,,)</p>
<p>이번 기회에 해커톤에 도전해보고 부족한 점이 있다면 개선하여 다른 해커톤에 또 도전해보고 싶다고 생각했다.</p>
<h2 id="개발-전의-일정">개발 전의 일정</h2>
<h3 id="1일차-">*<em>1일차 *</em></h3>
<p>일정은 아이스 브레이킹 및 교육으로 잡혀있었다 ! 아이스 브레이킹에서는 <strong>영어 안쓰고 🗣 자기소개</strong> 도 하고 다양한 게임을 진행하며 분위기를 많이 풀어주셨고 그래서 점심을 먹으며 더 친해질 수 있었다 ! </p>
<p>점심을 4명에서 먹게 되었는데 처음엔 다들 배가 고파서 배를 채우고 있던 도중,, 어색한 것을 참기 힘들어서 바로 자기소개 하고 이야기를 시작했다 ! 그래도 다들 잘 받아 주셔서 같이 카페가서 인스타 맞팔도 하고 이야기도 많이 하며 저녁약속까지 잡아버렸다 !</p>
<p>이후 오후에는 쿠버네티스와 도커, 카카오 클라우드와 같은 내용의 수업을 듣고 해커톤 진행 방향 및 일정에 관한 설명을 들었다 ! 그리고 마지막에 키워드를 알려주셔서 해당하는 키워드에 대한 아이디어를 생각한 후 제출하고 다음 날 발표했어야 했다. </p>
<p>이렇게 1일차 일정이 모두 끝나고 서로 가까이 살거나 하는 사람들끼리 저녁을 먹으러 가기로 해서 이동해서 <strong>맛있는 고기</strong> 와 함께 <strong>한라산</strong> 을 살짝 먹어주며 사람들과 더욱 더 친해졌다 ㅎㅎ </p>
<h3 id="2일차"><strong>2일차</strong></h3>
<p>일정의 시작은 <strong>아이디어</strong> 를 발표하는 것이었다. 모바일 개발을 못했기 때문에 웹으로 구현 가능한 아이디어가 있는지, 그리고 아이디어가 재미있어 보이는지 두개를 중점으로 봤다. </p>
<p>그리고 첫 번째 발표자였던 <strong>우리팀의 기획자님</strong> 의 발표가 너무 재밌어 보였고 준비도 많이 해오신 것 같아 첫 발표부터 같이 해보고 싶다고 생각했다 ㅎㅎ 물론 나도 아이디어를 준비해가긴 했지만 기획자의 아이디어와 준비성을 절대 따라가지 못하겠다고 생각했고,, 그냥 개발자스러운 발표자료와 함께 아이디어 발표만 했다 !</p>
<p>그래서 발표가 끝나고 가장 맘에 들었던 <strong>우리팀 기획자님</strong> 께 바로 달려가 같이 하고싶다고 했고 그렇게 모인 사람들이 그대로 팀이되어 1등으로 팀이 되었다. (첫 만남부터 완벽해 보였던 우리 팀은 역시나 끝까지 완벽했다.)</p>
<p>까다롭기로 소문난 <strong>우리팀 백엔드 개발자님</strong> 의 평가에서 무려 4점을 받은 <strong>우리팀 기획자님</strong>이 생각보다 더 잘 준비해오신 것 같았고 아이디어도 너무 재밌어 보여서 얼른 개발하고 싶었다(?).ㅎㅎ </p>
<h2 id="프로젝트-소개">프로젝트 소개</h2>
<ul>
<li>프로젝트 명 : 니집내집</li>
<li>프로젝트 주제 : 홈익스체인지</li>
<li>프로젝트 기간 : 10/11 ~ 10/14 <strong>(개발 기간 : 10/12 ~ 10/14)</strong></li>
<li>팀원 : 기획자 1 / 디자이너 1 / Front-End 2 / Back-End 1</li>
<li>Git : <a href="https://github.com/NiZipNaeZip/frontend">https://github.com/NiZipNaeZip/frontend</a></li>
</ul>
<h2 id="프로젝트-내용-및-서비스-플로우">프로젝트 내용 및 서비스 플로우</h2>
<h4 id="프로젝트-내용">프로젝트 내용</h4>
<p>먼저 아이디어 발표에서 가장 재밌을 것 같다고 생각했던 점이 홈익스체인지라는 주제였다. 언제나 제주도 한달살이의 꿈을 갖고 있던 나에게 제주와 육지의 사람의 집을 바꿔살 수 있도록 해준다는 서비스의 주제는 너무 재밌어 보였다. 집을 등록하고 원하는 집과 기간을 고르고 채팅을 통해 확정하는 서비스를 보며 개발에 참여하고 싶다고 생각했다.</p>
<p><strong>자세한 서비스의 플로우는 아래와 같다.</strong></p>
<p><img src="https://user-images.githubusercontent.com/58380158/195978845-ddaf267c-c712-429b-b40f-eb69abddadf8.png" alt="image">
<img src="https://user-images.githubusercontent.com/58380158/195978861-8d854912-c87c-426b-9472-3b194ef1277e.png" alt="image">
<img src="https://user-images.githubusercontent.com/58380158/195978879-721e039d-ba61-42e6-be21-162bf6e98f67.png" alt="image">
<img src="https://user-images.githubusercontent.com/58380158/195978900-2d1d6b79-de1e-4a10-ad30-e203d816bc5c.png" alt="image">
<img src="https://user-images.githubusercontent.com/58380158/195978910-b12939a0-fef7-41ae-a079-79db033ac1d2.png" alt="image">
<img src="https://user-images.githubusercontent.com/58380158/195978933-2577bf95-6c0f-4f7c-a3b0-1d89985b73e4.png" alt="image">
<img src="https://user-images.githubusercontent.com/58380158/195978949-2169bd4f-33f5-4923-a73e-74dd42fb162d.png" alt="image"></p>
<p><strong>내용이 너무 길어질 것 같아 개발 내용은 다음 게시글에 추가하겠다 !</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSR / SSR with Next.js]]></title>
            <link>https://velog.io/@taehong0-0/CSR-SSR-with-Next.js</link>
            <guid>https://velog.io/@taehong0-0/CSR-SSR-with-Next.js</guid>
            <pubDate>Sun, 02 Oct 2022 13:55:37 GMT</pubDate>
            <description><![CDATA[<h3 id="1-csrclient-side-rendering이란-무엇이며-그것의-장단점에-대하여-설명해주세요">1. CSR(Client-side Rendering)이란 무엇이며, 그것의 장단점에 대하여 설명해주세요.</h3>
<p>CSR에 관해 이야기 하기 전에 먼저 SPA를 먼저 알 필요가 있다.</p>
<h4 id="spa-란-">SPA 란 ?</h4>
<p>SPA란 Single Page Application 으로 <strong>최초 한 번 페이지 전체를 로딩한 이후부터는 데이터만 변경하여 사용하는 단일 페이지로 구성된 웹 애플리케이션이다.</strong> SPA에서는 화면 구성에 필요한 모든 HTML을 클라이언트가 갖고 있고 서버 측에는 필요한 데이터를 요청하고 JSON으로 받기 때문에 기존의 어플리케이션에 비해 화면을 구성하는 속도가 빠르다</p>
<h4 id="csr이란-">CSR이란 ?</h4>
<p>CSR이란 말 그대로 클라이언트 측에서 렌더링이 진행된다는 뜻이다. CSR은 <strong>클라이언트 측에서 최초에 1번 서버에서 전체 페이지를 로딩하여 보여준다. 그 이후에는 사용자의 요청이 올 때마다, 자원(Resource)을 서버에서 제공한 후, 클라이언트가 해석하고 렌더링 하는 방식이다.</strong> 
참고로 여기에 Angular JS와 같이 SPA개발에 쉬운 JavaScript 프레임워크 등장했는데, 다시 클라이언트가 무거워지자 다시 View로만 관리하자는 의견에 의해 React JS가 등장하였다. </p>
<ul>
<li>동작과정 : HTML 다운로드-&gt; JS 다운로드 -&gt; JS 실행 -&gt; DATA 서버로부터 받기 -&gt; Content 확인가능</li>
</ul>
<h5 id="csr의-장점">CSR의 장점</h5>
<ul>
<li>클라이언트 사이드 렌더링은 사용자의 행동에 따라 필요한 부분만 다시 읽어들이기때문에 SSR 보다 조금 더 빠른 인터랙션이 가능하다.</li>
<li>page 전체를 요청하지 않고 페이지에 필요한 부분만 변경하게 하기 때문에, 모바일 네트워크에서도 빠른 속도로 렌더링이 가능하다.</li>
<li>서버의 부화를 줄여줄 수 있다.</li>
</ul>
<h5 id="csr의-단점">CSR의 단점</h5>
<ul>
<li><p>SEO(검색엔진최적화)가 좋지 않다. </p>
<blockquote>
<p>검색엔진은 index.html을 기준으로 하여 데이터, 태그 등을 통해 적합한 사이트 인지 판단을 한다. 하지만 CSR로 작성된 어플리케이션의 경우에는 html파일이 비어있다. 이 때문에 검색엔진에 걸리지 못한다.</p>
</blockquote>
</li>
<li><p>초기 구동속도가 느리다.</p>
<blockquote>
<p>SSR은 서버에서 view를 렌더링 한 후 가져오지만 CSR은 HTML 다운, JavaScript 파일, 각 종 자원(Resource)을 다운로드한 후에 브라우저에서 렌더링을 하기 때문에 초기 구동 속도가 느리다.</p>
</blockquote>
</li>
<li><p>보안 문제가 있다.</p>
<blockquote>
<p>SSR은 사용자 정보를 서버 측에서 세션으로 관리를 하지만, CSR은 쿠키 말고는 사용자 정보를 저장할 공간이 마땅치 않다.</p>
</blockquote>
</li>
</ul>
<h3 id="2-spasingle-page-application로-구성된-웹-앱에서-ssrserver-side-rendering이-필요한-이유에-대하여-설명해주세요">2. SPA(Single Page Application)로 구성된 웹 앱에서 SSR(Server-side Rendering)이 필요한 이유에 대하여 설명해주세요.</h3>
<p>위에서 말했듯이 CSR을 통한 SPA에는 초기 구동속도가 느리고 SEO가 좋지 않다고 했다. 이에 SSR을 적용하게 되면 초기 로딩 속도를 개선하여 UX를 향상시킬 수 있다. 이 뿐만 아니라 규모가 커질수록 초기 로딩속도가 느리다는 단점을 SSR을 적용시킴으로써 보완할 수 있다.</p>
<h3 id="3-nextjs-프로젝트를-세팅한-뒤-yarn-start-스크립트를-실행했을-때-실행되는-코드를-nextjs-github-레포지토리에서-찾은-뒤-해당-파일에-대한-간단한-설명을-첨부해주세요">3. Next.js 프로젝트를 세팅한 뒤 yarn start 스크립트를 실행했을 때 실행되는 코드를 nextjs github 레포지토리에서 찾은 뒤, 해당 파일에 대한 간단한 설명을 첨부해주세요.</h3>
<p><a href="https://nextjs.org/docs/getting-started">https://nextjs.org/docs/getting-started</a> (Next.js 세팅 가이드)
<a href="https://github.com/vercel/next.js/">https://github.com/vercel/next.js/</a> (Next.js Github 레포지토리)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Flux 패턴이란 ? ]]></title>
            <link>https://velog.io/@taehong0-0/React-Flux-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@taehong0-0/React-Flux-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Fri, 08 Jul 2022 06:39:26 GMT</pubDate>
            <description><![CDATA[<p>이번 포스트에서는 Flux 패턴에 대해 이야기 해보려고 한다. 개발을 시작하며 Redux Toolkit을 사용하게 되어 이를 이해하기 위해 Redux, Flux 패턴에 대해 학습을 진행하였다.</p>
<h2 id="🗣-flux-패턴의-등장-배경--mvc-모델의-한계">🗣 Flux 패턴의 등장 배경 : MVC 모델의 한계</h2>
<p>Flux 패턴이 등장하게된 배경은 <strong>MVC모델의 한계</strong> 때문이라고 한다.  </p>
<h3 id="mvc-패턴이란-">MVC 패턴이란 ?</h3>
<p>그럼 먼저 MVC 패턴에 대해 간략하게 알아보자. 
<img src="https://velog.velcdn.com/images/taehong0-0/post/0f9886e7-8935-4e6e-a1cf-165f194b19b9/image.png" alt="">
먼저 MVC 패턴은 위와 같은 구조로 되어있다. MVC 패턴은 이전에 부스트캠프 기간에 학습하고 적용해본 기억이 있다. </p>
<p>우선 Model에는 데이터가 정의되어 있으며 Controller는 Model의 데이터를 <strong>생성 / 조회 / 수정 / 삭제(CRUD)</strong> 한다. 그리고 해당하는 Model의 데이터는 View에 반영되어 나타난다. 그리고 View에서의 변화(사용자의 입력)는 Model의 데이터를 변화시키기도 한다.</p>
<h3 id="mvc-패턴의-문제점-">MVC 패턴의 문제점 ?</h3>
<p>MVC 패턴의 문제점은 어플리케이션의 규모가 커질수록 데이터 흐름의 복잡도가 너무나도 커진다는 것이었다. 
<img src="https://velog.velcdn.com/images/taehong0-0/post/5ed3b02a-bfab-4f8d-a4ec-7ca2f723b56e/image.png" alt="">
위의 그림은 Facebook에서 이야기한 MVC 패턴의 복잡성을 나타낸 그림이다. 
MVC패턴은 데이터의 변경 사항을 신속하게 전파하기 어렵다. 그리고 또한 View의 변화가 Model의 데이터를 변경하게 되고 이는 하나의 Model이 아닌 여러 Model이 될 수가 있다. 그래서 큰 문제는 각 모델에서 발생한 이벤트가 어플리케이션 전체로 무차별적으로 번져나갈 때 어떤 변화가 일어날지 예측할 수가 없다는 데에 있다.</p>
<h3 id="해결방안">해결방안</h3>
<p>Facebook은 이러한 문제점을 해결하기 위해 Flux패턴을 만들었다. 기존의 MVC 패턴은 Model의 데이터를 View에 반영하고, View의 입력,변화가 Model을 변경하는 양방향 데이터 흐름이었다. 하지만 Flux패턴은 아래의 그림과 같이 <strong>단방향 데이터 흐름</strong> 이다.</p>
<p><img src="https://velog.velcdn.com/images/taehong0-0/post/6fddae65-8b62-4963-a9e1-4a357a990af9/image.png" alt=""></p>
<p>위의 그림을 보면 Dispatcher -&gt; Store -&gt; View의 형태로 데이터가 흐른다. 
그렇다면 Action, Dispatcher, Store, View 는 각각 어떤 역할을 하는걸까?</p>
<h4 id="action">Action</h4>
<p>Action은 일종의 데이터의 상태를 변경할 수 있는 명령어 카드와 같은 역할을 한다. Dispatcher를 통해 Store를 변경하는데 이 때 Dispatcher의 데이터 묶음을 Action이라고 한다. Action은 대채로 액션 생성자(Action creator)에서 만들어집니다. 액션 생성자는 새로 발생한 액션의 타입과 데이터 페이로드를 액션 메시지로 묶어 디스패쳐로 전달합니다.</p>
<h4 id="dispatcher">Dispatcher</h4>
<p>Dispatcher는 Flux 어플리케이션의 모든 데이터 흐름을 관리하는 일종의 허브 역할 입니다. 액션이 발생하면 Dispatcher로 메세지나 액션 객체나 전달되고 Dispatcher에서는 이러한 메세지 혹은 액션 객체를 콜백 함수를 통해 스토어로 전달합니다. 만약 스토어가 서로를 의존하고 있다면 특정 스토어가 업데이트되기를 기다리게 해주는 waitFor()를 사용할 수 있다.</p>
<h4 id="store">Store</h4>
<p>Store는 어플리케이션의 상태와, 상태를 변경할 수 있는 메서드를 가지고 있다. 어떤 타입의 액션이 날아왔느냐에 따라 메서드를 다르게 적용해 상태를 변경하게 된다. 상태를 변경하기 위해서는 반드시 Dispatcher를 거쳐서 와야한다. </p>
<h4 id="view--controller-view">View / Controller View</h4>
<p>View는 Store에서 변경된 데이터를 가져와 화면을 보여주는 역할을 한다. Controller View 는 Store와 View 사이의 관리자 역할을 한다. Controller View 는 Store의 변경된 데이터를 받아와 모든 자식 View에게 전달하는 역할을 한다. </p>
<p>이전에 MVC 패턴을 학습하며 Flux에 대해 들어보고 간단히 찾아보았지만 이번 기회에 제대로 학습하며 정리를 하게 되었다. 그럼 다음 포스트에서는 Redux와 Redux Toolkit에 대해 알아봐야겠당 🙌</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2d 메타버스(게더타운)]]></title>
            <link>https://velog.io/@taehong0-0/2d-%EB%A9%94%ED%83%80%EB%B2%84%EC%8A%A4%EA%B2%8C%EB%8D%94%ED%83%80%EC%9A%B4</link>
            <guid>https://velog.io/@taehong0-0/2d-%EB%A9%94%ED%83%80%EB%B2%84%EC%8A%A4%EA%B2%8C%EB%8D%94%ED%83%80%EC%9A%B4</guid>
            <pubDate>Sun, 19 Jun 2022 10:16:06 GMT</pubDate>
            <description><![CDATA[<h2 id="2d-메타버스-구현">2d 메타버스 구현</h2>
<h3 id="코드">코드</h3>
<p>소켓이벤트를 통해 룸에 조인했음을 알리는 부분과 키보드 이벤트가 발생했을 경우 해당 위치로 캐릭터를 이동시키고 또 다른 캐릭터의 이동을 감지하여 이동시켜주는 부분의 코드이다.</p>
<pre><code class="language-javascript"> socket.emit(&#39;JOIN_ROOM&#39;, { id: socket.id, cId: 0 }, (userData: any) =&gt; setUser(userData));
    socket.on(&#39;makeRoomClient&#39;, (RoomUser: any) =&gt; setUsers(RoomUser));

    handleMove.addEventListener(&#39;keydown&#39;, (event) =&gt; {
      if (Object.keys(keyboardEvent).includes(event.code)) {
        setTempBackground((v: any) =&gt; {
          return keyboardEvent[event.code].background(v);
        });
        setUser((v: any) =&gt; keyboardEvent[event.code].character(v));
      }
    });

    handleMove.addEventListener(&#39;keyup&#39;, () =&gt; setUser((v: any) =&gt; ({ ...v, state: &#39;mid&#39; })));

    socket.on(&#39;changeMove&#39;, (list: any, temp: any) =&gt; {
      setUsers(list);
      if (temp !== null)
        (function () {
          setBackground((v: any) =&gt; temp ?? v);
          setMove(false);
        })();
      else setMove(true);
    });
</code></pre>
<p>백그라운드 이미지를 캐릭터 이동에 따라 해당하는 부분을 그려주는 코드이다.</p>
<pre><code class="language-javascript"> useEffect(() =&gt; {
    const context = canvasBackgroundRef.current?.getContext(&#39;2d&#39;);
    background.src = testMap;
    background.onload = () =&gt; {
      context?.drawImage(
        background,
        -marginBackground.left / 2,
        -marginBackground.top / 2,
        window.innerWidth,
        window.innerHeight,
        0,
        0,
        window.innerWidth * 2,
        window.innerHeight * 2,
      );
    };
  });

  useEffect(() =&gt; {
    const context = canvasBackgroundRef.current?.getContext(&#39;2d&#39;);
    context !== null &amp;&amp; context.clearRect(0, 0, window.screen.width, window.screen.height);
    context?.drawImage(
      background,
      -marginBackground.left / 2,
      -marginBackground.top / 2,
      window.innerWidth,
      window.innerHeight,
      0,
      0,
      window.innerWidth * 2,
      window.innerHeight * 2,
    );
  }, [marginBackground]);</code></pre>
<p>캐릭터가 이동햇을 경우 모션과 이미지를 바꿔주는 역할을 하고 위치를 수정해주게 된다.</p>
<pre><code class="language-javascript">  const characterCanvas = useRef&lt;any&gt;(null);

  const changeMotion = (direction: any, state: any) =&gt; dance[state] ?? direct[direction] + run[state];

  const [characterImg, setCharacterImg] = useState&lt;any&gt;();

  useEffect(() =&gt; {
    setCharacterImg(new Image());
  }, []);

  useEffect(() =&gt; {
    if (!characterCanvas.current) return;
    const actx = characterCanvas.current.getContext(&#39;2d&#39;);
    actx.clearRect(0, 0, window.innerWidth, window.innerHeight);
    Object.entries(users).map(([key, { x, y, direction, state }]: any) =&gt; {
      if (key !== socketId) {
        if (characterImg === undefined || !characterImg) return;
        characterImg.src = Yukey;

        actx?.drawImage(
          characterImg,
          changeMotion(direction, state),
          0,
          32,
          32,
          window.innerWidth / 2 + x - users[socketId].x,
          window.innerHeight / 2 + y - users[socketId].y,
          50,
          50,
        );
      } else {
        if (characterImg === undefined) return;
        characterImg.src = Yukey;

        actx?.drawImage(
          characterImg,
          changeMotion(direction, state),
          0,
          32,
          32,
          window.innerWidth / 2,
          window.innerHeight / 2,
          50,
          50,
        );
      }
    });
  }, [characterImg, users]);
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] DOM과 Virtual DOM]]></title>
            <link>https://velog.io/@taehong0-0/DOM%EA%B3%BC-Virtual-DOM</link>
            <guid>https://velog.io/@taehong0-0/DOM%EA%B3%BC-Virtual-DOM</guid>
            <pubDate>Sat, 18 Jun 2022 08:00:48 GMT</pubDate>
            <description><![CDATA[<h2 id="렌더링이란-">렌더링이란 ?</h2>
<p>HTML , CSS , JavaScript 등 개발자가 작성한 문서를 브라우저에서 출력하는 과정을 말한다.</p>
<h2 id="브라우저의-동작원리">브라우저의 동작원리</h2>
<p><img src="https://velog.velcdn.com/images/taehong0-0/post/92453b65-db55-4af6-b339-16659deb44db/image.png" alt=""></p>
<h3 id="1-domcssdom-트리-생성">1. Dom/CSSDOM 트리 생성</h3>
<p>첫 번째로 브라우저가 렌더링할 문서를 읽게 되는데 HTML과 CSS로 나눠서 읽게 된다. 이때 HTML과 CSS는 단순한 텍스트이므로 각각 연산과 관리가 가능하도록 HTML Parser와 CSS Parser를 사용해 관리가 가능한 Object Model로 만든다.</p>
<p>여기서 렌더링 엔진은 더 나은 사용자 경험을 위해 가능한 빠르게 내용을 표시하게 만들어졌는데, 따라서 모든 HTML, CSS파싱이 끝나기도 전에 이후의 과정을 수행하여 미리 사용자에게 보여줄 수 있는 내용들을 출력한다.</p>
<h3 id="2-렌더링-트리rendering-tree-생성">2. 렌더링 트리(Rendering Tree) 생성</h3>
<p> 단계에서 DOM 트리와 CSSOM 트리가 생성되면, 이 둘을 연결하여 표시해야 할 순서로 내용을 그려낼 수 있도록 하기 위해 렌더 트리를 생성해준다. 이 과정을 Attachment 라고 한다. DOM 트리의 모든 노드들은 ‘attach’ 라는 메소드가 있다. 이 메소드는 스타일 정보를 계산해서 객체형태로 반환한다.
이 과정은 동기적(synchronous) 작업이며, DOM 트리에 새로운 노드가 추가되면 그 노드의 attach 메소드가 실행된다.
순수한 요소들의 구조와 텍스트만 존재하는 DOM 트리와는 달리 렌더 트리는 스타일 정보가 설정되어 있으며 실제 화면에 표현되는 노드들로만 구성된다. display: none;과 같은 속성이 설정된 노드는 화면에서 어떠한 공간도 차지하고 있지 않기 때문에 렌더 트리를 만드는 과정에서 제외된다.</p>
<h4 id="노드란-">노드란 ?</h4>
<p>tree 구조에서 root 노드를 포함한 모든 개개의 개체를 node라고 표현한다. head, body, title, script, h1, HEADER-1 등의 태그뿐 아니라 태그 안의 텍스트나 속성 등도 모두 node에 속한다.
이중 HTML 태그를 요소노드(Element Node)라고 부르고 요소 노드 안에 있는 글자를 Text 노드(Text Node)라고 부르기도 한다.</p>
<h3 id="3-레이아웃layout-단계">3. 레이아웃(Layout) 단계</h3>
<p>레이아웃 단계에서는 뷰포트 내에서 각 요소의 정확한 위치와 크기를 정확하게 캡처하는 Box 모델이 출력됩니다. 모든 상대적인 측정값은 화면에서 절대적인 픽셀로 변환됩니다.</p>
<h3 id="4-페인팅-단계">4. 페인팅 단계</h3>
<p>마지막으로 렌더링 트리의 각 노드를 화면의 실제 픽셀로 변환하게 됩니다. 레이아웃 단계에서 모든 계산이 완료가 되면, 화면에 요소들을 그리게 됩니다. 이 단계를 “페인팅” 또는 “래스터화”라고 합니다.</p>
<p>이미 레이아웃 단계에서 각 노드들이 위치, 크기, 색상 등 스타일이 모두 계산이 되었기 때문에 화면에 실제 픽셀로 변환하게 됩니다.</p>
<h3 id="dom의-변화">DOM의 변화</h3>
<p>DOM에 변화가 생기면, 렌더트리를 재생성하고 (그러면 모든 요소들의 스타일이 다시 계산됩니다) 레이아웃을 만들고 페인팅을 하는 과정이 다시 반복된다.
복잡한 SPA(싱글 페이지 어플리케이션) 에서는 DOM 조작이 많이 발생해요. 그 뜻은 그 변화를 적용하기 위해 브라우저가 많이 연산을 해야한단 소리고, 전체적인 프로세스를 비효율적으로 만든다.</p>
<h2 id="virtual-dom">Virtual DOM</h2>
<h3 id="virtual-dom이란-">Virtual DOM이란 ?</h3>
<p>우선 위의 과정에서 DOM에 변화가 생기면 과정을 반복하며 비효율적이라는 것을 알게되었다. 그러면 Virtual DOM은 이를 어떻게 해결했을까?
우선 Virtual DOM을 리액트 공식 홈페이지에서는 다음과 같이 설명한다.</p>
<blockquote>
<p>Virtual DOM (VDOM)은 UI의 이상적인 또는 “가상”적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 “실제” DOM과 동기화하는 프로그래밍 개념입니다. 이 과정을 재조정이라고 합니다.</p>
</blockquote>
<p>조금더 풀어서 설명하자면 Virtual DOM은 만약에 뷰에 변화가 생기면, 그 변화를 실제 DOM에 적용하기 전에 먼저 가상의 DOM (Virtual DOM)에 적용을 시킨 후 실제 DOM과 비교하여 변화된 부분만을 수정하는 개념이다. 이러한 방법을 통해 기존의 DOM보다 연산의 양을 줄일 수 있어 성능이 개선된다.</p>
<h3 id="virtual-dom의-장점">Virtual DOM의 장점</h3>
<p>OM 조작의 실제 문제는 각 조작이 레이아웃 변화, 트리 변화와 렌더링을 일으킨다. 그래서, 예를 들어 30개의 노드를 하나 하나 수정하면, 그 뜻은 30번의 (잠재적인) 레이아웃 재계산과 30번의 (잠재적인) 리렌더링을 초래하게 된다.</p>
<p>Virtual DOM 은 DOM 차원에서의 더블 버퍼링과 비슷한 개념이다. 변화가 일어나면 그걸 가상의 DOM 트리에 적용시킨다. 이 DOM 트리는 렌더링도 되지 않기때문에 연산 비용이 적다. 연산이 끝나고나면 그 최종적인 변화를 한번에 묶어서  실제 DOM에 보내주게 된다. 이 방식으로 하게 되면 레이아웃 계산과 리렌더링의 규모는 커지겠지만, 하나로 묶어서 보내기 때문에 연산의 횟수는 확실히 줄어들게 된다.
사실, 이 과정은 Virtual DOM 이 없이도 이뤄질수 있어요. 그냥, 변화가 있을 때, 그 변화를 묶어서 DOM fragment 에 적용한 다음에 기존 DOM 에 던져주면 돼요.</p>
<h3 id="virtual-dom이-하는-일">Virtual DOM이 하는 일</h3>
<p>DOM fragment를 관리하는 과정을 수동으로 하나하나 작업 할 필요 없이, 자동화하고 추상화한다. 그 뿐만 아니라, 만약에 이 작업을 직접 한다면, 기존 값 중 어떤게 바뀌었고 어떤게 바뀌지 않았는지 계속 파악하고 있어야하는데 (그렇지 않으면 수정 할 필요가 없는 DOM 트리도 업데이트를 하게 될 수도 있기 때문에), 이것도 Virtual DOM 이 이걸 자동으로 해준다.</p>
<p>마지막으로, DOM 관리를 Virtual DOM 이 하도록 함으로써, 컴포넌트가 DOM 조작 요청을 할 때 다른 컴포넌트들과 상호작용을 하지 않아도 되고, 특정 DOM 을 조작할 것 이라던지, 이미 조작했다던지에 대한 정보를 공유 할 필요가 없다. 즉, 각 변화들의 동기화 작업을 거치지 않으면서도 모든 작업을 하나로 묶어줄 수 있다는 것이다.</p>
<h3 id="참고">참고</h3>
<p><a href="https://velopert.com/3236">https://velopert.com/3236</a>
<a href="https://velog.io/@kja/Virtual-DOM">https://velog.io/@kja/Virtual-DOM</a>
<a href="https://www.howdy-mj.me/dom/what-is-dom/">https://www.howdy-mj.me/dom/what-is-dom/</a>
<a href="https://boxfoxs.tistory.com/408">https://boxfoxs.tistory.com/408</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 forEach / for (동기 / 비동기)]]></title>
            <link>https://velog.io/@taehong0-0/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-forEach-for-%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0</link>
            <guid>https://velog.io/@taehong0-0/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-forEach-for-%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0</guid>
            <pubDate>Sun, 01 May 2022 08:27:04 GMT</pubDate>
            <description><![CDATA[<h2 id="문제점">문제점</h2>
<p>프로젝트를 진행하며 배열에 담긴 요소들을 s3에 이미지 업로드를 해야해서 아래와 같이 코드를 작성하였다.</p>
<pre><code class="language-javascript">srcList.forEach(async (tag, idx) =&gt; {
    ...
  const data = await s3.upload(param).promise();
  result = result.replace(srcData, data.Location);
      ...
});
  console.log(result);</code></pre>
<p>이런 식으로 작성하였더니 result 값에 원하던 값이 들어가있지 않고 초기에 넣어준 값이 나왔다.</p>
<p><strong>그 이유는 forEach는 내부에 들어있는 순차적으로 배열을 돌며 callback을 실행을 하기 때문에 callback이 동기인지 비동기인지에 따라 달라지지 않는다고 한다. 따라서 forEach는 비동기를 기다려 주지 않는다.</strong></p>
<p>그래서 위와 같이 작성한 코드는 내부 callback에서도 비동기 처리를 기다려 주지 않고 result를 출력했을 때도 내부의 비동기처리를 기다려주지 않기 때문에 초기값이 출력된다고 한다.</p>
<blockquote>
<p><strong>Note</strong> : forEach expects a synchronous function.
forEach does not wait for promises. Make sure you are aware of the implications while using promises (or async functions) as forEach callback.
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach">mdn web docs</a></p>
</blockquote>
<p>위와 같이 mdn web docs 의 Array.prototype.forEach()의 설명에 forEach는 비동기 함수를 기다려주지 않는다고 나와있다.</p>
<h2 id="해결방안">해결방안</h2>
<h3 id="1-forof">1. for...of</h3>
<blockquote>
<p> for...of 명령문은 반복가능한 객체 (Array, Map, Set, String, TypedArray, arguments 객체 등을 포함)에 대해서 반복하고 각 개별 속성값에 대해 실행되는 문이 있는 사용자 정의 반복 후크를 호출하는 루프를 생성합니다.
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/for...of">mdn web docs</a></p>
</blockquote>
<p>for...of 명령문은 각 항목에 대해 비동기 처리를 기다리기 때문에 대안이 될 수 있다. 그래서 위의 코드를 아래와 같이 수정하였다.</p>
<pre><code class="language-javascript">for (let tag of srcList) {
  const data = await s3.upload(param).promise();
  result = result.replace(srcData, data.Location);
}
console.log(result);</code></pre>
<p>위와 같이 코드를 작성하니 비동기 처리가 성공적으로 이루어 지기 때문에 result값이 정상적으로 출력이 되었다. 하지만 for...of는 병렬적으로 처리하지 않기 때문에 배열이 길어질 경우 시간이 오래 걸릴 수 있다고 한다. 위의 단점을 해결하기 위한 방법으로는 Promise.all()이 있다.</p>
<h3 id="2-promiseall">2. Promise.all()</h3>
<p>이 방법은 비동기 처리의 시간이 오래걸릴 경우 순차적으로 하는 것 보다 병렬적으로 처리하도록 한다. 비동기 함수를 Promise 배열로 만든 후 Promise.all()을 사용하여 모든 비동기 함수를 병렬적으로(동시에) 실행할 수 있다. 
그래서 아래와 같이 코드를 수정하였다.</p>
<pre><code class="language-javascript">const promiseList = srcList.map(async (tag, idx) =&gt; {
      ...
      return await s3
        .upload(param)
        .promise()
        .then((data) =&gt; {
          result = result.replace(srcData, data.Location);
        });
    });
    await Promise.all(promiseList);</code></pre>
<p>참고 : <a href="https://interacting.tistory.com/162">자바스크립트 foreach와 for의 동기 비동기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] React Router]]></title>
            <link>https://velog.io/@taehong0-0/React-Router</link>
            <guid>https://velog.io/@taehong0-0/React-Router</guid>
            <pubDate>Sun, 06 Mar 2022 12:15:16 GMT</pubDate>
            <description><![CDATA[<h2 id="spa란-">SPA란 ?</h2>
<p>Single Page Application의 줄임말으로 하나의 페이지로 이루어진 어플리케이션을 말한다.</p>
<p>기존에는 사용자가 페이지를 이동하게 되면 새로운 html 파일을 받아와 보여주었다. </p>
<p>리액트와 같은 라이브러리나 프레임워크를 사용하게 되면 사용자의 브라우저에서 view를 렌더링 하게 되고 인터렉션이 일어나게 되면 필요한 부분만을 받아와서 다시 리렌더링을 진행한다.</p>
<p>SPA는 말 그대로 하나의 페이지로 이루어진 어플리케이션 이지만, 로딩된 자바스크립트와 url에 따라 다른 view를 보여준다. 이렇게 url에 따라 다른 view를 보여주는 것을 Routing이라고 한다.</p>
<h2 id="라우팅이란-">라우팅이란 ?</h2>
<p>위에서 말했듯이 URL에 따라 그에 해당하는 view를 보여주는 것이다. 리액트에서는 라우팅 처리를 위해 react router를 가장 많이 사용한다.</p>
<h2 id="react-router">React Router</h2>
<p>React Router를 사용하면 앱에서 발생하는 라우팅이 location과 history 같은 wep API 와 연동된다.</p>
<h3 id="react-router-설치">React Router 설치</h3>
<pre><code>npm install react-router-dom --save</code></pre><h3 id="react-router-사용">React Router 사용</h3>
<pre><code class="language-javascript">import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom&quot;;
import { BrowserRouter as Router } from &quot;react-router-dom&quot;;
import { RecoilRoot } from &quot;recoil&quot;;
import App from &quot;./App&quot;;

ReactDOM.render(
  &lt;React.StrictMode&gt;
    &lt;RecoilRoot&gt;
      &lt;Router&gt;
        &lt;React.Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
          &lt;App /&gt;
        &lt;/React.Suspense&gt;
      &lt;/Router&gt;
    &lt;/RecoilRoot&gt;
  &lt;/React.StrictMode&gt;,
  document.getElementById(&quot;root&quot;)
);</code></pre>
<p>위와 같이 밖에 Router 태그로 감싸주어야 한다.</p>
<pre><code class="language-javascript">&lt;Switch&gt;
      // path에 해당하는 url이 오면 해당하는 component를 보여주겠다.
    &lt;Route path=&quot;/main&quot; component={MainPage} /&gt;
    &lt;Route path=&quot;/sub&quot; component={Page} /&gt;
    &lt;Route path=&quot;/ChatRoom&quot; component={ChatRoom} /&gt;
    &lt;Route path=&quot;/Project&quot; component={Project} /&gt;
    &lt;Redirect path=&quot;*&quot; to=&quot;/main&quot; /&gt;
&lt;/Switch&gt;</code></pre>
<p>위와 같이 Switch 태그로 감싸주고 Route태그 안에서 위와 같이 사용하면 된다.</p>
<pre><code class="language-javascript">import { Link } from &quot;react-router-dom&quot;;

export default function example() {
  return (
    &lt;Link to=&quot;/main&quot;&gt;메인으로 가기&lt;/Link&gt;
  );
}</code></pre>
<p>와 같이 Link 태그를 사용하게 되면 이후 a태그로 변환되어 url이 변경되며 main에 해당하는 컴포넌트가 보여진다.</p>
<h4 id="결국-a태그로-변환되지만-link태그를-사용하는-이유">결국 a태그로 변환되지만 Link태그를 사용하는 이유?</h4>
<blockquote>
<p>효율을 위해서 사용한다고 한다. a태그로 하게 되면 서버에 문서를 요청하게 된다. 그런데 Link를 이용하면 화면의 특정부분만 바꿔주면 되는 경우에 효율적인 화면 전환을 할 수 있다. 단순히 경로만 바꿔 불필요한 렌더링을 피한다. 완전히 외부사이트로 가야할때는 당연히 a태그를 사용해야한다.</p>
</blockquote>
<h2 id="url-params-사용">URL params 사용</h2>
<p><a href="https://v5.reactrouter.com/web/example/url-params">https://v5.reactrouter.com/web/example/url-params</a></p>
<h3 id="참고-사이트">참고 사이트</h3>
<p><a href="https://velog.io/@somangoi/TIL042-React-Session-2-Router">https://velog.io/@somangoi/TIL042-React-Session-2-Router</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] React 란 ?]]></title>
            <link>https://velog.io/@taehong0-0/React-%EB%9E%80</link>
            <guid>https://velog.io/@taehong0-0/React-%EB%9E%80</guid>
            <pubDate>Sat, 05 Mar 2022 11:50:14 GMT</pubDate>
            <description><![CDATA[<h2 id="react-란-">React 란 ?</h2>
<ul>
<li>React는 페이스북이 만든 사용자 UI 구축을 위한 라이브러리</li>
<li>SPA(Single Page Application)로 사이트를 표현할 수 있도록하는 프레임 워크<h2 id="react-특징">React 특징</h2>
<h3 id="컴포넌트-단위-개발">컴포넌트 단위 개발</h3>
<blockquote>
<p> 캡슐화된 컴포넌트가 스스로 상태를 관리하고 복잡한 UI도 효과적으로 구성할 수 있음</p>
</blockquote>
</li>
</ul>
<h3 id="선언적">선언적</h3>
<blockquote>
<p>리액트는 대화형 UI를 작성하기에 유리하다. 데이터가 변경되었을 때 효율적으로 렌더링을 수행할 수 있도록 한다.</p>
</blockquote>
<h3 id="virtual-dom을-통한-빠른-ui-업데이트">Virtual Dom을 통한 빠른 UI 업데이트</h3>
<h4 id="virtual-dom-이란-">Virtual Dom 이란 ?</h4>
<ul>
<li>실제 돔에 접근하여 조작하는 대신, 이를 추상화시킨 자바스크립트 객체를 이용한다.</li>
</ul>
<h4 id="virtual-dom-을-사용하는-이유-">Virtual Dom 을 사용하는 이유 ?</h4>
<p> 기존에는 Dom에 변화가 일어나게 되면 CSS를 다시 연산하고 레이아웃을 다시 잡고 그려주는데 많은 시간을 사용하게 된다. 이 부분에서 Virtual Dom을 사용하게 되면 이전의 Dom과 비교하여 바뀐 부분만을 실제 Dom에 적용하도록 하여 연산을 많이 줄여주게 된다.</p>
<p><a href="https://velog.io/@taehong0-0/DOM%EA%B3%BC-Virtual-DOM">https://velog.io/@taehong0-0/DOM%EA%B3%BC-Virtual-DOM</a></p>
]]></description>
        </item>
    </channel>
</rss>