<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Flutter, JavaScript]]></title><description><![CDATA[Flutter, JavaScript]]></description><link>https://blog.skku-comit.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 06:14:17 GMT</lastBuildDate><atom:link href="https://blog.skku-comit.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[[ZSH] tree 사용하기]]></title><description><![CDATA[들어가며
큰 규모의 프로젝트를 출시한 뒤, 후일을 위해서 더 늦기 전에 파일 정리 및 문서화를 진행해야했다.
문서화 작업을 하는 중에 기왕 정리하는 거 파일 구조를 이쁘게 트리 구조로 나열하여 코멘트를 달면 나중에 보더라도 이해하기 더 쉬울 것 같았다.
어떻게 해야 간지나는 트리 구조를 만들 수 있을까 방법을 찾다보니 역시나 파일 구조를 트리로 이쁘게 출력해주는 커맨드 툴이 존재했다.
tree 커맨드에 대해서 알아보고 알짜배기 내용만 정리했다....]]></description><link>https://blog.skku-comit.dev/zsh-tree</link><guid isPermaLink="true">https://blog.skku-comit.dev/zsh-tree</guid><category><![CDATA[zsh]]></category><category><![CDATA[Tree]]></category><category><![CDATA[command line]]></category><dc:creator><![CDATA[Donghan Kim]]></dc:creator><pubDate>Wed, 21 Feb 2024 08:43:33 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-65ok7ja06rca66mw">들어가며</h1>
<p>큰 규모의 프로젝트를 출시한 뒤, 후일을 위해서 더 늦기 전에 파일 정리 및 문서화를 진행해야했다.</p>
<p>문서화 작업을 하는 중에 기왕 정리하는 거 파일 구조를 이쁘게 트리 구조로 나열하여 코멘트를 달면 나중에 보더라도 이해하기 더 쉬울 것 같았다.</p>
<p>어떻게 해야 간지나는 트리 구조를 만들 수 있을까 방법을 찾다보니 역시나 파일 구조를 트리로 이쁘게 출력해주는 커맨드 툴이 존재했다.</p>
<p>tree 커맨드에 대해서 알아보고 알짜배기 내용만 정리했다.</p>
<h1 id="heading-7isk7lmy">설치</h1>
<p>나는 Mac을 사용한다. 기본적으로 zsh에 내장되어있진 않으니 Homebrew를 이용해서 직접 <code>tree</code>를 설치한다.</p>
<pre><code class="lang-bash">brew install tree
</code></pre>
<h1 id="heading-7iks7jqp67kv">사용법</h1>
<p>사용법은 참으로 단순하다. 그냥 터미널에다가 <code>tree dirPath</code> 해주면 해당 경로 아래의 모든 파일 및 폴더 구조를 아래와 같이 이쁜 트리로 출력해준다.</p>
<pre><code class="lang-bash">tree ./lib/src
</code></pre>
<pre><code class="lang-plaintext">./lib/src
├── animation_widgets
├── common_widgets
│   ├── app_bar
│   ├── buttons
│   ├── dialog
│   ├── image_widgets
│   ├── indicators
│   ├── list_items
│   ├── modal
│   └── search_bar
├── constants
├── demo_widgets
├── error

... 생략 ...
</code></pre>
<h3 id="heading-7ji17iwy">옵션</h3>
<p>파라미터를 추가하여 다양한 옵션을 설정할 수 있다. <code>-a</code> : 숨김 폴더 및 파일 표시 <code>-d</code> : 디렉토리만 표시</p>
<p>이외에도 다양한 옵션을 함께 활용할 수 있는데, 나는 이정도면 충분해서 더 알아보진 않았다.</p>
<p>더 자세한 정보가 필요하다면 <code>man tree</code>를 통해 확인해보자.</p>
<p>터미널에 출력되는 걸 복붙하는 건 조금 애송이 같으니까 파일을 따로 생성하여 tree 정보를 저장했다.</p>
<pre><code class="lang-bash">tree ./lib/src -d &gt; tree.md
</code></pre>
<h1 id="heading-6rkw66gg">결론</h1>
<p>어디서 본 듯한 이쁜 디렉토리 구조를 이제 나도 출력할 수 있다!</p>
]]></content:encoded></item><item><title><![CDATA[GraphQL vs REST API]]></title><description><![CDATA[GraphQL은 API를 위한 Query Language이다.
우리에게 더 익숙한 REST API와 GraphQL의 차이점이 무엇인지 알아보고, Next.js에서 GraphQL을 사용하는 방법에 대해 알아보겠다.
REST API
Representational State Transfer의 약자로 URL로 자원을 표현하고 HTTP 요청을 통해 CRUD를 실행하는 API이다.
GET api/todos/1PUT api/todos/1
처럼 HTTP 메소...]]></description><link>https://blog.skku-comit.dev/graphql-vs-rest-api</link><guid isPermaLink="true">https://blog.skku-comit.dev/graphql-vs-rest-api</guid><category><![CDATA[GraphQL]]></category><category><![CDATA[REST API]]></category><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[권서진]]></dc:creator><pubDate>Sun, 04 Feb 2024 16:09:53 GMT</pubDate><content:encoded><![CDATA[<p>GraphQL은 API를 위한 Query Language이다.</p>
<p>우리에게 더 익숙한 REST API와 GraphQL의 차이점이 무엇인지 알아보고, Next.js에서 GraphQL을 사용하는 방법에 대해 알아보겠다.</p>
<h3 id="heading-rest-api">REST API</h3>
<p>Representational State Transfer의 약자로 URL로 자원을 표현하고 HTTP 요청을 통해 CRUD를 실행하는 API이다.</p>
<p>GET api/todos/1<br />PUT api/todos/1</p>
<p>처럼 HTTP 메소드와 URL을 조합해서 여러 요청을 할 수 있다.</p>
<p>하지만 REST API에는 2가지 단점이 있다.</p>
<h4 id="heading-over-fetching"><strong>Over Fetching</strong></h4>
<p>API 호출 시 필요 이상의 데이터를 가져오는 것이다. API를 요청하면 좋든 싫든 주는 대로 받아야 한다. 따라서, 자신이 클라이언트에 보여줄 것 이상으로 데이터를 많이 받기 마련이다.</p>
<h4 id="heading-under-fetching"><strong>Under Fetching</strong></h4>
<p>한 번의 API 호출로 원하는 모든 데이터를 가져오지 못하는 것이다. 즉 필요한 모든 데이터를 가져오기 위해서 2번, 3번 이상의 API 호출을 해야 하는 경우이다.</p>
<p>예시로 스터디 목록과 멘토 목록을 API로 불러오게 될 경우<br />GET api/study, GET api/study/mentor<br />이런 식으로 구분해서 2번의 API 요청을 보내야 한다.</p>
<p>API 요청 수가 많아지면 로딩 속도가 저하되기 때문에 비경제적이다.</p>
<h3 id="heading-graphql">GraphQL</h3>
<p>GraphQL은 위의 Over Fetching과 Under Fetching을 해결하기 위해 등장한 specification이다. GraphQL은 하나의 endpoint에 자신이 원하는 만큼만 데이터를 요청하고 받기 때문에 overfetching, underfetching이 없다.</p>
<p>그러면 REST 안 쓰고 다들 GraphQL 쓸텐데 왜 그렇지 만은 않을까?</p>
<p>GraphQL도 단점이 있다.</p>
<ol>
<li><p>HTTP 캐싱을 활용하기 어렵다<br /> REST의 경우 endpoint 별로 캐싱을 지정할 수 있는 반면<br /> GraphQL은 단일 endpoint 이기 때문에 활용하기 어렵다.</p>
</li>
<li><p>에러 핸들링이 어렵다<br /> 여러 데이터를 하나의 endpoint에 묶어 요청하니 당연히 어렵다</p>
</li>
<li><p>요청이 무거워진다<br /> 클라이언트 측에서 보내는 요청의 쿼리가 복잡해지고<br /> 이에 대응하기 위해 백엔드에서는 더욱 정교하게 설계해야 한다.</p>
</li>
</ol>
<p>결론: GraphQL은 경제적이지만 설계와 사용이 복잡해진다.</p>
<h3 id="heading-nextjs-graphql">Next.js에서의 GraphQL</h3>
<p><a target="_blank" href="https://beta.pokeapi.co/graphql/console/">https://beta.pokeapi.co/graphql/console/</a></p>
<p>위의 링크는 포켓몬 데이터 GraphQL 서버이다.<br />간단하게 포켓몬의 id, 이름, 기술명을 받아와서 페이지에 뿌려보겠다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707031711158/ddcdf8d1-884e-493b-a168-c14ec329d07a.png" alt class="image--center mx-auto" /></p>
<p>API의 endpoint는 <a target="_blank" href="https://beta.pokeapi.co/graphql/v1beta">https://beta.pokeapi.co/graphql/v1beta</a>로 하나이고<br />Explorer에서 원하는 데이터를 선택하고 실행하면 데이터를 확인할 수 있다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707031789237/62e00112-4e95-40a7-a8d1-32b498adbd90.png" alt class="image--center mx-auto" /></p>
<p>vscode에서 Next 프로젝트를 하나 생성 해준다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707032097195/23b42dc2-6819-4930-8563-6fe73a61a361.png" alt /></p>
<p>page.tsx에 내용을 싹 날려주고</p>
<pre><code class="lang-bash">npm i @apollo/client graphql
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ApolloClient, InMemoryCache, gql } <span class="hljs-keyword">from</span> <span class="hljs-string">"@apollo/client"</span>;

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> ApolloClient({
  cache: <span class="hljs-keyword">new</span> InMemoryCache(),
  uri: <span class="hljs-string">"https://beta.pokeapi.co/graphql/v1beta"</span>,
});

<span class="hljs-keyword">interface</span> Pokemon {
  base_experience: <span class="hljs-built_in">number</span>;
  id: <span class="hljs-built_in">number</span>;
  name: <span class="hljs-built_in">string</span>;
  pokemon_v2_pokemonabilities: {
    pokemon_v2_ability: {
      name: <span class="hljs-built_in">string</span>;
    };
  }[];
}

<span class="hljs-keyword">const</span> GET_POKEMONS = gql<span class="hljs-string">`
  query GetPokemons($first: Int!) {
    pokemon_v2_pokemon(limit: $first) {
      id
      name
      base_experience
      pokemon_v2_pokemonabilities {
        pokemon_v2_ability {
          name
        }
      }
    }
  }
`</span>;

<span class="hljs-keyword">const</span> getPokemons = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> { data } = <span class="hljs-keyword">await</span> client.query({
    query: GET_POKEMONS,
    variables: { first: <span class="hljs-number">10</span> },
  });
  <span class="hljs-keyword">return</span> data.pokemon_v2_pokemon;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Page</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> pokemons = <span class="hljs-keyword">await</span> getPokemons();
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"grid grid-cols-5 gap-6"</span>&gt;
      {pokemons.map(<span class="hljs-function">(<span class="hljs-params">pokemon: Pokemon</span>) =&gt;</span> (
        &lt;div key={pokemon.id} className=<span class="hljs-string">"border border-white w-80 p-6"</span>&gt;
          &lt;h2 className=<span class="hljs-string">"capitalize text-2xl"</span>&gt;{pokemon.name}&lt;/h2&gt;
          &lt;p&gt;Base Experience: {pokemon.base_experience}&lt;/p&gt;
          &lt;p&gt;
            Abilities:{<span class="hljs-string">" "</span>}
            {pokemon.pokemon_v2_pokemonabilities
              .map(<span class="hljs-function">(<span class="hljs-params">ability</span>) =&gt;</span> ability.pokemon_v2_ability.name)
              .join(<span class="hljs-string">" "</span>)}
          &lt;/p&gt;
        &lt;/div&gt;
      ))}
    &lt;/div&gt;
  );
}
</code></pre>
<p>위와 같이 작성해주면 'use client' 나 ApolloProvider 등 사용 없이 간편하게 graphql을 가져다 사용할 수 있다.</p>
<p>주의해야 할 점은 interface를 명시해줘야만 객체가 자동 완성 된다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707062764516/5af07622-a0b3-47e4-8d44-f728ca2037d0.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-7lc46rog7j6q66om">참고자료</h2>
<p><a target="_blank" href="https://graphql.org/">https://graphql.org/</a><br /><a target="_blank" href="https://yozm.wishket.com/magazine/detail/2113/">https://yozm.wishket.com/magazine/detail/2113/</a><br /><a target="_blank" href="https://www.tabnine.com/code/javascript/functions/apollo-client/ApolloClient/query">https://www.tabnine.com/code/javascript/functions/apollo-client/ApolloClient/query</a></p>
]]></content:encoded></item><item><title><![CDATA[[Next.js] parallel routes & intercepting routes]]></title><description><![CDATA[트위터 로그인 모달창을 만들어보며 넥스트의 parallel routes 와 intercepting routes 을 학습한 내용을 정리해보았습니다.
트위터 로그인 창을 확인해봅시다.

루트 디렉토리 화면을 배경으로 i/flow/login 페이지가 동시에 표시되고 있습니다.
저는 app router 를 학습하기 전까지는 createPortal 을 사용하여 포탈 영역에 로그인 컴포넌트를 띄우는 방식을 사용했었습니다.
const NoLogin =()=...]]></description><link>https://blog.skku-comit.dev/nextjs-parallel-routes-intercepting-routes</link><guid isPermaLink="true">https://blog.skku-comit.dev/nextjs-parallel-routes-intercepting-routes</guid><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[박지은]]></dc:creator><pubDate>Thu, 01 Feb 2024 02:48:55 GMT</pubDate><content:encoded><![CDATA[<p>트위터 로그인 모달창을 만들어보며 넥스트의 parallel routes 와 intercepting routes 을 학습한 내용을 정리해보았습니다.</p>
<p>트위터 로그인 창을 확인해봅시다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706749133821/6ebdcd31-9d1c-4529-a9de-5d79607fa7de.png" alt class="image--center mx-auto" /></p>
<p>루트 디렉토리 화면을 배경으로 i/flow/login 페이지가 동시에 표시되고 있습니다.</p>
<p>저는 app router 를 학습하기 전까지는 createPortal 을 사용하여 포탈 영역에 로그인 컴포넌트를 띄우는 방식을 사용했었습니다.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> NoLogin =<span class="hljs-function">()=&gt;</span>{

    <span class="hljs-keyword">let</span> [loginModal, setLoginModal] = useState&lt;boolean&gt;(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">let</span> [signupModal, setsignupModal] = useState&lt;boolean&gt;(<span class="hljs-literal">false</span>);


    <span class="hljs-keyword">const</span> loginModalHandler = useCallback(<span class="hljs-function">(<span class="hljs-params">sign:boolean</span>)=&gt;</span>{
        setLoginModal(sign);
    }, [])

    <span class="hljs-keyword">const</span> SignupModalHandler = useCallback(<span class="hljs-function">(<span class="hljs-params">sign:boolean</span>)=&gt;</span>{
      setsignupModal(sign);
  }, [])

    <span class="hljs-keyword">return</span>(
       {<span class="hljs-comment">/* 생략 */</span>}
        &lt;div className=<span class="hljs-string">"pt-6"</span>&gt;
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'/'</span> <span class="hljs-attr">as</span>=<span class="hljs-string">'/i/flow/login'</span> <span class="hljs-attr">scroll</span>=<span class="hljs-string">{false}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"rounded-2xl border-2 p-2"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span>=&gt;</span>{loginModalHandler(true)}}&gt;
              로그인
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span></span>
          {loginModal &amp;&amp; createPortal(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">LoginModal</span> <span class="hljs-attr">loginModalHandler</span>=<span class="hljs-string">{()</span>=&gt;</span>{loginModalHandler(false)}}/&gt;</span>, <span class="hljs-built_in">document</span>.body)}
      &lt;/div&gt;
    {<span class="hljs-comment">/* 생략 */</span>}
)
</code></pre>
<p>href 경로와 as 경로를 달리해 뒷배경으로 메인 화면을 띄우고 그 위에 로그인 모달을 띄우는 데 성공했습니다. 하지만 이 방식은 loginModal state 를 정의해서 modal 의 상태를 계속 감시해야하는 번거로움이 있습니다.</p>
<p>Next.js app router 를 학습하며, 모달창을 쉽게 구현하도록 해주는 기능이 내재되어있다는 것을 알게되었고, 위의 createPortal 방식을 넥스트의 parallel routes, intercepting routes 를 활용한 모달창 방식으로 바꿔보고자 하였습니다.</p>
<hr />
<h2 id="heading-parallel-routes">parallel routes</h2>
<p>하나의 레이아웃에서 여러 페이지를 동시에 보여줄 수 있는 기능입니다. slots(<code>@folderName</code>)에 의해 생성이 되며, 이렇게 생성된 slot 속 페이지는 slot과 같은 레벨의 레이아웃에 props 로 전달됩니다. (이 때, slot 폴더는 url 에 영향을 주지 않고 무시됩니다.)</p>
<p>넥스트 앱라우터 공식 문서를 참고하며 parallel routes 에 대해 자세히 알아봅시다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706752792590/9318f0b9-159f-4f15-84f0-5221bd7f53b7.png" alt class="image--center mx-auto" /></p>
<p><code>@analytics</code> 와 <code>@team</code> 이라는 slot 이 생성되었습니다. 이들은 모두 같은 레벨의 레이아웃에 props 로 다음과 같이 전달되어 렌더링됩니다.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Layout</span>(<span class="hljs-params">{
  children,
  team,
  analytics,
}: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
}</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      {children}
      {team}
      {analytics}
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<p>자 이제 다시 트위터로 돌아가겠습니다. app router 의 파일 컨벤션에 따라서 app 디렉토리에 다음과 같은 폴더 구조를 생성했습니다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706752964242/117ac8a4-2b83-4b20-836b-6d5d3d29450e.png" alt class="image--center mx-auto" /></p>
<p>폴더구조를 잘 살펴봅시다. 같은 레이아웃을 공유해야한다는 것, 그것에 따라 폴더 구조를 정렬해야한다는 것을 이해해야 합니다.</p>
<p><code>@modal</code> 은 props 의 <strong>modal</strong>로 전달이 되었고, 그 외 요소들은 <strong>children</strong>로 전달이 되었습니다. 그 후 매칭되는 라우트에 네비게이션할 때 해당 라우팅의 컴포넌트가 렌더링됩니다.</p>
<p>slot 을 따라 레이아웃을 다음과 같이 구성했습니다.</p>
<pre><code class="lang-tsx">import { ReactNode } from "react"

export default function Layout({children, modal}:{children: ReactNode, modal:ReactNode}){
    return(
        &lt;div&gt;
            {children}
            {modal}
        &lt;/div&gt;
    )
}
</code></pre>
<p>이제 <code>baseURL/i/flow/login</code> 으로 접속 시 children 부분에는 <code>(beforeLogin)/i/flow/login</code> 이, modal 부분에는 <code>(beforeLogin)/@modal/i/flow/login</code> 이 매핑됩니다.</p>
<p>그렇다면 <code>baseURL/</code> 로 접속한다면?</p>
<p>children 부분에는 <code>page.tsx</code> 가 매핑되고, modal 부분에는 <code>default.tsx</code> 가 매핑됩니다. (<code>@modal/page.tsx</code> 부재를 <code>default.tsx</code> 가 대체)</p>
<blockquote>
<p><code>default.tsx</code> → parallel route 의 default page. parallel route 가 이용되지 않을 때 이 페이지를 디폴트값으로 띄워줍니다. 즉, 라우팅이 unmatched 할 때 이 페이지를 렌더링합니다. (따라서 위와 같은 상황에 default.tsx 페이지가 없다면 에러가 발생하겠지요.)</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706753553302/7855b07f-56cd-49df-bde6-0ce509ba05f2.png" alt class="image--center mx-auto" /></p>
<p>하지만 우리가 원하는 것은 <code>i/flow/login/page.tsx</code> 가 동시에 렌더링 되는 것이 아닙니다.. 우리가 원하는 것은 main page(라우팅 주소가 다름)가 뒷배경으로 렌더링 되는 것! 페이지를 동시에 띄우는 것까지 했으니, 주소가 다른 페이지를 렌더링하는 작업을 해보도록 하겠습니다.</p>
<h2 id="heading-intercepting-routes">Intercepting routes</h2>
<p>intercepting routes 는 현재 페이지 컨텍스트를 유지한 채로 새로운 라우트를 렌더링합니다. 따라서 주소가 다른 페이지들을 동시에 렌더링 하고자 할 때 유용합니다.</p>
<p>원하는 화면을 구성하기 위해 layout props 로 <strong>children</strong> 을 <code>page.tsx</code> 로, <strong>modal</strong> 을 <code>@modal/i/flow/login/page.tsx</code> 로 전달해봅시다.</p>
<p>interception routes 는 <code>(..)</code> 와 같은 컨벤션으로 정의됩니다.</p>
<ul>
<li><p><code>(.)</code> 동일한 라우팅 레벨 세그먼트에 매칭</p>
</li>
<li><p><code>(..)</code> 부모 라우팅 레벨 세그먼트에 매칭</p>
</li>
<li><p><code>(..)(..)</code> 2단계 윗 레벨</p>
</li>
<li><p><code>(…)</code> app 디렉토리 루트 요소에 매칭</p>
</li>
</ul>
<blockquote>
<p>💡 주의할 것은, 기준이 브라우저 주소라는 것입니다! (라우트 세그먼트 기준)</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706754307341/95430172-8acb-45d7-9427-4a35c6a9cbad.png" alt class="image--center mx-auto" /></p>
<p>구조를 위와 같이 바꿔 intercepting routes 를 사용해보았습니다.</p>
<p>다시 구조를 살펴보면, <code>@modal</code> 은 라우팅에 영향을 미치지 않는 slot이지, 라우트 세그먼트가 아닙니다. 그렇기 때문에 <code>@modal</code> 요소 컨텍스트는 유지한 채 (브라우저 주소 상)같은 레벨의 i 요소의 라우팅을 인터셉트할 수 있습니다. ((..)i 로 폴더 이름을 바꾸는 실수를 저지르면 안됩니다.)</p>
<p>이제 <code>layout.tsx</code> 에 children prop 에 <code>page.tsx</code> 가 렌더링되고, modal prop에 <code>i/flow/login</code> 으로 인터셉트한 기존 모달 부분이 렌더링되어 원하던 결과를 얻을 수 있게 됩니다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706754478047/890c3550-2f6d-4a35-84fc-df8fd5ef3f16.png" alt class="image--center mx-auto" /></p>
<p>(<a target="_blank" href="http://localhost:3000/i/flow/login">localhost:3000/i/flow/login</a> 화면)</p>
<p>이렇게 모달을 만들면 좋은 점은 다음과 같습니다. (공식문서 참조)</p>
<ul>
<li><p>뒤로가기/앞으로 가기로 모달을 열고 닫을 수 있음</p>
</li>
<li><p>URL 통해 모달 내용 공유 가능</p>
</li>
<li><p>페이지 새로고침 시 모달이 닫히는 대신 컨텍스트가 유지됨</p>
</li>
</ul>
<p>즉, 페이지를 모달로 띄우기가 가능해 모달 자체가 url 로 관리될 수 있다는 것입니다. createPortal 보다 방법이 직관적이고 간단해서 좋은 것 같습니다.</p>
<hr />
<h3 id="heading-67ki7jm4li4">번외..</h3>
<p>왜 인터셉트 후, <code>page.tsx</code>가 children 으로, <code>@modal/i/flow/login/page.tsx</code> 가 modal 로 렌더링되는가?</p>
<p>저는 위 사항이 이해가 가지 않았습니다. parallel routes 는 매칭되는 라우팅에 해당하는 여러 페이지를 동시에 보여준다고 이해했는데, 둘은 매칭되는 라우팅 주소가 다른데 어떻게 동시에 렌더링이 되는 것일까요.</p>
<p>공식문서에 의하면 기본적으로 슬롯에서 렌더링되는 컨텐츠는 현재 url 과 매치됩니다. 그런데 매치되지 않는 슬롯이 있는 경우는 다음과 같이 페이지를 렌더링한다고 합니다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706754827572/63495109-545d-4c0d-8db2-abab615c314c.png" alt class="image--center mx-auto" /></p>
<p>위는 라우팅이 통일되지 않았습니다. 여기서 /settings 로 이동하는 상황을 가정해보면, @analytics 는 어떤 페이지를 보내줘야할까요?</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td></td><td><code>@analytics/default.js</code> 가 존재하는 경우</td><td><code>@analytics/default.js</code> 가 존재하지 않는 경우</td></tr>
</thead>
<tbody>
<tr>
<td>소프트 네비게이션</td><td><code>@team/settings/page.js</code> 그리고 <code>@analytics/page.js</code></td><td><code>@team/settings/page.js</code> 그리고 <code>@analytics/page.js</code></td></tr>
<tr>
<td>하드 네비게이션</td><td><code>@team/settings/page.js</code> 그리고 <code>@analytics/default.js</code></td><td>404</td></tr>
</tbody>
</table>
</div><p><strong>Softe navigation:</strong> 변화된 segments들의 캐시 사용</p>
<p><strong>Hard Navigation:</strong> 세그먼트 리렌더링, 데이터 refetch</p>
<p>(출처: <a target="_blank" href="https://velog.io/@glassfrog8256/번역-Next.js13-App-Router-Routing-Parallel-Routes">https://velog.io/@glassfrog8256/번역-Next.js13-App-Router-Routing-Parallel-Routes</a><a target="_blank" href="https://velog.io/@glassfrog8256/%EB%B2%88%EC%97%AD-Next.js13-App-Router-Routing-Parallel-Routes">)</a></p>
<p>기본적으로 브라우저는 하드 네비게이션으로 동작하지만, 넥스트 앱라우터는 소프트 네비게이션을 제공합니다. 위에서 제가 느꼈던 의문은 기존 하드 네비게이션 방식이 아닌 소프트 네비게이션 방식으로 페이지 이동이 이루어졌기 때문인 것 같습니다. 아직 정확히 넥스트에서 어떤 기준으로 soft navigation 을 사용하고, hard navigation 을 사용하는지는 모르겠습니다. 추후에 관련 내용을 추가해보고자 합니다.</p>
<p>오류 / 번외 관련 내용은 댓글로 알려주시면 감사하겠습니다.</p>
<p><a target="_blank" href="https://velog.io/@glassfrog8256/%EB%B2%88%EC%97%AD-Next.js13-App-Router-Routing-Parallel-Routes">참고 자료:</a></p>
<p><a target="_blank" href="https://rocketengine.tistory.com/entry/NextJS-13-Routing-Intercepting-Routes라우트-가로채기">https://rocketengine.tistory.com/entry/NextJS-13-Routing-Intercepting-Routes라우트-가로채기</a></p>
<p><a target="_blank" href="https://nextjs.org/docs/app/building-your-application/routing/parallel-routes">https://nextjs.org/docs/app/building-your-application/routing/parallel-routes</a></p>
<p><a target="_blank" href="https://nextjs.org/docs/app/building-your-application/routing/intercepting-routes">https://nextjs.org/docs/app/building-your-application/routing/intercepting-routes</a></p>
]]></content:encoded></item><item><title><![CDATA[C/C++ 이진 트리(binary tree) 개요 및 구현(1)]]></title><description><![CDATA[개요
트리는 노드들이 나무 가지처럼 연결된 비선형 계층적 자료구조이다. 하위 트리가 존재하고, 그 노드에 또 하위 트리가 존재하는 자료구조 이다. 트리의 맨 위에 있는 루트 노드가 존재한다. 우리가 알아볼 트리는 이진 트리이다. 이진 트리는 자식 노드(부모로부터 아래로 이어진 노드)가 2개 이하인 구조를 말한다. 트리의 사용 사례로는 다음과 같다

계층 적 데이터 저장(파일,폴더)

효율적인 검색 속도

힙

데이터 베이스의 인덱싱


트리에 ...]]></description><link>https://blog.skku-comit.dev/cc-binary-tree-1</link><guid isPermaLink="true">https://blog.skku-comit.dev/cc-binary-tree-1</guid><category><![CDATA[C++]]></category><category><![CDATA[C]]></category><category><![CDATA[data structures]]></category><category><![CDATA[BinaryTrees]]></category><dc:creator><![CDATA[Gon Tae]]></dc:creator><pubDate>Wed, 31 Jan 2024 15:28:36 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-6rcc7jqu">개요</h3>
<p>트리는 노드들이 나무 가지처럼 연결된 비선형 계층적 자료구조이다. 하위 트리가 존재하고, 그 노드에 또 하위 트리가 존재하는 자료구조 이다. 트리의 맨 위에 있는 루트 노드가 존재한다. 우리가 알아볼 트리는 이진 트리이다. 이진 트리는 자식 노드(부모로부터 아래로 이어진 노드)가 2개 이하인 구조를 말한다. 트리의 사용 사례로는 다음과 같다</p>
<ul>
<li><p>계층 적 데이터 저장(파일,폴더)</p>
</li>
<li><p>효율적인 검색 속도</p>
</li>
<li><p>힙</p>
</li>
<li><p>데이터 베이스의 인덱싱</p>
</li>
</ul>
<h3 id="heading-7yq466as7jeqioq0go2vncdsmqnslrq">트리에 관한 용어</h3>
<ul>
<li><p>degree - 각 노드가 가진 자식 노드(가지)의 수</p>
</li>
<li><p>root node(루트 노드) - 맨 위의 노드, 부모가 없다</p>
</li>
<li><p>leaf node(단말 노드) - 자식이 없는 노드</p>
</li>
<li><p>internal node(내부 노드) - leaf node가 아닌 노드</p>
</li>
<li><p>edge - 노드를 연결하는 선</p>
</li>
<li><p>depth - 루트에서 어떤 노드에 도달하기 위해 거쳐야 하는 edge의 수</p>
</li>
<li><p>level - 같은 깊이를 가진 노드의 집합, 맨 위부터 레벨 1, 2, 3 순으로 간다.</p>
</li>
<li><p>height - 루트 노드에서 가장 깊숙이 있는 노드의 깊이</p>
</li>
</ul>
<h3 id="heading-7j207keeio2kuoumroydmcdsooxrpzg">이진 트리의 종류</h3>
<ul>
<li><p>정 이진트리(Strict Binary Tree)</p>
<p>  오직 0 또는 2의 degree의 노드만 존재한다. 즉 자식이 무조건 두 개 또는 한 개이다.</p>
</li>
<li><p>포화 이진트리(Perfect Binary Tree)</p>
<p>  모든 노드가 2개의 자식을 가지고 leaf 노드가 모두 같은 level이다.</p>
</li>
<li><p>완전 이진트리(Complete Binary Tree)</p>
<p>  마지막 level을 제외하고 모든 노드가 채워져 있어야 한다. 마지막 레벨의 노드는 다 채워져 있을 수도 있고 아닐 수도 있다. 또한 노드는 왼쪽에서 오른 쪽으로 빈 공간 없이 채워져 있어야 한다.</p>
</li>
</ul>
<h3 id="heading-7j207keeio2kuoumroydmcdtirnsp5u">이진 트리의 특징</h3>
<ul>
<li><p>height 당 최대/최소 노드</p>
<ul>
<li><p>최대 - n = 2^(h+1) -1</p>
</li>
<li><p>최소 - n = h + 1</p>
</li>
</ul>
</li>
<li><p>strict m-array(최대 dgree가 2인) tree 에서 height 당 최대/최소 노드</p>
<ul>
<li><p>최대 - (m^(h+1) - 1) / (m - 1)</p>
</li>
<li><p>최소 - m*h + 1</p>
</li>
<li><p>이진트리는 2-array tree다.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-7j207keeio2kuoumroydmcdtkzztmiq">이진 트리의 표현</h3>
<ul>
<li><p>array</p>
<p>  하나의 배열에 노드를 순서대로 입력하는 것이다. 일반적으로 맨 위 level의 노드부터 해서 왼쪽에서 오른쪽 순서대로 입력한다.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706706751846/1f28358e-6cc2-42c1-89df-9a7d435b9bae.png" alt class="image--center mx-auto" /></p>
<p>  각 노드의 인덱스에 대한 규칙은 다음과 같다.</p>
</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td>루트 노드</td><td>1</td></tr>
</thead>
<tbody>
<tr>
<td>노드 i의 부모</td><td>i / 2</td></tr>
<tr>
<td>노드 i의 왼쪽 자식</td><td>i * 2</td></tr>
<tr>
<td>노드 i의 오른쪽 자식</td><td>i * 2 + 1</td></tr>
</tbody>
</table>
</div><ul>
<li><p>연결 리스트 활용</p>
<p>  각 노드를 연결 리스트의 노드로 보는 것이다. 각 노드는 데이터와 왼쪽 노드를 향한 포인터, 오른쪽 노드를 향한 포인터로 이루어져 있다.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706707016014/fa987e04-4d78-4123-a95a-d916e6b168e8.jpeg" alt class="image--center mx-auto" /></p>
<p>  훨씬 직관적이기에 연결 리스트 형태의 이진 트리 활용을 선호한다.</p>
</li>
</ul>
<h3 id="heading-traversing">이진 트리 순회(traversing) 방식</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706706751846/1f28358e-6cc2-42c1-89df-9a7d435b9bae.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p>preorder</p>
<p>  A-&gt;B-&gt;D-&gt;H-&gt;I-&gt;E-&gt;J-&gt;K-&gt;C-&gt;F-&gt;G</p>
<p>  부모 노드, 왼쪽 노드, 오른쪽 노드 순으로 순회함</p>
</li>
<li><p>postorder</p>
<p>  H-&gt;I-&gt;D-&gt;J-&gt;K-&gt;E-&gt;B-&gt;F-&gt;G-&gt;C-&gt;A</p>
<p>  왼쪽 노드, 오른쪽 노드, 부모 노드 순으로 순회함</p>
</li>
<li><p>Inorder</p>
<p>  H-&gt;D-&gt;I-&gt;B-&gt;J-&gt;E-&gt;K-&gt;A-&gt;F-&gt;C-&gt;G</p>
<p>  왼쪽 노드, 부모 노드, 오른쪽 노드 순으로 순회함</p>
</li>
</ul>
<h3 id="heading-treecreate">TreeCreate함수-트리 생성</h3>
<p>트리 생성은 큐와 연결 리스트를 통해 구현한다. 원리는 다음과 같다. 노드를 생성하고, 큐에 넣는다. 큐의 선입선출 방식을 이용해 큐에서 노드를 뽑은 다음, 해당 노드의 왼쪽 자식 노드, 오른쪽 자식 노드를 생성해 큐에 집어 넣는다. 그 후, 다시 큐에서 노드를 뽑아 자식 노드를 만들어 큐에 넣는 방식을 반복한다. 코드를 통해 알아보자.</p>
<pre><code class="lang-cpp">Node *root = <span class="hljs-literal">NULL</span>;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Treecreate</span><span class="hljs-params">()</span>
</span>{
    Node *p, *t;
    Queue q;
    create(&amp;q, <span class="hljs-number">100</span>);

    <span class="hljs-keyword">int</span> x;
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Enter root value"</span>);
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%d"</span>, &amp;x);
    root = <span class="hljs-keyword">new</span> Node;
    root-&gt;data = x;
    root-&gt;lchild = root-&gt;rchild = <span class="hljs-literal">NULL</span>;
    enqueue(&amp;q, root);

    <span class="hljs-keyword">while</span>(!isEmpty(q))
    {
        p = dequeue(&amp;q);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Enter left child: %d"</span>, p-&gt;data);
        <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%d"</span>, &amp;x);
        <span class="hljs-keyword">if</span> (x != <span class="hljs-number">-1</span>)
        {
            t = <span class="hljs-keyword">new</span> Node;
            t-&gt;data = x;
            t-&gt;lchild = t-&gt;rchild = <span class="hljs-literal">NULL</span>;
            p-&gt;lchild = t;
            enqueue(&amp;q, t);
        }

        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Enter right child: %d"</span>, p-&gt;data);
        <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%d"</span>, &amp;x);
        <span class="hljs-keyword">if</span> (x != <span class="hljs-number">-1</span>)
        {
            t = <span class="hljs-keyword">new</span> Node;
            t-&gt;data = x;
            t-&gt;lchild = t-&gt;rchild = <span class="hljs-literal">NULL</span>;
            p-&gt;rchild = t;
            enqueue(&amp;q, t);
        }
    }
}
</code></pre>
<p>큐에 노드가 존재할 때, 노드를 큐에서 뽑아 자식 노드를 만들고, 자식 노드를 큐에 넣는 방식이다.</p>
<h3 id="heading-preorder-preorder">preorder함수-preorder 순회를 재귀 함수로</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">preorder</span><span class="hljs-params">(Node *p)</span>
</span>{
    <span class="hljs-keyword">if</span> (p)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d"</span>, p-&gt;data);
        preorder(p-&gt;lchild);
        preorder(p-&gt;rchild);
    }
}
</code></pre>
<p>부모 노드의 데이터를 출력하고, 왼쪽 자식 노드로 옮기고, 오른쪽 자식 노드로 옮기는 방식이다. 트리 자체가 하위 노드에 또 하위 노드가 있는 재귀 형태이기 때문에 재귀 함수로의 구현이 쉽다.</p>
<h3 id="heading-postorder-postorder">postorder함수-postorder 순회를 재귀 함수로</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">postorder</span><span class="hljs-params">(Node *p)</span>
</span>{
    <span class="hljs-keyword">if</span> (p)
    {
        postorder(p-&gt;lchild);
        postorder(p-&gt;rchild);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d "</span>, p-&gt;data);
    }
}
</code></pre>
<h3 id="heading-inorder-inorder">Inorder함수-Inorder 순회를 재귀 함수로</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">inorder</span><span class="hljs-params">(Node *p)</span>
</span>{
    <span class="hljs-keyword">if</span> (p)
    {
        inorder(p-&gt;lchild);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d "</span>, p-&gt;data);
        inorder(p-&gt;rchild);
    }
}
</code></pre>
<h3 id="heading-66ei66y066as">마무리</h3>
<p>트리의 기초와 트리 생성, 순회 코드를 알아보았다. 비선형 구조인 만큼 직관적인 트리 생성을 이해하기 힘들다. 하지만, 연결 리스트와 재귀를 활용해서 쉽게 구현할 수 있다. 다음 글에서는 위의 순회 코드를 루프를 통해 구현해보자. 루프는 이보다 이해가 어려우니 주의를 기울이자.</p>
]]></content:encoded></item><item><title><![CDATA[[React] Server component (RSC)]]></title><description><![CDATA[React.js 18 에 도입된 리액트 서버 컴포넌트는 서버에서 동작하는 리액트 컴포넌트를 의미합니다. Next가 권장하는 라우팅 방식인 app router의 기반이 되는 컴포넌트이기 때문에 app router 를 이해하기 위해서는 server component 에 대한 이해가 필요합니다.
server component

리액트는 클라이언트단만을 컴포넌트화하는 대신, server component라는 개념을 통해 서버 영역을 컴포넌트화합니다.
...]]></description><link>https://blog.skku-comit.dev/react-server-component-rsc</link><guid isPermaLink="true">https://blog.skku-comit.dev/react-server-component-rsc</guid><category><![CDATA[React]]></category><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[박지은]]></dc:creator><pubDate>Mon, 29 Jan 2024 11:47:55 GMT</pubDate><content:encoded><![CDATA[<p>React.js 18 에 도입된 리액트 서버 컴포넌트는 서버에서 동작하는 리액트 컴포넌트를 의미합니다. Next가 권장하는 라우팅 방식인 app router의 기반이 되는 컴포넌트이기 때문에 app router 를 이해하기 위해서는 server component 에 대한 이해가 필요합니다.</p>
<h3 id="heading-server-component">server component</h3>
<blockquote>
<p>리액트는 클라이언트단만을 컴포넌트화하는 대신, server component라는 개념을 통해 <mark>서버 영역을 컴포넌트화</mark>합니다.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706529083470/87f56227-c038-47b5-b45a-4de2dd317069.png" alt class="image--center mx-auto" /></p>
<p>(출처: <a target="_blank" href="https://github.com/reactwg/server-components/discussions/4">https://github.com/reactwg/server-components/discussions/4</a>)</p>
<p>서버 컴포넌트의 등장으로 server component 에서 데이터를 직접 가져와 client component 로 데이터를 prop 하는 형식으로 <strong>server 영역과 client 영역이 구분</strong>되기 시작했습니다.</p>
<p>서버컴포넌트는 컴포넌트 렌더링을 클라이언트가 아닌 <strong>서버에서 수행</strong>합니다. 서버에서 동작하기 때문에 서버 사이드 데이터에 직접 접근할 수 있으며, <strong>유저와의 상호작용을 추가할 수 없기</strong> 때문에 클라이언트 컴포넌트와 서버 컴포넌트를 적절히 사용하는 것이 중요합니다.</p>
<p>서버컴포넌트는 data fetching 측면에서 기존 클라이언트 컴포넌트에서 나타나는 문제점을 해결할 수 있습니다.</p>
<hr />
<ol>
<li><p><strong>client-server waterfall</strong> 문제 해결 : 아래처럼 컴포넌트마다 다른 데이터 fetch 를 하려고 한다는 상황을 생각해봅시다.</p>
<pre><code class="lang-javascript"> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HomePage</span>(<span class="hljs-params"></span>)</span>{
     <span class="hljs-keyword">return</span> (
         <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ParentComponent</span>&gt;</span>
             <span class="hljs-tag">&lt;<span class="hljs-name">ChildrenComponentOne</span>/&gt;</span>
             <span class="hljs-tag">&lt;<span class="hljs-name">ChildrenComponentTwo</span>/&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">ParentComponent</span>/&gt;</span>
     )
 }</span>
</code></pre>
<p> 이 경우 부모 컴포넌트는 api 호출이 완료될때까지 렌더링되지 않고, 자식 컴포넌트들의 렌더링과 api 호출이 수행되지 않습니다.(waterfall 문제 -&gt; 성능 저하)</p>
<p> 중첩되는 컴포넌트가 많아질수록 client-server api 호출은 많아질 것이고, 호출에 대한 응답이 완료될 때 까지 기다리는 waterfall 문제는 더욱 심각해집니다.</p>
<pre><code class="lang-js"> <span class="hljs-comment">//server component</span>
 <span class="hljs-keyword">import</span> dbConnect <span class="hljs-keyword">from</span> <span class="hljs-string">'db'</span>; 

 <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyPost</span>(<span class="hljs-params">props</span>) </span>{
   <span class="hljs-keyword">const</span> {userId} = props;
   <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> dbConnect.users.get(userId);

   <span class="hljs-keyword">return</span> (
     <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{userData.post}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
   );
 }
</code></pre>
<p> 다음은 server component 에서 데이터를 직접 fetch 하는 예시 코드입니다. server component 를 통해서 <strong>server-to-server</strong>로 데이터를 요청한다면 <strong>데이터 요청의 latency 를 줄일 수 있고</strong>, api 호출 없이 직접 DB 에 접근해 데이터를 가져올 수 있기 때문에 <strong>중첩되는 client-server api 호출을 제거</strong>할 수 있습니다.</p>
</li>
<li><p><strong>js 번들 사이즈 줄이기</strong></p>
<p> 서버 컴포넌트는 렌더링을 서버에서 수행하기 때문에 클라이언트단의 번들 사이즈를 감소시킬 수 있습니다. (서버컴포넌트는 서버에서 수행된 결과를 전달하기 때문에 번들을 필요로하지 않음)</p>
</li>
</ol>
<hr />
<h3 id="heading-server-component-vs-server-side-rendering">server component VS server side rendering</h3>
<p><strong>server component</strong> 와 <strong>server side rendering</strong> 개념은 혼동하기 쉬운 개념입니다. 하지만 둘은 다른 것..! (둘은 보완 개념에 가깝습니다.)</p>
<ul>
<li><p>(Next.js 에서의 서버사이드렌더링을 기준으로) 서버사이드렌더링은 서버에서 미리 정적인 html 을 생성해주어 초기 랜딩 시 사용할 수 있도록 합니다.</p>
</li>
<li><p>하지만 서버 컴포넌트는 서버 영역을 컴포넌트화한것으로, 클라이언트에서는 보이지 않도록, 서버에서만 실행되도록 하는 컴포넌트입니다. (ssr 은 코드는 js 번들에 포함되어 클라이언트로 전송됨)</p>
</li>
</ul>
<h3 id="heading-client-component-server-component">client component 와 server component 적절히 사용해보기</h3>
<blockquote>
<p>server component 내에서 client component import -&gt; 가능</p>
<p>client component 내에서 server component import -&gt; 불가</p>
</blockquote>
<p>위에서 언급한 것 처럼 서버 컴포넌트에서는 유저 인터렉션 관련 작업을 수행할 수 없습니다. 따라서 서버 컴포넌트 데이터를 그 안에서 import 한 클라이언트 컴포넌트로 props 로 전달하는 방식을 사용해야 합니다. (Next.js 는 client component 를 leaf node 로 둘 것을 권장)</p>
<p>이런 패턴으로 배치한 컴포넌트는</p>
<blockquote>
<p>1. 모든 서버 컴포넌트 렌더링</p>
<p>2. 클라이언트단에서 리액트가 클라이언트 컴포넌트와 1의 결과(slot) 를 렌더링, 즉 합침</p>
</blockquote>
<p>의 과정으로 동작합니다.</p>
<p>위에서 user 의 post 를 보여주는 예시 코드를 보여줬는데, 이와 같이 유저 인터렉션이 필수적이지 않은 상황에서는 server component 를 활용하는 것이 적절하고, 글을 작성하거나 업데이트하는 등의 상호작용이 필요한 경우에는 useState 등의 hook을 사용할 수 있는 client component 를 활용해야 합니다.</p>
<p>참고</p>
<p><a target="_blank" href="https://tech.kakaopay.com/post/react-server-components/">https://tech.kakaopay.com/post/react-server-components/</a></p>
<p><a target="_blank" href="https://velog.io/@asdf99245/Next.js-app-router-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%EC%A0%95%EB%A6%AC">https://velog.io/@asdf99245/Next.js-app-router-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%EC%A0%95%EB%A6%AC</a></p>
]]></content:encoded></item><item><title><![CDATA[[Flutter] Flutter Project Structure : Feature-first vs Layer-first]]></title><description><![CDATA[Flutter에서 단일 페이지 앱을 만들 때에는 하나의 폴더에 모든 파일들을 넣어도 된다. 하지만 다양한 페이지와 데이터 모델들을 추가하기 시작할 때, 파일들을 어떻게 일관적인 방식으로 정리할 수 있을까?
대표적인 방식으로 feature-first 구조와 layer-first 구조가 있다.
Layer-first (feature inside layers)
‣ lib 
    ‣ src 
        ‣ presentation 
         ...]]></description><link>https://blog.skku-comit.dev/flutter-flutter-project-structure-feature-first-vs-layer-first</link><guid isPermaLink="true">https://blog.skku-comit.dev/flutter-flutter-project-structure-feature-first-vs-layer-first</guid><dc:creator><![CDATA[김성중]]></dc:creator><pubDate>Sun, 28 Jan 2024 12:50:41 GMT</pubDate><content:encoded><![CDATA[<p>Flutter에서 단일 페이지 앱을 만들 때에는 하나의 폴더에 모든 파일들을 넣어도 된다. 하지만 다양한 페이지와 데이터 모델들을 추가하기 시작할 때, 파일들을 어떻게 <strong>일관적인</strong> 방식으로 정리할 수 있을까?</p>
<p>대표적인 방식으로 <strong>feature-first</strong> 구조와 <strong>layer-first</strong> 구조가 있다.</p>
<h2 id="heading-layer-first-feature-inside-layers">Layer-first (feature inside layers)</h2>
<pre><code class="lang-bash">‣ lib 
    ‣ src 
        ‣ presentation 
            ‣ feature1 
            ‣ feature2 
        ‣ application 
            ‣ feature1 
            ‣ feature2 
        ‣ domain 
            ‣ feature1 
            ‣ feature2 
        ‣ data 
            ‣ feature1 
            ‣ feature2
</code></pre>
<h3 id="heading-feature-inside-layers">feature inside layers</h3>
<blockquote>
<p>각각의 layer마다 Dart 파일을 넣는 대신, 폴더를 생성한다. 서로 관련 있는 Dart 파일들을 각각의 feature 폴더에 추가한다. (widgets, controllers는 presentation 폴더 안에, models는 domain 안에)</p>
</blockquote>
<p>만약 feature3를 추가하고 싶다면, 각각의 layer마다 feature3 폴더를 넣는다:</p>
<pre><code class="lang-bash">‣ lib 
    ‣ src 
        ‣ presentation
            ‣ feature1 
            ‣ feature2 
            ‣ feature3 &lt;--
        ‣ application 
            ‣ feature1 
            ‣ feature2 
            ‣ feature3 &lt;--
        ‣ domain 
            ‣ feature1 
            ‣ feature2 
            ‣ feature3 &lt;--
        ‣ data 
            ‣ feature1 
            ‣ feature2 
            ‣ feature3 &lt;--
</code></pre>
<h3 id="heading-layer-first">Layer-first 구조의 단점</h3>
<blockquote>
<p>하나의 feature에 대해 다른 layer에 속한 파일들은 서로 멀리 떨어져 있다. 따라서 프로젝트의 여러 곳을 계속해서 이동해야 하기 때문에, 각각의 feature에 대해 작업하기 번거롭다. 또한 특정 feature을 삭제하려고 할 때, 일부 파일을 까먹을 수 있다. 각 파일들이 layer에 의해 정렬되어 있기 때문이다. 이러한 이유 때문에, 대규모의 앱을 만들 때에는 이어서 설명할 feature-first 구조를 선택하는 것이 좋다.</p>
</blockquote>
<h2 id="heading-feature-first-layers-inside-features">Feature-first (layers inside features)</h2>
<p>feature-first 구조에서는 모든 새로운 feature마다 새로운 폴더를 생성한다. 그리고 해당 폴더 안에 layer을 집어넣는다.</p>
<pre><code class="lang-bash">‣ lib 
    ‣ src 
        ‣ features 
            ‣ feature1 
                ‣ presentation 
                ‣ application 
                ‣ domain 
                ‣ data 
            ‣ feature2 
                ‣ presentation 
                ‣ application 
                ‣ domain 
                ‣ data
</code></pre>
<p>이러한 구조가 더 논리적이다. 특정 feature에 해당하는 파일들이 layer로 그룹화되어 쉽게 찾을 수 있기 때문이다.</p>
<h3 id="heading-layer-first-1"><strong>layer-first 구조보다 좋은 점</strong></h3>
<ul>
<li><p>새로운 feature을 추가하거나 수정하고 싶을 때, 하나의 폴더에서만 작업을 하면 된다.</p>
</li>
<li><p>특정 feature을 삭제하려면, 하나의 폴더만 삭제하면 된다.</p>
</li>
</ul>
<h3 id="heading-common-mistakes">Common mistakes</h3>
<p><strong>Feature-first 는 UI에 관한 구조가 아니다!!</strong></p>
<p>feature을 단순히 앱 스크린 상의 페이지로 생각할 수도 있다.</p>
<p>예를 들면:</p>
<pre><code class="lang-bash">‣ lib 
    ‣ src 
        ‣ features 
            ‣ account 
            ‣ admin 
            ‣ checkout 
            ‣ leave_review_page 
            ‣ orders_list 
            ‣ product_page 
            ‣ products_list 
            ‣ shopping_cart 
            ‣ sign_in
</code></pre>
<p>위 코드에서 각각의 feature은 앱의 실제 화면에 따라 분류되어 있다. 이처럼 UI에 의한 feature-first 구조로 접근하면 안 된다.</p>
<h3 id="heading-feature">Feature란 무엇인가?</h3>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">유저가 '보는' 게 아닌 '하는' 것 (장바구니 담기, 주문 조회하기, 리뷰 남기기 등)</div>
</div>

<h3 id="heading-how-to-do-feature-first-the-right-way">How to do feature-first, the right way</h3>
<ul>
<li><p>domain layer부터 시작해서 model classes와 이 classes를 관리하기 위한 business logic을 찾아내라</p>
</li>
<li><p>각각의 모델에 대한 폴더를 생성한다</p>
</li>
<li><p>폴더마다 'presentation','application','domain','data' 같은 sub 폴더를 생성한다.</p>
</li>
<li><p>해당 sub 폴더에 필요한 파일을 넣는다</p>
</li>
</ul>
<h3 id="heading-6rkw66gg">결론</h3>
<blockquote>
<p>feature-first 구조가 layer-first에 비해 많은 장점을 가진다. domain 주도 설계를 적용한다면, 앱 내의 서로 다른 layers와 components 간에 명확한 경계가 형성될 것이다. 이로써 후에 의존성 문제를 효율적으로 관리할 수 있게 된다.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[React + Express 개발 환경 세팅]]></title><description><![CDATA[초보 개발자에게 있어 개발 프로젝트에서 넘어야 하는 첫 번째 난관은 개발 환경 세팅입니다. 개발 환경 세팅으로 몇 분, 혹은 몇 시간을 삽질로 태우게 된다면 '시작이 반이다' 라는 말에 격하게 공감하게 될 것입니다. 저도 공감하고 싶지는 않았습니다.
오늘은 React, Typescript, Tailwind를 사용하는 프론트엔드 개발 환경 설정과 Express로 서버를 만들어 client에서 data fetching을 할 수 있도록 하는 과정에 ...]]></description><link>https://blog.skku-comit.dev/react-express</link><guid isPermaLink="true">https://blog.skku-comit.dev/react-express</guid><category><![CDATA[Express]]></category><category><![CDATA[React]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[setup]]></category><dc:creator><![CDATA[권서진]]></dc:creator><pubDate>Fri, 26 Jan 2024 15:25:11 GMT</pubDate><content:encoded><![CDATA[<p>초보 개발자에게 있어 개발 프로젝트에서 넘어야 하는 첫 번째 난관은 개발 환경 세팅입니다. 개발 환경 세팅으로 몇 분, 혹은 몇 시간을 삽질로 태우게 된다면 '시작이 반이다' 라는 말에 격하게 공감하게 될 것입니다. 저도 공감하고 싶지는 않았습니다.</p>
<p>오늘은 React, Typescript, Tailwind를 사용하는 프론트엔드 개발 환경 설정과 Express로 서버를 만들어 client에서 data fetching을 할 수 있도록 하는 과정에 대해 설명하고자 합니다.</p>
<p>우선 프로젝트의 루트 폴더로 이동해서 server 디렉토리를 만들고 server로 이동해주세요</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706275767790/f42d4af9-11fc-467e-bc01-4f5825686044.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-express">Express 서버 만들기</h2>
<pre><code class="lang-bash">npm init -y
</code></pre>
<p>위의 코드로 package.json 파일을 만듭니다. 뒤에 붙는 -y는 모든 값을 디폴트로 하겠다는 의미입니다.</p>
<pre><code class="lang-bash">npm i express
npm i nodemon
</code></pre>
<p>express와 nodemon을 설치해 주세요.</p>
<pre><code class="lang-bash">touch server.js
</code></pre>
<p>server.js 파일을 생성하고 아래와 같이 작성해주세요.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server.js</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> port = <span class="hljs-number">8080</span>;

app.listen(port, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Server started!"</span>);
});
</code></pre>
<p>서버 실행을 nodemon으로 하기 위해 package.json을 수정합니다.</p>
<pre><code class="lang-json">  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"nodemon server"</span>
  },
</code></pre>
<p>이제 npm start를 입력하면 서버를 nodemon으로 실행할 수 있습니다.</p>
<p>하지만 실행해도 Cannot GET / 밖에 안 보일겁니다.</p>
<h2 id="heading-frontend">Frontend 환경 세팅</h2>
<p>서버는 그대로 놔두고 터미널을 하나 더 열어봅시다.</p>
<p>vite를 이용해 개발 환경을 구성해봅시다<br />Project name은 client로 해주고<br />framework랑 variant는 자유롭게 설정해주시면 됩니다.<br />저는 React, TS + SWC를 선택했습니다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706276910801/90037801-2e07-4cf7-8d05-8162ed7186ee.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> client
npm i
npm run dev
</code></pre>
<p>위의 과정을 거쳐 npm run dev까지 입력하면 vite에서 기본 제공하는 까리한 웹페이지를 볼 수 있습니다. 하지만 저희에겐 필요없기 때문에 파일들을 정리해보겠습니다.</p>
<p>우선 public, assets 폴더를 통째로 지워줍니다.<br />App.css, App.tsx, index.css 3개의 파일은 내용을 전부 지워줍니다.</p>
<p>3개의 파일을 채우기 전에 tailwindcss 부터 설정해보겠습니다.</p>
<pre><code class="lang-bash">npm i tailwindcss postcss autoprefixer
npx tailwindcss init -p
</code></pre>
<p>tailwindcss, postcss, autoprefixer 를 설치하고 tailwind.config.js 와 postcss.config.js 파일을 생성하는 코드입니다. 왜인지는 모르지만 postcss와 autoprefixer를 설치 안하면 묘하게 tailwind가 적용되지 않으니 일단 시키는대로 전부 다 설치합니다.</p>
<p>순서대로 tailwind.config.js, App.css, App.tsx, index.css 파일을 아래와 같이 작성해주세요.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// tailwind.config.js</span>
<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./pages/**/*.{ts,tsx}"</span>,
    <span class="hljs-string">"./components/**/*.{ts,tsx}"</span>,
    <span class="hljs-string">"./app/**/*.{ts,tsx}"</span>,
    <span class="hljs-string">"./src/**/*.{ts,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
};
</code></pre>
<pre><code class="lang-css"> <span class="hljs-comment">/* App.css */</span>
<span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;
</code></pre>
<pre><code class="lang-javascript"> <span class="hljs-comment">// App.tsx</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>COMIT<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>마지막으로 index.css에는<br />http://meyerweb.com/eric/tools/css/reset/ 의 코드를 복붙해줍니다</p>
<pre><code class="lang-json"><span class="hljs-comment">// package.json  </span>
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc &amp;&amp; vite build"</span>,
    <span class="hljs-attr">"lint"</span>: <span class="hljs-string">"eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"</span>,
    <span class="hljs-attr">"preview"</span>: <span class="hljs-string">"vite preview"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"npx vite --port 3000"</span>
  },
</code></pre>
<p>마지막으로 아래와 같이 start에 npx vite --port 3000을 설정해주면 npm start를 입력하는 것으로 코드를 실행할 수 있습니다. 실행하면 "text-red-500"으로 text가 빨간 것으로 보아 tailwindcss가 잘 적용된 것을 볼 수 있습니다.</p>
<p>혹시나 따라오는 도중에<br />'JSX.IntrinsicElements' 인터페이스가 없으므로 JSX 요소는 암시적으로 'any' 형식입니다<br />와 같은 에러가 발생하는 경우 vscode를 껐다가 다시 켜주시면 해결됩니다!</p>
<h2 id="heading-api-data-fetching">API 서버 생성 및 data fetching</h2>
<p>다시 server로 돌아와 server.js에 간단한 API를 작성하겠습니다</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> port = <span class="hljs-number">8080</span>;

app.get(<span class="hljs-string">"/api/comit"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"I love COMIT"</span> });
});

app.listen(port, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Server started!"</span>);
});
</code></pre>
<p>/api/comit으로 GET 요청을 보내면<br />message: "I love COMIT"을 보내주는 간단한 API 입니다.<br /><a target="_blank" href="http://localhost:8080/api/comit">http://localhost:8080/api/comit</a> 로 접속하면 확인할 수 있습니다.</p>
<p>이제 client에서 요청을 보내는 코드를 작성해보겠습니다.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [data, setData] = useState([{}]);
  useEffect(<span class="hljs-function">() =&gt;</span> {
    fetch(<span class="hljs-string">"http://localhost:8080/api/comit"</span>)
      .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> res.json())
      .then(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> setData(data));
  }, []);
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>{data.message}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>서버로 요청을 보내 data에 응답을 저장하고 data.message를 표시하는 코드입니다. 정상 작동한다면 "I love COMIT"이 보여야 하는데 아마 안 될 겁니다.</p>
<p>다시 server로 돌아와서 cors를 설치하고</p>
<pre><code class="lang-bash">npm i cors
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> port = <span class="hljs-number">8080</span>;
<span class="hljs-keyword">const</span> cors = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cors"</span>);
<span class="hljs-keyword">const</span> bodyParser = <span class="hljs-built_in">require</span>(<span class="hljs-string">"body-parser"</span>);

app.use(cors());
app.use(bodyParser.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">false</span> }));
app.use(bodyParser.json());

app.get(<span class="hljs-string">"/api/comit"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"I love COMIT"</span> });
});

app.listen(port, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Server started!"</span>);
});
</code></pre>
<p>위와 같이 server.js를 수정하면 정상 작동합니다.</p>
<p>위의 과정을 그대로 따라해도 잘 안된다면</p>
<p>client의 package.json에서 다음과 같이 proxy룰 추가해주세요</p>
<pre><code class="lang-json">  <span class="hljs-string">"name"</span>: <span class="hljs-string">"client"</span>,
  <span class="hljs-string">"private"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"0.0.0"</span>,
  <span class="hljs-string">"proxy"</span>: <span class="hljs-string">"http://localhost:8080"</span>,
  <span class="hljs-string">"type"</span>: <span class="hljs-string">"module"</span>,
</code></pre>
<p>혹은 vite.config.ts 파일에서 proxy 설정을 할 수도 있습니다.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"vite"</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">"@vitejs/plugin-react-swc"</span>;

<span class="hljs-comment">// https://vitejs.dev/config/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      <span class="hljs-string">"/api"</span>: {
        target: <span class="hljs-string">"localhost:8080"</span>,
        changeOrigin: <span class="hljs-literal">true</span>,
        rewrite: <span class="hljs-function">(<span class="hljs-params">path</span>) =&gt;</span> path.replace(<span class="hljs-regexp">/^\/api/</span>, <span class="hljs-string">""</span>),
        secure: <span class="hljs-literal">false</span>,
        ws: <span class="hljs-literal">true</span>,
      },
    },
  },
});
</code></pre>
<h2 id="heading-66ei66y066as">마무리</h2>
<p>이상 client, server 개발 환경 구축하는 과정에 대해 소개했습니다. 분명 한 번 해본 작업을 다시 정리해나가면서 글을 작성하고 있음에도 막히는 부분들이 있었습니다. 개발 환경 구축에 어려움을 느끼는 분들께 조금이나마 도움이 되길 바랍니다.</p>
<h2 id="heading-7lc46rog7j6q66om">참고자료</h2>
<p><a target="_blank" href="https://www.youtube.com/watch?v=JoaTcz3K6do">https://www.youtube.com/watch?v=JoaTcz3K6do</a><br /><a target="_blank" href="https://tailwindcss.com/docs/guides/nextjs">https://tailwindcss.com/docs/guides/nextjs</a><br /><a target="_blank" href="https://abangpa1ace.tistory.com/entry/Expressjs-CORSCross-Origin-Resource-Sharing">https://abangpa1ace.tistory.com/entry/Expressjs-CORSCross-Origin-Resource-Sharing</a></p>
]]></content:encoded></item><item><title><![CDATA[Cs 전공면접 질문]]></title><description><![CDATA[학교에서 전공을 하는 학생들이라면 자료구조, 알고리즘, 운영체제 등 어려운 컴퓨터과학(CS)에 대한 공부들을 합니다. 개발자는 개발이나 하면 되지 왜 이런 것들을 배울까 불만이 쌓이기도 합니다.
하지만, 양질의 개발자 채용에 심혈을 기울이는 회사일수록 CS 전공 면접을 중요하게 봅니다. Kakao, Naver, Toss, 당근마켓 등 개발자가 선호하는 회사의 면접 질문에는 CS 전공 질문이 많이 등장합니다. 피상적인 질문을 하는 것이 아니라 깊...]]></description><link>https://blog.skku-comit.dev/cs</link><guid isPermaLink="true">https://blog.skku-comit.dev/cs</guid><category><![CDATA[CS]]></category><category><![CDATA[면접대비]]></category><dc:creator><![CDATA[조준형]]></dc:creator><pubDate>Fri, 26 Jan 2024 07:00:03 GMT</pubDate><content:encoded><![CDATA[<p>학교에서 전공을 하는 학생들이라면 자료구조, 알고리즘, 운영체제 등 어려운 컴퓨터과학(CS)에 대한 공부들을 합니다. 개발자는 개발이나 하면 되지 왜 이런 것들을 배울까 불만이 쌓이기도 합니다.</p>
<p>하지만, 양질의 개발자 채용에 심혈을 기울이는 회사일수록 CS 전공 면접을 중요하게 봅니다. Kakao, Naver, Toss, 당근마켓 등 개발자가 선호하는 회사의 면접 질문에는 CS 전공 질문이 많이 등장합니다. 피상적인 질문을 하는 것이 아니라 깊게 파고들면서 질문을 합니다.</p>
<p><a target="_blank" href="http://Teachyourselfcs.com">Teachyourselfcs.com</a>에서:</p>
<blockquote>
<p>소프트웨어 엔지니어에는 두 가지 유형이 있습니다. 도전적이고 혁신적인 작업을 수행할 만큼 컴퓨터 과학을 잘 이해하는 사람과 몇 가지 고급 도구에 익숙하기 때문에 그럭저럭 살아가는 사람입니다. 둘 다 스스로를 소프트웨어 엔지니어라고 부르며, 둘 다 초기 경력에서 비슷한 급여를 받는 경향이 있습니다. 그러나 유형 1 엔지니어는 가치 있는 상업적 작업이든 획기적인 오픈 소스 프로젝트이든, 기술 리더십이든 고품질의 개인 기여든 관계없이 시간이 지남에 따라 더 만족스럽고 보수가 좋은 작업을 향해 나아갑니다. 유형 1 엔지니어는 기존 방법을 통해서든, 경력 전반에 걸쳐 끊임없이 학습하여 컴퓨터 공학을 깊이 있게 배울 수 있는 방법을 찾습니다. 유형 2 엔지니어는 일반적으로 표면에 머물면서 기본 기반이 아닌 특정 도구와 기술을 배우고 기술 패션의 바람이 바뀔 때만 새로운 기술을 습득합니다. 현재 업계에 진출하는 사람의 수가 급격히 증가하고 있는 반면, CS 졸업생의 수는 상대적으로 정체되어 있습니다. 이러한 유형 2 엔지니어의 과잉 공급으로 인해 고용 기회가 줄어들고 업계에서 더 만족스러운 업무를 수행할 수 없게 됩니다. Type 1 엔지니어가 되기 위해 노력하든 아니면 단순히 더 많은 직업 안정성을 찾고 있든, 컴퓨터 과학을 배우는 것이 유일하고 신뢰할 수 있는 길입니다.</p>
</blockquote>
<p><em><s>개인적인 경험으로 유형2의 개발자라 하더라도 깊이있는 개발자가 되지못하는 것은 아니라고 생각하지만</s></em>, 어찌됐든 중요한 것은 채용면접을 담당하는 기업들의 시니어 개발자들은 이런 생각을 가지고 있다는 것입니다.</p>
<p>다행히 CS 전공 면접 질문에는 빈출 유형이 있습니다. 실무와 직결되는 내용과 필히 알아야할 지식이 정해져 있기 때문입니다. 따라서 CS 전공 면접의 파훼법은 간단합니다. <mark>빈출 질문에 정확히(깊게) 대답하자!</mark> 컴퓨터 공학과나 소프트웨어학을 전공하면서 배우는 다음과 같은 과목들:</p>
<ol>
<li><p>자료구조</p>
</li>
<li><p>알고리즘</p>
</li>
<li><p>운영체제</p>
</li>
<li><p>데이터베이스</p>
</li>
<li><p>네트워크</p>
</li>
</ol>
<p>수업을 열심히 듣고 기본적인 지식들을 쌓아가고 <mark>면접에 나오는 빈출 질문을 생각하며 공부하면 학교강의를 들으면서 자연스레 면접준비를 할 수 있을 것</mark>입니다(보통은 면접이 얼마 안남아서 부랴부랴 공부하는데 그때는 빈출 순서대로 공부하면 됩니다). 만일 <strong><em>이미 나는 성적은 시원히 날려버렸는데?</em></strong> 하는 사람도 괜찮습니다. 면접전에 이 질문들 만이라도 보고 대비해 가시면 됩니다.</p>
<blockquote>
<p>빈출 프로젝트 관련 면접 질문</p>
</blockquote>
<ul>
<li><p>자료구조</p>
<p>  Q. Array를 설명하시오.</p>
<p>  Q. Dynamic Array를 설명하시오.</p>
<p>  Q. Linked List를 설명하시오.</p>
<p>  Q. ⭐ Array vs Linked list를 비교 설명하시오.</p>
<p>  Q. Queue를 설명하시오.</p>
<p>  Q. Stack을 설명하시오.</p>
<p>  Q. Stack 두 개를 이용하여 Queue를 구현하시오.</p>
<p>  Q. Queue 두 개를 이용하여 Stack을 구현하시오.</p>
<p>  Q. ⭐ Queue vs priority queue를 비교 설명하시오.</p>
<p>  Q. ⭐⭐BST를 설명하시오.</p>
<p>  Q. ⭐⭐ Hash table을 설명하시오.</p>
<p>  Q. ⭐⭐⭐⭐ Hash table에서 collision이 발생하면 어떻게 되는지, 해결 방법을 설명하시오.</p>
</li>
<li><p>알고리즘</p>
<p>  Q. Insertion sort코드를 작성하고 설명하시오.</p>
<p>  Q. Selection sort 코드를 작성하고 설명하시오.</p>
<p>  Q. Bubble sort 코드를 작성하고 설명하시오.</p>
<p>  Q. Quick sort 코드를 작성하고 설명하시오.</p>
<p>  Q. Merge sort 코드를 작성하고 설명하시오.</p>
<p>  Q. Quick sort vs merge sort비교 설명하시오.</p>
<p>  Q. ⭐⭐ ⭐ 가장 빠른 정렬알고리즘을 설명하시오.</p>
<p>  Q. n개의 Hanoi 탑을 1번 막대에서 3번 막대로 모두 옮기는 알고리즘을 구현하시오.</p>
<p>  Q. Fibonacci 수열의 n번 째 값을 구하는 알고리즘을 구현하시오.</p>
<p>  Q. ⭐⭐ binary search 알고리즘을 화이트보드에 작성하면서 설명하시오.</p>
</li>
<li><p>운영체제</p>
<p>  Q. process를 설명하시오.</p>
<p>  Q. ⭐⭐⭐⭐ Multi process를 설명하시오..</p>
<p>  Q. ⭐⭐ Multi thread를 설명하시오.</p>
<p>  Q. ⭐⭐ multi process와 multi thread를 비교 설명하시오.</p>
<p>  Q. ⭐⭐ multi process환경에서 process간에 데이터를 주고받는 방식을 설명하시오.</p>
<p>  Q. ⭐ Multi process/thread 환경에서 동기화 문제의 해결방안을 설명하시오.</p>
<p>  Q. 교착상태(Deadlock)를 설명하시오.</p>
<p>  Q. paging을 설명하시오.</p>
<p>  Q. segmentation을 설명하시오.</p>
<p>  Q. ⭐ 가상 메모리를 설명하시오.</p>
</li>
<li><p>데이터베이스</p>
<p>  Q. Primary key를 설명하시오.</p>
<p>  Q. 관계형 데이터베이스의 N:M 관계를 설명하시오.</p>
<p>  Q. left outer join, inner join 차이를 설명하시오.</p>
<p>  Q. ⭐ RDB - NoSQL을 비교 설명하시오.</p>
<p>  Q. ⭐⭐ Transaction을 설명하시오.</p>
<p>  Q. ⭐ DeadLock을 설명하시오.</p>
<p>  Q. Index의 필요성을 설명하시오.</p>
<p>  Q. ⭐⭐⭐⭐ index를 어느 column에 사용하는 것이 좋을지 설명하시오.</p>
<p>  Q. ⭐데이터를 검색을 할 때 hash table의 시간복잡도는 O(1)이고, b+tree는 O(logn)으로 더 느리다. 왜 index는 hash table이 아니라 b+tree로 구현되는지 설명하시오.</p>
</li>
<li><p>네트워크</p>
<p>  Q. OSI 7계층과 TCP/IP 4계층을 비교 설명하시오.</p>
<p>  Q. ⭐ TCP vs UDP를 비교 설명하시오.</p>
<p>  Q. 3-way handshake는 무엇이고 각 과정은 어떻게 되는지 설명하시오.</p>
<p>  Q. HTTP를 설명하시오.</p>
<p>  Q. ⭐⭐ HTTP resquest method 중 GET vs POST를 비교 설명하시오.</p>
<p>  Q. HTTP status code를 설명하시오.</p>
<p>  Q. ⭐⭐ <a target="_blank" href="http://www.google.com">www.google.com</a>을 주소창에 입력 시, 화면이 나오기까지의 과정을 네트워크 관점으로 설명하시오.</p>
<p>  Q. ⭐쿠키와 세션의 차이점을 설명하시오.</p>
<p>  Q. 쿠키와 세션을 이용한 로그인 방식을 화이트보드에 설명하시오.</p>
</li>
</ul>
<p>** 성균관대 선배 <a target="_blank" href="http://www.nossi.dev/">www.nossi.dev</a>의 사이트를 참고함</p>
<p>-edited by 조준형</p>
]]></content:encoded></item><item><title><![CDATA[[OS]multi process와 multi thread]]></title><description><![CDATA[크롬에서 탭을 쓴다고 생각해봅시다. 우리는 chat gpt를 쓰던 학교 강의를 듣던 여러개의 탭을 켜놓고 작업들을 할 것입니다. 이 때에 우리는 멀티 프로세스(Multi-process)와 멀티 스레드(Multi-thread)의 개념을 생각해 볼 수 있습니다.

멀티-프로세스 모델 : 크롬은 탭이 마치 독립된 브라우저처럼 작동합니다.

멀티-쓰레드 사용: 각각의 탭이 각각 하나의 프로세스이고 그 안에서는 다수의 쓰레드가 사용됩니다. ex) 웹페이...]]></description><link>https://blog.skku-comit.dev/osmulti-process-multi-thread</link><guid isPermaLink="true">https://blog.skku-comit.dev/osmulti-process-multi-thread</guid><category><![CDATA[os]]></category><category><![CDATA[CS]]></category><category><![CDATA[Threads]]></category><dc:creator><![CDATA[조준형]]></dc:creator><pubDate>Fri, 26 Jan 2024 06:32:47 GMT</pubDate><content:encoded><![CDATA[<p>크롬에서 탭을 쓴다고 생각해봅시다. 우리는 chat gpt를 쓰던 학교 강의를 듣던 여러개의 탭을 켜놓고 작업들을 할 것입니다. 이 때에 우리는 멀티 프로세스(Multi-process)와 멀티 스레드(Multi-thread)의 개념을 생각해 볼 수 있습니다.</p>
<ol>
<li><p>멀티-프로세스 모델 : 크롬은 탭이 마치 독립된 브라우저처럼 작동합니다.</p>
</li>
<li><p>멀티-쓰레드 사용: 각각의 탭이 각각 하나의 프로세스이고 그 안에서는 다수의 쓰레드가 사용됩니다. ex) 웹페이지 렌더링, 자바스크립트 실행, 네트워크 작업 등은 별도의 쓰레드에서 처리될 수 있습니다.</p>
</li>
</ol>
<p>멀티 프로세스(Multi-process)와 멀티 스레드(Multi-thread)는 컴퓨터 프로그램이 작업을 병렬로 수행하는 두 가지 주요 방법입니다. 각각의 방식은 장단점이 있으며, 특정 상황에 따라 적합한 선택이 달라질 수 있습니다.</p>
<h3 id="heading-multi-process"><strong>멀티 프로세스(Multi-process)</strong></h3>
<ol>
<li><p><strong>정의</strong>: 멀티 프로세스는 여러 개의 독립적인 프로세스가 동시에 실행되는 방식입니다. 각 프로세스는 자신만의 메모리 공간과 시스템 자원을 가집니다.</p>
</li>
<li><p><strong>장점</strong>:</p>
<ul>
<li><p><strong>격리성</strong>: 프로세스 간에 메모리와 자원이 분리되어 있어, 한 프로세스의 오류가 다른 프로세스에 영향을 미치지 않습니다.</p>
</li>
<li><p><strong>안정성</strong>: 한 프로세스의 충돌이 시스템 전체에 영향을 미치는 일이 적습니다.</p>
</li>
</ul>
</li>
<li><p><strong>단점</strong>:</p>
<ul>
<li><p><strong>자원 소모</strong>: 각 프로세스는 독립적인 메모리와 자원을 가지므로, 많은 자원을 소모합니다.</p>
</li>
<li><p><strong>통신 복잡성</strong>: 프로세스 간 통신(IPC)이 복잡하고 비용이 많이 듭니다.</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-multi-thread"><strong>멀티 스레드(Multi-thread)</strong></h3>
<ol>
<li><p><strong>정의</strong>: 멀티 스레드는 하나의 프로세스 내에서 여러 개의 스레드가 동시에 실행되는 방식입니다. 이들 스레드는 프로세스의 메모리와 자원을 공유합니다.</p>
</li>
<li><p><strong>장점</strong>:</p>
<ul>
<li><p><strong>자원 효율성</strong>: 스레드는 프로세스의 자원을 공유하기 때문에, 자원 소모가 프로세스에 비해 적습니다.</p>
</li>
<li><p><strong>통신 용이성</strong>: 같은 메모리 공간을 공유하기 때문에 스레드 간 통신이 더 간단하고 효율적입니다.</p>
</li>
</ul>
</li>
<li><p><strong>단점</strong>:</p>
<ul>
<li><p><strong>안정성 문제</strong>: 한 스레드의 문제가 전체 프로세스에 영향을 줄 수 있습니다.</p>
</li>
<li><p><strong>동시성 문제</strong>: 공유 자원에 대한 접근 관리가 필요하며, 이를 위한 동기화 처리가 필요합니다.</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-kirruytqtzaqkg"><strong>비교</strong></h3>
<ul>
<li><p><strong>자원 관리</strong>: 멀티 프로세스는 자원을 공유하지 않기 때문에 관리가 더 단순하지만, 더 많은 자원을 소모합니다. 반면, 멀티 스레드는 효율적인 자원 사용을 가능하게 하지만, 공유 자원에 대한 복잡한 동기화가 필요합니다.</p>
</li>
<li><p><strong>성능과 오버헤드</strong>: 멀티 스레드는 컨텍스트 스위칭 오버헤드가 프로세스보다 작기 때문에 성능상 이점이 있습니다. 반면, 멀티 프로세스는 프로세스 간 컨텍스트 스위칭에 더 많은 비용이 듭니다.</p>
</li>
<li><p><strong>안정성과 격리성</strong>: 멀티 프로세스는 프로세스 간 격리가 잘 되어 있어 안정성이 높습니다. 그러나 멀티 스레드는 한 스레드의 오류가 전체 프로세스에 영향을 줄 수 있어, 상대적으로 안정성이 낮습니다.</p>
<p>  -edited by 조준형</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[C/C++ 큐(Queue) (2)- 연결 리스트로 구현, 클래스 구현]]></title><description><![CDATA[개요
연결 리스트로 큐를 구현해보고, C++ 클래스로 변환해보자. 연결 리스트는 구현이 매우 쉽다. 일반 배열로 하는 것과 다르게 실제로 데이터를 입력하면 공간을 할당하고 데이터를 출력하면 데이터의 메모리 할당을 해제하면 되어 훨씬 직관적이다.
연결 리스트 큐 구현 Node
struct Node
{
    int data;
    Node* next;
}* front = NULL, *rear = NULL;

front의 위치를 연결 리스트의 맨...]]></description><link>https://blog.skku-comit.dev/cc-queue-2</link><guid isPermaLink="true">https://blog.skku-comit.dev/cc-queue-2</guid><category><![CDATA[C]]></category><category><![CDATA[C++]]></category><category><![CDATA[datastructure]]></category><category><![CDATA[queue]]></category><dc:creator><![CDATA[Gon Tae]]></dc:creator><pubDate>Thu, 25 Jan 2024 16:25:08 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-6rcc7jqu">개요</h3>
<p>연결 리스트로 큐를 구현해보고, C++ 클래스로 변환해보자. 연결 리스트는 구현이 매우 쉽다. 일반 배열로 하는 것과 다르게 실제로 데이터를 입력하면 공간을 할당하고 데이터를 출력하면 데이터의 메모리 할당을 해제하면 되어 훨씬 직관적이다.</p>
<h3 id="heading-node">연결 리스트 큐 구현 Node</h3>
<pre><code class="lang-cpp"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Node</span>
{</span>
    <span class="hljs-keyword">int</span> data;
    Node* next;
}* front = <span class="hljs-literal">NULL</span>, *rear = <span class="hljs-literal">NULL</span>;
</code></pre>
<p>front의 위치를 연결 리스트의 맨 앞, rear를 맨 뒤로 설정하여 front를 통해 데이터가 출력되고, rear를 통해 데이터가 들어간다.</p>
<h3 id="heading-enqueue">enqueue함수-데이터 입력 함수</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">enqueue</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x)</span>
</span>{
    Node* t;
    t = <span class="hljs-keyword">new</span> Node;

    <span class="hljs-keyword">if</span> (t == <span class="hljs-literal">NULL</span>)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue is Full\n"</span>);
    <span class="hljs-keyword">else</span>{
        t-&gt;data = x;
        t-&gt;next = <span class="hljs-literal">NULL</span>;
        <span class="hljs-keyword">if</span> (front == <span class="hljs-literal">NULL</span>)
            front = rear = t;
        <span class="hljs-keyword">else</span>
        {
            rear-&gt;next = t;
            rear = t;
        }
    }
}
</code></pre>
<p>새로운 노드를 할당하고, 할당이 되지 않는다면(t=NULL) 메모리에 할당할 공간이 없다는 것이기에 큐가 꽉 찼다고 판단한다. 그게 아니라면 데이터를 넣고, rear의 다음에 노드를 연결하고 rear를 해당 노드로 옮긴다.</p>
<h3 id="heading-dequeue">dequeue함수-데이터 출력 함수</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">dequeue</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">int</span> x = <span class="hljs-number">-1</span>;
    Node* t = <span class="hljs-literal">NULL</span>;

    <span class="hljs-keyword">if</span> (front == <span class="hljs-literal">NULL</span>)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue is Empty\n"</span>);
    <span class="hljs-keyword">else</span>
    {
        x = front-&gt;data;
        t = front;
        front = front-&gt;next;
        <span class="hljs-keyword">delete</span> t;
    }

    <span class="hljs-keyword">return</span> x;
}
</code></pre>
<p>front가 NULL이라면 연결 리스트가 없다는 뜻이기에 비었다고 본다. 그게 아니라면 front의 데이터를 출력하고, front의 메모리를 해제하고, next 노드로 front를 옮긴다. 일반 배열에서는 front가 데이터의 시작의 앞 인덱스를 나타냈지만, 연결 리스트에서는 front가 맨 앞 데이터를 나타낸다는 차이가 있다.</p>
<h3 id="heading-class">Class 활용 큐 구현</h3>
<p>로직은 같으므로 코드만 첨부하겠다. 클래스에 size, front, rear, Q를 모두 넣어 함수 인자로 큐 구조체를 주지 않아도 되는 점이 좋다.</p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Queue</span>
{</span>
    <span class="hljs-keyword">private</span>:
        <span class="hljs-keyword">int</span> size;
        <span class="hljs-keyword">int</span> front;
        <span class="hljs-keyword">int</span> rear;
        <span class="hljs-keyword">int</span>* Q;
    <span class="hljs-keyword">public</span>:
        Queue(<span class="hljs-keyword">int</span> size);
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">enqueue</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x)</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">dequeue</span><span class="hljs-params">()</span></span>;
        <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">display</span><span class="hljs-params">()</span></span>;
        ~Queue();
};

Queue::Queue(<span class="hljs-keyword">int</span> size)
{
    size = size;
    Q = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[size];
    front = rear = <span class="hljs-number">-1</span>;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Queue::enqueue</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x)</span>
</span>{
    <span class="hljs-keyword">if</span> (rear == size - <span class="hljs-number">1</span>)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue is Full\n"</span>);
    <span class="hljs-keyword">else</span>
    {
        rear++;
        Q[rear] = x;
    }
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">Queue::dequeue</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">int</span> x = <span class="hljs-number">-1</span>;
    <span class="hljs-keyword">if</span> (front == rear)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue is Empty\n"</span>);
    }
    <span class="hljs-keyword">else</span>
    {
        front++;
        x = Q[front];
    }
    <span class="hljs-keyword">return</span> x;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Queue::display</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">int</span> i;
    <span class="hljs-keyword">for</span> (i = front + <span class="hljs-number">1</span>; i &lt;= rear; i++)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d "</span>, Q[i]);
    }
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
}
</code></pre>
<h3 id="heading-dequeuedouble-enabled-queue">추가::Dequeue(Double Enabled Queue)</h3>
<p>Dequeue는 front와 rear을 통해 모두 데이터를 입력할 수 있고, 뺄 수 있는 양방향 큐이다. 일반 큐가 front는 오직 데이터 출력, rear이 오직 데이터 입력 구멍이라고 하면, Dequeue는 front를 통해서도 데이터를 넣고, 뺄 수 있고, rear에서도 가능하다. 이 또한, 코드를 보면 쉽게 이해할 수 있기에 코드만 첨부한다.</p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Queue</span>
{</span>
    <span class="hljs-keyword">int</span> size;
    <span class="hljs-keyword">int</span> front;
    <span class="hljs-keyword">int</span> rear;
    <span class="hljs-keyword">int</span>* Q;
};

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">create</span><span class="hljs-params">(Queue *q,<span class="hljs-keyword">int</span> x)</span>
</span>{
    q-&gt;size = x;
    q-&gt;front = q-&gt;rear = <span class="hljs-number">-1</span>;
    q-&gt;Q = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[q-&gt;size];

}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">enqueueRear</span><span class="hljs-params">(Queue *q, <span class="hljs-keyword">int</span> x)</span>
</span>{
    <span class="hljs-keyword">if</span> (q-&gt;rear == q-&gt;size - <span class="hljs-number">1</span>)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue is Full\n"</span>);
    <span class="hljs-keyword">else</span>
    {
        q-&gt;rear++;
        q-&gt;Q[q-&gt;rear] = x;
    }
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">enqueueFront</span><span class="hljs-params">(Queue *q, <span class="hljs-keyword">int</span> x)</span>
</span>{
    <span class="hljs-keyword">if</span> (q-&gt;front == <span class="hljs-number">-1</span>)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue through Front is Impossible\n"</span>);
    <span class="hljs-keyword">else</span>
    {
        q-&gt;Q[q-&gt;front] = x;
        q-&gt;front--;
    }
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">dequeueRear</span><span class="hljs-params">(Queue *q)</span>
</span>{
    <span class="hljs-keyword">int</span> x = <span class="hljs-number">-1</span>;

    <span class="hljs-keyword">if</span> (q-&gt;rear == <span class="hljs-number">-1</span>)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue through Rear is Impossible\n"</span>);
    <span class="hljs-keyword">else</span>
    {
        x = q-&gt;Q[q-&gt;rear] = x;
        q-&gt;rear--;
    }

    <span class="hljs-keyword">return</span> x;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">dequeueFront</span><span class="hljs-params">(Queue *q)</span>
</span>{
    <span class="hljs-keyword">int</span> x = <span class="hljs-number">-1</span>;

    <span class="hljs-keyword">if</span> (q-&gt;front == q-&gt;size - <span class="hljs-number">1</span>)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue through Front is Impossible\n"</span>);
    <span class="hljs-keyword">else</span>
    {
        q-&gt;front++;
        x = q-&gt;Q[q-&gt;front];
    }

    <span class="hljs-keyword">return</span> x;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Display</span><span class="hljs-params">(Queue *q)</span>
</span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = q-&gt;front + <span class="hljs-number">1</span>; i &lt;= q-&gt;rear; i++)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d "</span>, q-&gt;Q[i]);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span>
</span>{
    Queue *q;
    create(q, <span class="hljs-number">5</span>);

    enqueueRear(q, <span class="hljs-number">1</span>);
    enqueueRear(q, <span class="hljs-number">2</span>);
    enqueueRear(q, <span class="hljs-number">3</span>);
    enqueueRear(q, <span class="hljs-number">4</span>);
    enqueueRear(q, <span class="hljs-number">5</span>);
    enqueueRear(q, <span class="hljs-number">6</span>);
    dequeueFront(q);
    enqueueFront(q, <span class="hljs-number">7</span>);
    Display(q);

    dequeueFront(q);
    Display(q);
    dequeueRear(q);
    Display(q);
}
</code></pre>
<h3 id="heading-ia"> </h3>
<p>마무리</p>
<p>클래스와 연결 리스트로 구현해 보았다. 연결 리스트로 구현했을 때, 훨씬 직관적인 구현이 가능했음을 알 수 있다. 여담으로, 스택은 FILO 형태로 처음 들어간 데이터가 제일 늦게 나오지만 스택을 두 개 사용하면 처음 들어간 데이터가 그 다음 스택으로 제일 늦게 들어갔다가 마지막에는 제일 먼저 나오므로 FIFO를 간접적으로 구현 가능하다.</p>
]]></content:encoded></item><item><title><![CDATA[C/C++ 큐(Queue)]]></title><description><![CDATA[개요
큐(Queue)는 자료구조의 한 형태로 스택과 다르게 FIFO(First In First Out) 형태의 자료구조이다. FIFO는 말 그대로 선입선출이라는 의미로 먼저 들어간 데이터가 먼저 나온다나는 것이다. 쉽게 생각하여 표를 받기 위해 줄을 서는 것으로 보면 된다. 표를 받기 위해 먼저 줄을 서면 먼저 표를 받는 방식이다. 너비 우선 탐색, 캐시 구현 등의 다양한 곳에 사용된다. 큐의 구조와 큐를 구성하는 메소드 들에 대해 알아보자.
...]]></description><link>https://blog.skku-comit.dev/cc-queue</link><guid isPermaLink="true">https://blog.skku-comit.dev/cc-queue</guid><category><![CDATA[C++]]></category><category><![CDATA[C]]></category><category><![CDATA[data structures]]></category><category><![CDATA[queue]]></category><dc:creator><![CDATA[Gon Tae]]></dc:creator><pubDate>Thu, 25 Jan 2024 15:59:46 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-6rcc7jqu">개요</h3>
<p>큐(Queue)는 자료구조의 한 형태로 스택과 다르게 FIFO(First In First Out) 형태의 자료구조이다. FIFO는 말 그대로 선입선출이라는 의미로 먼저 들어간 데이터가 먼저 나온다나는 것이다. 쉽게 생각하여 표를 받기 위해 줄을 서는 것으로 보면 된다. 표를 받기 위해 먼저 줄을 서면 먼저 표를 받는 방식이다. 너비 우선 탐색, 캐시 구현 등의 다양한 곳에 사용된다. 큐의 구조와 큐를 구성하는 메소드 들에 대해 알아보자.</p>
<h3 id="heading-7ygqioq1royhsoyyta">큐 구조체</h3>
<pre><code class="lang-cpp"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Queue</span>
{</span>
    <span class="hljs-keyword">int</span> size;
    <span class="hljs-keyword">int</span> front;
    <span class="hljs-keyword">int</span> rear;
    <span class="hljs-keyword">int</span>* Q;
};
</code></pre>
<p>큐에는 줄의 시작(front)와 끝(rear)이 존재하며, 데이터가 통하는 구멍이 두 개라 볼 수 있다. size는 큐가 데이터를 담을 수 있는 개수를 나타내며, Q는 할당할 배열 포인터이다.</p>
<h3 id="heading-create">create함수-큐 생성 함수</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">create</span><span class="hljs-params">(Queue *q, <span class="hljs-keyword">int</span> size)</span>
</span>{
    q-&gt;size = size;
    q-&gt;front = q-&gt;rear = <span class="hljs-number">-1</span>;
    q-&gt;Q = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[q-&gt;size];
}
</code></pre>
<p>선언한 큐 구조체에 사이즈를 채우고, Q에 배열을 할당한다. front와 rear는 각각 배열의 인덱스를 나타내는데 아직 큐가 비어 있으므로 -1 인덱스를 할당한다.</p>
<h3 id="heading-enqueue">enqueue함수-큐 데이터 입력함수</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">enqueue</span><span class="hljs-params">(Queue *q, <span class="hljs-keyword">int</span> x)</span>
</span>{
    <span class="hljs-keyword">if</span> (q-&gt;rear == q-&gt;size <span class="hljs-number">-1</span>)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue is Full"</span>);
    <span class="hljs-keyword">else</span>
    {
        q-&gt;rear++;
        q-&gt;Q[q-&gt;rear] = x;
    }
}
</code></pre>
<p>먼저 rear이 큐의 끝에 도달했는지 체크한다. 데이터가 입력될 때마다, 큐의 rear이 증가하고 그 자리에 데이터가 들어간다. rear이 마지막 인덱스에 있으면 더 이상 데이터를 넣지 못한다. 아래의 그림을 보면 이해가 쉬울 것이다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706197215627/b9004d00-66eb-47ac-87d5-36148ca41706.jpeg" alt class="image--center mx-auto" /></p>
<h3 id="heading-dequeue">dequeue함수-데이터를 출력하는 함수</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">dequeue</span><span class="hljs-params">(Queue *q)</span>
</span>{
    <span class="hljs-keyword">int</span> x = <span class="hljs-number">-1</span>;

    <span class="hljs-keyword">if</span> (q-&gt;front == q-&gt;rear)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue is Empty\n"</span>);
    <span class="hljs-keyword">else</span>
    {
        q-&gt;front++;
        x = q-&gt;Q[q-&gt;front];
    }
    <span class="hljs-keyword">return</span> x;
}
</code></pre>
<p>front는 쉽게 말해 표를 주는 맨 앞 점원이라 보면 된다. front 뒤에 데이터가 존재하며, front를 통해 데이터가 나간다고 보면 된다. front와 rear 사이에 데이터가 저장되는데 front와 rear의 위치가 같다면 그 사이에 공간이 없다는 뜻이기에 큐가 비었음을 알 수 있다. 그게 아니라면, front의 위치가 한 칸 밀리고, 거기에 있는 데이터를 빼낸다.</p>
<p>큐의 데이터가 빠져나가는데 왜 배열의 데이터가 안 밀리냐고 궁금할 수 있다. 데이터가 나가는 위치를 고정해 놓고 데이터(표를 받는 사람)이 앞으로 움직이는 것은 소요가 크다. 대신에 데이터가 나가는 위치(front, 표를 주는 사람)이 뒤로 움직이면 똑같은 효과를 front만 옮겨서 얻을 수 있어 효율이 좋다.</p>
<h3 id="heading-display">Display함수-데이터 출력 함수</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Display</span><span class="hljs-params">(Queue q)</span>
</span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = q.front+<span class="hljs-number">1</span>; i &lt;= q.rear; i++)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d "</span>, q.Q[i]);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
}
</code></pre>
<p>배열의 값들을 front부터 rear까지 인덱스로 출력한다.</p>
<h3 id="heading-7j287j6qio2bkoydmcdri6jsoja">일자 큐의 단점</h3>
<p>위에 구현한 일자 큐, 배열의 처음과 끝이 존재하는 큐는 효율이 좋지 않다. 데이터를 넣고 빼는 것을 반복해서 rear이 배열의 끝에 도달하면 front가 밀려 앞의 데이터가 큐에서 빠져나가서 공간이 비더라도 큐는 꽉 찼다고 판단하여 입력이 불가능하다. 이렇듯 일자 큐는 일회성이다. 이를 해결하기 위한 방법이 순환 큐이다. 배열의 끝이 배열의 처음으로 이어져서 rear는 배열의 마지막에서 한 칸 움직이면 배열의 처음으로 움직일 수 있다.</p>
<h3 id="heading-7iic7zmyio2bkcdqtaztmittlzjqula">순환 큐 구현하기</h3>
<p>순환 큐의 front rear 움직임은 그저 +1을 하지 않는다. 다음과 같은 식으로 움직인다.</p>
<p>rear = (rear + 1) % 배열의 size</p>
<p>이 방식으로 front와 rear는 배열의 끝에서 한 칸 움직이면 나머지 연산에 의해 배열의 처음 인덱스로 이동하게 된다. 똑같이 배열이 빈 것은 front와 rear이 같을 때이며, 큐가 꽉 찬 것은 rear에서 이동한 곳이 front일 때이다. 아래는 empty일 때와 full일 때의 상황이다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706198148750/753ddc97-9de3-4467-9e2c-f348f8b8e1c7.jpeg" alt class="image--center mx-auto" /></p>
<h3 id="heading-enqueue-1">순환 큐 enqueue함수</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">enqueue</span><span class="hljs-params">(Queue *q, <span class="hljs-keyword">int</span> x)</span>
</span>{
    <span class="hljs-keyword">if</span> ((q-&gt;rear + <span class="hljs-number">1</span>) % q-&gt;size == q-&gt;front)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue is full"</span>);
    <span class="hljs-keyword">else</span>
    {
        q-&gt;rear = ((q-&gt;rear + <span class="hljs-number">1</span>) % q-&gt;size);
        q-&gt;Q[q-&gt;rear] = x;
    }
}
</code></pre>
<p>rear 연산을 통해 다음 이동 자리가 front와 같다면 큐는 꽉 찬 것이며, 그게 아니라면 rear을 한 칸 뒤로 옮긴 뒤 데이터를 넣는다.</p>
<h3 id="heading-dequeue-1">순환 큐 dequeue함수</h3>
<pre><code class="lang-cpp"> <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">dequeue</span><span class="hljs-params">(Queue *q)</span>
</span>{
    <span class="hljs-keyword">int</span> x = <span class="hljs-number">-1</span>;

    <span class="hljs-keyword">if</span> (q-&gt;front == q-&gt;rear)
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Queue is Empty\n"</span>);
    <span class="hljs-keyword">else</span>
    {
        q-&gt;front = (q-&gt;front + <span class="hljs-number">1</span>) % q-&gt;size;
        x = q-&gt;Q[q-&gt;front];
    }
    <span class="hljs-keyword">return</span> x;
}
</code></pre>
<p>rear과 front가 같다면 큐는 빈 것이며, 그게 아니라면 front를 뒤로 밀고, 거기의 데이터를 뽑는다.</p>
<h3 id="heading-66ei66y066as">마무리</h3>
<p>큐에 대해 알아보았다. 스택과 다르게 FIFO 방식이며, 일반 큐와 일반 큐의 일회성을 보완한 순환 큐가 있다. 다음 글에서 연결 리스트로 큐를 구현해보고, C++ 클래스로 구현해보자</p>
]]></content:encoded></item><item><title><![CDATA[C/c++ 단순 연결 리스트 C++ 클래스 화]]></title><description><![CDATA[개요
전 글에서 공부한 단순 연결 리스트의 구성과 함수를 C++의 클래스로 바꿔 작성해 보았다. 전 글과 함수의 구현 로직은 같기에 코드와 실행 결과만 첨부하고자 한다.
클래스가 되며 바뀐 점
사실 Node에 대한 것은 structure을 사용하나 class를 사용하나 별 차이가 없다. LinkedList에 대해 살펴볼 점은 first의 위치이다. 기존 structure을 사용할 때는 모든 함수에 기본적으로 인자로 first를 줘서 어떤 연결 ...]]></description><link>https://blog.skku-comit.dev/cc-c</link><guid isPermaLink="true">https://blog.skku-comit.dev/cc-c</guid><category><![CDATA[C++]]></category><category><![CDATA[#linkedlists]]></category><category><![CDATA[data structures]]></category><dc:creator><![CDATA[Gon Tae]]></dc:creator><pubDate>Mon, 22 Jan 2024 04:45:45 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-6rcc7jqu">개요</h3>
<p>전 글에서 공부한 단순 연결 리스트의 구성과 함수를 C++의 클래스로 바꿔 작성해 보았다. 전 글과 함수의 구현 로직은 같기에 코드와 실행 결과만 첨부하고자 한다.</p>
<h3 id="heading-7yg0656y7iqk6rcaiouqmoupscdrsjtrgjag7kcq">클래스가 되며 바뀐 점</h3>
<p>사실 Node에 대한 것은 structure을 사용하나 class를 사용하나 별 차이가 없다. LinkedList에 대해 살펴볼 점은 first의 위치이다. 기존 structure을 사용할 때는 모든 함수에 기본적으로 인자로 first를 줘서 어떤 연결 리스트의 시작점을 기준으로 하는지 필요했다. 하지만, 클래스가 되면서 연결 리스트를 객체로 생성하면, 해당 객체는 첫 시작의 주소를 알고 있으며 멤버 함수는 당연히 해당 객체의 연결 리스트에 대한 함수이기에 굳이 인자로 줄 필요가 없었다. 또한, 생성자에 create함수의 로직을 사용했다는 점에서 차이가 있다.</p>
<h3 id="heading-7j6r7isx7zwcioy9loutna">작성한 코드</h3>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;iostream&gt;</span></span>

<span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> <span class="hljs-built_in">std</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node</span>
{</span>
    <span class="hljs-keyword">public</span>:
        <span class="hljs-keyword">int</span> data;
        Node* next;
};

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinkedList</span>
{</span>
<span class="hljs-keyword">private</span>:
    Node* first;

<span class="hljs-keyword">public</span>:
    LinkedList()
    {
        Node* first = <span class="hljs-literal">NULL</span>;
    };

    LinkedList(<span class="hljs-keyword">int</span> A[], <span class="hljs-keyword">int</span> n);
    ~LinkedList();

    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">Count</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Display</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function">Node* <span class="hljs-title">Search</span><span class="hljs-params">(<span class="hljs-keyword">int</span> key)</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Insert</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> index)</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">Delete</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Reverse</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">isLoop</span><span class="hljs-params">()</span></span>;
};

LinkedList::LinkedList(<span class="hljs-keyword">int</span> A[], <span class="hljs-keyword">int</span> n)
{
    Node* t;
    Node* last;
    first = <span class="hljs-keyword">new</span> Node;
    first-&gt;data = A[<span class="hljs-number">0</span>];
    first-&gt;next = <span class="hljs-literal">NULL</span>;
    last = first;

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span>; i &lt; n; i++)
    {
        t = <span class="hljs-keyword">new</span> Node;
        t-&gt;data = A[i];
        t-&gt;next = <span class="hljs-literal">NULL</span>;
        last-&gt;next = t;
        last = t; 
    }
}

LinkedList::~LinkedList()
{
    Node* p = first;
    Node* t = <span class="hljs-literal">NULL</span>;

    <span class="hljs-keyword">while</span>(p)
    {
        <span class="hljs-keyword">if</span>(p)
        {
            t = p;
            p = p-&gt;next;
            <span class="hljs-keyword">delete</span> t;
        }
    }
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">LinkedList::Count</span><span class="hljs-params">()</span>
</span>{
    Node* p = first;
    <span class="hljs-keyword">int</span> count = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">while</span>(p)
    {
        <span class="hljs-keyword">if</span> (p)
            count++;

        p = p-&gt;next;
    }

    <span class="hljs-keyword">return</span> count;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">LinkedList::Display</span><span class="hljs-params">()</span>
</span>{
    Node* t = first;

    <span class="hljs-keyword">while</span>(t)
    {
        <span class="hljs-built_in">cout</span> &lt;&lt; t-&gt;data &lt;&lt; <span class="hljs-string">" "</span>;
        t = t-&gt;next;
    }
    <span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-built_in">endl</span>;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">LinkedList::Delete</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span>
</span>{
    Node* p;
    Node* q;
    <span class="hljs-keyword">int</span> x;

    <span class="hljs-keyword">if</span> (index &lt; <span class="hljs-number">1</span> || index &gt; Count())
        <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;

    <span class="hljs-keyword">if</span> (index == <span class="hljs-number">1</span>)
    {
        p = first;
        x = p-&gt;data;
        first = p-&gt;next;
        <span class="hljs-keyword">delete</span> p;
        <span class="hljs-keyword">return</span> x;
    }
    <span class="hljs-keyword">else</span>
    {
        p = first;

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; index - <span class="hljs-number">1</span> &amp;&amp; p; i++)
        {
            q = p;
            p = p-&gt;next;
        }

        q-&gt;next = p-&gt;next;
        x = p-&gt;data;
        <span class="hljs-keyword">delete</span> p;
        <span class="hljs-keyword">return</span> x;


    }
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">LinkedList::Insert</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index, <span class="hljs-keyword">int</span> x)</span>
</span>{
    Node* t = <span class="hljs-keyword">new</span> Node;
    t-&gt;data = x;
    t-&gt;next = <span class="hljs-literal">NULL</span>;

    Node* p = first;
    Node* q;

    <span class="hljs-keyword">if</span> (!p)
    {
        first = t;
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-keyword">if</span> (index == <span class="hljs-number">0</span>)
        {
            t-&gt;next = first;
            first = t;
        }

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; index - <span class="hljs-number">1</span>; i++)
        {
            p = p-&gt;next;        
        }
        t-&gt;next = p-&gt;next;
        p-&gt;next = t;
    }
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">LinkedList::Reverse</span><span class="hljs-params">()</span>
</span>{
    Node* p = first, *q = <span class="hljs-literal">NULL</span>, *r = <span class="hljs-literal">NULL</span>;

    <span class="hljs-keyword">while</span>(p)
    {
        r = q;
        q = p;
        p = p-&gt;next;

        r = q-&gt;next;
    }
    first = q;
}

<span class="hljs-function">Node* <span class="hljs-title">LinkedList::Search</span><span class="hljs-params">(<span class="hljs-keyword">int</span> key)</span>
</span>{
    Node* q = <span class="hljs-literal">NULL</span>, *p = first;

    <span class="hljs-keyword">while</span>(p)
    {
        <span class="hljs-keyword">if</span> (p-&gt;data == key)
            <span class="hljs-keyword">return</span> p;

        <span class="hljs-keyword">if</span> (key == p-&gt;data)
        {
            q-&gt;next = p-&gt;next;
            p-&gt;next = first;
            first = p;
            <span class="hljs-keyword">return</span> p;
        }
        q = p;
        p = p-&gt;next;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">LinkedList::isLoop</span><span class="hljs-params">()</span>
</span>{
    Node* p = first, *q = first;


    <span class="hljs-keyword">do</span> {
        p = p-&gt;next;
        q = q-&gt;next;

        q = q ? q-&gt;next : q;
    } <span class="hljs-keyword">while</span>(p &amp;&amp; q &amp;&amp; p != q);

    <span class="hljs-keyword">if</span> (p == q)
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    <span class="hljs-keyword">else</span>
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<h3 id="heading-main">main함수에서의 객체 생성 후, 실행 결과</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-keyword">int</span> A[]={<span class="hljs-number">1</span>,<span class="hljs-number">6</span>,<span class="hljs-number">8</span>,<span class="hljs-number">12</span>,<span class="hljs-number">56</span>};
    <span class="hljs-function">LinkedList <span class="hljs-title">l</span><span class="hljs-params">(A,<span class="hljs-number">5</span>)</span></span>;

    l.Insert(<span class="hljs-number">2</span>,<span class="hljs-number">67</span>);

     l.Display();

    <span class="hljs-built_in">cout</span>&lt;&lt;<span class="hljs-string">"lenght "</span>&lt;&lt;l.Count()&lt;&lt;<span class="hljs-built_in">endl</span>;


    l.Delete(<span class="hljs-number">1</span>);
    <span class="hljs-built_in">cout</span>&lt;&lt;<span class="hljs-string">"After deletion"</span>&lt;&lt;<span class="hljs-built_in">endl</span>;
    l.Display();
    <span class="hljs-keyword">return</span>  <span class="hljs-number">0</span>;
}
</code></pre>
<p>실행 결과</p>
<p>1 6 67 8 12 56</p>
<p>lenght 6</p>
<p>After deletion</p>
<p>6 67 8 12 56</p>
<p>올바른 결과가 나옴을 알 수 있다.</p>
]]></content:encoded></item><item><title><![CDATA[[Node.js] Sequelize 기초 및 개념]]></title><description><![CDATA[ORM

Object Relational Mapping의 약자로 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 것

SQL query가 아닌 직관적인 코드로 데이터를 조작할 수 있어 객체 지향 프로그래밍에 집중할 수 있다.


Sequelize

MySQL, MaraiDB, MS SQL 등 관계형 데이터베이스를 위한 promise 기반 Node.js ORM 도구

promise 기반으로 구현되었기 때문에 비동기 로직을 편리하게 작성할...]]></description><link>https://blog.skku-comit.dev/nodejs-sequelize</link><guid isPermaLink="true">https://blog.skku-comit.dev/nodejs-sequelize</guid><dc:creator><![CDATA[김성중]]></dc:creator><pubDate>Sun, 21 Jan 2024 14:31:06 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-orm">ORM</h2>
<ul>
<li><p>Object Relational Mapping의 약자로 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 것</p>
</li>
<li><p>SQL query가 아닌 직관적인 코드로 데이터를 조작할 수 있어 객체 지향 프로그래밍에 집중할 수 있다.</p>
</li>
</ul>
<h2 id="heading-sequelize">Sequelize</h2>
<ul>
<li><p>MySQL, MaraiDB, MS SQL 등 관계형 데이터베이스를 위한 promise 기반 Node.js ORM 도구</p>
</li>
<li><p>promise 기반으로 구현되었기 때문에 비동기 로직을 편리하게 작성할 수 있다.</p>
</li>
</ul>
<h2 id="heading-sequelize-1">Sequelize 설치</h2>
<pre><code class="lang-basic">npm install sequelize sequelize-cli mysql2 // sequelize 설치
sequelize init // sequelize 초기화
</code></pre>
<p>sequelize init을 완료하면 해당 작업 경로에 config, migrations, models, seeders와 같은 폴더들이 생긴다.</p>
<pre><code class="lang-basic">.
├── config 
├── migrations 
├── models  
├── seeders
</code></pre>
<ul>
<li><p>config : 데이터베이스 설정 파일, 사용자 이름, DB 이름, 비밀번호 등의 정보가 들어있음</p>
</li>
<li><p>migrations : 데이터베이스의 변화하는 과정들을 추적해나가는 정보로 실제 데이터베이스에 반영할 수 있음</p>
</li>
<li><p>models : 데이터베이스 각 테이블의 정보 및 필드 타입을 정의하고 하나의 객체로 만들어 사용</p>
</li>
<li><p>seeders : 테이블에 기본 데이터를 넣을 때 사용</p>
</li>
</ul>
<h2 id="heading-model">model 생성</h2>
<pre><code class="lang-basic">sequelize model:generate --<span class="hljs-keyword">name</span> Customer --attributes id:integer,<span class="hljs-keyword">name</span>:string, email:string, phone:string, address:string
</code></pre>
<h2 id="heading-model-1">model 옵션</h2>
<ul>
<li><p>type - DataTypes : STRING, INTEGER, FLOAT, DOUBLE, BOOLEAN ...</p>
</li>
<li><p>allowNull - Boolean : Null 허용 여부 (default: true)</p>
</li>
<li><p>defaultValue - any : 기본값 설정(default: null)</p>
</li>
<li><p>unique - Boolean : 유일한 값 여부 (default: false)</p>
</li>
<li><p>primaryKey - Boolean : primary key 여부 (default: false)</p>
</li>
<li><p>autoIncrement - Boolean : 자동으로 값을 1씩 증가 (default: false)</p>
</li>
</ul>
<h2 id="heading-model-2">model 예시</h2>
<pre><code class="lang-javascript">customer.init({ 
<span class="hljs-attr">id</span>: { <span class="hljs-attr">type</span>: DataTypes.INTEGER, 
    <span class="hljs-attr">allowNull</span>: <span class="hljs-literal">false</span>, 
    <span class="hljs-attr">primaryKey</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">autoIncrement</span>: <span class="hljs-literal">true</span>}, 
<span class="hljs-attr">name</span>: { <span class="hljs-attr">type</span>: DataTypes.STRING, <span class="hljs-attr">allowNull</span>: <span class="hljs-literal">false</span> }, 
<span class="hljs-attr">email</span>: { <span class="hljs-attr">type</span>: DataTypes.STRING, <span class="hljs-attr">allowNull</span>: <span class="hljs-literal">false</span> }, 
<span class="hljs-attr">phone</span>: { <span class="hljs-attr">type</span>: DataTypes.STRING, <span class="hljs-attr">allowNull</span>: <span class="hljs-literal">false</span> }, 
<span class="hljs-attr">address</span>: { <span class="hljs-attr">type</span>: DataTypes.STRING, <span class="hljs-attr">allowNull</span>: <span class="hljs-literal">true</span> } 
}, { 
sequelize, 
<span class="hljs-attr">timestamps</span>: <span class="hljs-literal">false</span>, 
<span class="hljs-attr">modelName</span>: <span class="hljs-string">'customer'</span>, 
});
</code></pre>
<h3 id="heading-7lc46rog7j6q66om">참고자료</h3>
<p><a target="_blank" href="https://sequelize.org/">https://sequelize.org/</a></p>
<p><a target="_blank" href="https://resilient-923.tistory.com/276">https://resilient-923.tistory.com/276</a></p>
]]></content:encoded></item><item><title><![CDATA[[React/Node.js] 쿠키를 이용해 refresh-token 구현]]></title><description><![CDATA[JSON 로그인 프론트엔드 + 백엔드 구현
프로젝트를 진행하면서 JWT (Json Web Token) 방식으로 로그인을 구현하기로 하였다. (JWT에 대한 설명은 생략, 이 글을 읽는 사람들은 에러를 해결하기 위해 온거겟지?)
백엔드에서 access-token은 header의 Authorization에 담아서 보내주고, refresh-token은 cookie에 담아서 보내기로 하였다. (보안 이슈 고려 body에 넣지 않음)
프론트에서는 acc...]]></description><link>https://blog.skku-comit.dev/reactnodejs-refresh-token</link><guid isPermaLink="true">https://blog.skku-comit.dev/reactnodejs-refresh-token</guid><category><![CDATA[React]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JWT]]></category><category><![CDATA[cookies]]></category><dc:creator><![CDATA[lva_jiho]]></dc:creator><pubDate>Sun, 21 Jan 2024 13:20:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705842557274/14462f3b-7bdc-463f-8bc4-c668a364f38a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-json">JSON 로그인 프론트엔드 + 백엔드 구현</h2>
<p>프로젝트를 진행하면서 JWT (Json Web Token) 방식으로 로그인을 구현하기로 하였다. <s>(JWT에 대한 설명은 생략, 이 글을 읽는 사람들은 에러를 해결하기 위해 온거겟지?)</s></p>
<p>백엔드에서 <code>access-token</code>은 <code>header</code>의 <code>Authorization</code>에 담아서 보내주고, <code>refresh-token</code>은 <code>cookie</code>에 담아서 보내기로 하였다. (보안 이슈 고려 body에 넣지 않음)</p>
<p>프론트에서는 <code>access-token</code>을 <code>localStorage</code>에 저장하고, <code>refresh-token</code>은 <code>cookie</code>에 저장하기로 하였다. (아래 표와 같음)</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td></td><td>프론트엔드(저장 위치)</td><td>백엔드(response 방식)</td></tr>
</thead>
<tbody>
<tr>
<td><strong><em>access-token</em></strong></td><td>localStorage</td><td>header.authorization</td></tr>
<tr>
<td><strong><em>refresh-token</em></strong></td><td>cookie</td><td>cookie</td></tr>
</tbody>
</table>
</div><blockquote>
<h4 id="heading-jwt">프로젝트 과정에서 해당 방식으로 JWT를 구현하기 위해 삽질했던 기록을 작성한다.</h4>
<p>똑똑한 사람들을 위해 최종 코드 먼저 제공</p>
</blockquote>
<h4 id="heading-67cx7jeu65oc">백엔드</h4>
<pre><code class="lang-jsx"><span class="hljs-keyword">return</span> res
    .status(<span class="hljs-number">403</span>)
    .cookie(<span class="hljs-string">'refreshToken'</span>, refreshToken, {
        <span class="hljs-attr">expires</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-built_in">Date</span>.now() + <span class="hljs-number">259200</span>),
        <span class="hljs-attr">httpOnly</span>: <span class="hljs-literal">true</span>,
    })
    .header(<span class="hljs-string">'Authorization'</span>, newAccessToken)
    .json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Renewed expired access token'</span> });
</code></pre>
<h4 id="heading-7zse66gg7yq47jeu65oc">프론트엔드</h4>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> API = axios.create({
  <span class="hljs-attr">baseURL</span>: <span class="hljs-keyword">import</span>.meta.env.VITE_BASE_URL,
  <span class="hljs-attr">headers</span>: {
    <span class="hljs-attr">Authorization</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"accessToken"</span>)}</span>`</span>,
  },
  <span class="hljs-attr">withCredentials</span>: <span class="hljs-literal">true</span>,
});
</code></pre>
<hr />
<hr />
<p>처음에 설계했던 방법은 다음과 같다.</p>
<h4 id="heading-7zse66gg7yq47jeu65oc-1">프론트엔드</h4>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> API = axios.create({
  <span class="hljs-attr">baseURL</span>: <span class="hljs-keyword">import</span>.meta.env.VITE_BASE_URL,
  <span class="hljs-attr">headers</span>: {
    <span class="hljs-attr">Authorization</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"accessToken"</span>)}</span>`</span>,
  },
  <span class="hljs-attr">withCredentials</span>: <span class="hljs-literal">true</span>,
});
</code></pre>
<h4 id="heading-67cx7jeu65oc-1">백엔드</h4>
<pre><code class="lang-jsx"><span class="hljs-keyword">return</span> res
  .status(<span class="hljs-number">403</span>)
  .cookie(<span class="hljs-string">"refreshToken"</span>, refreshToken, {
    <span class="hljs-attr">httpOnly</span>: <span class="hljs-literal">true</span>,
  })
  .header(<span class="hljs-string">"Authorization"</span>, newAccessToken)
  .json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Renewed expired access token"</span> });
</code></pre>
<p><code>request</code>는 간단했다. 우리는 login post를 요청하였고 <code>headers</code>에서 <code>Authorization</code> 항목을 받아오고 싶었다.</p>
<p>하지만, CORS ERROR와 마주했다.</p>
<h3 id="heading-no1-cors"><mark>[삽질 No.1] CORS 설정</mark></h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// login request code</span>
<span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> API.post(<span class="hljs-string">"/auth/login"</span>, userInput);
      <span class="hljs-comment">// access token 저장</span>
      <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">"accessToken"</span>, res.headers.get(<span class="hljs-string">"Authorization"</span>));      
    } 
<span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-comment">// status에 따른 Error Handling</span>
  <span class="hljs-keyword">const</span> errorResponse = error.response;
  <span class="hljs-keyword">const</span> statusCode = errorResponse.status;
  <span class="hljs-built_in">console</span>.log(statusCode);
  <span class="hljs-keyword">switch</span> (statusCode) {
    <span class="hljs-keyword">case</span> <span class="hljs-number">40</span>X:
      ...
      break;
  }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705842043261/590bf02c-c688-433a-b2c6-577785471e75.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-cors"><mark>❓CORS란?</mark></h4>
<p><strong>CORS</strong>를 이해하기 위해서는 <strong>SOP</strong>를 이해해야 합니다.</p>
<p>브라우저에서는 보안적인 이유로 cross-origin HTTP Request들을 제한합니다. 말 그대로, 서로 다른 origin (간단하게 url) 끼리의 HTTP Request를 제한하는 것을 의미한다.</p>
<p>이를 SOP (Same - Origin Policy)라고 합니다.</p>
<p>즉, SOP는 동일한 출처 사이에서만 리소스를 공유할 수 있다는 규칙을 지니고 있습니다.</p>
<p>→ CSRF(Cross Site Request Frogery, 크로스 사이트 요청 위조 공격)을 대비해 만들어짐 (공격자의 요청이 사용자의 요청인 것처럼 속이는 공격 방식)</p>
<p>⭐예전에는 프론트엔드 / 백엔드가 분리되지 않아서, 처리를 같은 도메인 내에서 했기 때문에 해당 정책이 효과적으로 공격들을 막아낼 수 있었다(CSRF, XSS). 하지만, 이제는 프론트엔드 / 백엔드가 분리되고, 다양한 API들을 사용하기 위해서 동일하지 않은 <strong>출처</strong> 사이에서도 리소스를 공유해야 했다.</p>
<p>이때, SOP를 지키면서 몇가지 예외사항을 적용하는 것이 CORS이다.</p>
<p>✏️<strong>출처</strong>는 Protocol + Host + Port (<a target="_blank" href="HTTPS://www.domain.com:3000">HTTPS://www.domain.com:3000)이다</a>.</p>
<p>즉, 동일하지 않은 출처 사이에서도 HTTP Request를 가능하게끔 하는 것이 CORS이다. <s>(매번 에러가 발생해서 나쁜 놈인줄 알았지만 사실은 좋은 놈이였다)</s></p>
<p>단, 모든 출처에서 접근할 수 있게 만들면 SOP가 없는것과도 같으니, ⭐<strong>가능한 URL들을 지정해주는 방법으로 최소한의 접근만 허용하는 것이다.</strong></p>
<p>해당 프로젝트에서 어떻게 적용해야 할까?</p>
<h4 id="heading-1cors">1.CORS설정으로 프론트에서는 백엔드를, 백엔드에서는 프론트엔드를 등록한다.</h4>
<ol>
<li>→ 이때, 양쪽에서 등록해야 함을 주의하자.</li>
</ol>
<pre><code class="lang-jsx"><span class="hljs-comment">// 프론트엔드 (package.json)</span>
{
  ...,
  <span class="hljs-string">"proxy"</span>: <span class="hljs-string">"http://localhost:3000"</span> <span class="hljs-comment">//백엔드 url</span>
}
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-comment">// 백엔드 (cors_config.js)</span>
<span class="hljs-keyword">const</span> cors = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cors"</span>);
<span class="hljs-built_in">exports</span>.options = cors({
  <span class="hljs-attr">origin</span>: [
    <span class="hljs-string">"http://localhost:5173"</span>, <span class="hljs-comment">// 프론트 url</span>
  ],
});
</code></pre>
<p>이렇게 했는데도, CORS Error가 발생했다.</p>
<p>→ <code>The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include’</code></p>
<h4 id="heading-2credentials-true">2.<code>credentials: true</code> 설정하기</h4>
<p><strong>⭐CORS는 기본적으로 보안상의 이유로 쿠키를 request로 보낼 수 없도록 설계되었다.</strong></p>
<p>하지만, 다른 도메인을 가진 API 서버에 자신을 인증해야 하는 정상적인 응답을 받을 수 있는 상황에서는 쿠키를 통한 인증이 필요하므로, 해당 설정을 수정해야 한다.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// 프론트엔드</span>
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> API = axios.create({
  <span class="hljs-attr">baseURL</span>: <span class="hljs-keyword">import</span>.meta.env.VITE_BASE_URL,
  <span class="hljs-attr">headers</span>: {
    <span class="hljs-attr">Authorization</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"accessToken"</span>)}</span>`</span>,
  },
  <span class="hljs-attr">withCredentials</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// 추가하기</span>
});
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-comment">// 백엔드</span>
<span class="hljs-keyword">const</span> cors = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cors"</span>);
<span class="hljs-built_in">exports</span>.options = cors({
  <span class="hljs-attr">origin</span>: [
    <span class="hljs-string">"http://localhost:5173"</span>,
  ],
  <span class="hljs-attr">credentials</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// 추가하기</span>
});
</code></pre>
<h4 id="heading-1header-authorization">1.header의 <code>Authorization</code>에 접근 권한 설정하기</h4>
<p><strong>⭐ header는 기본적으로 값들이 접근가능하지 않도록 설정되어 있는 경우가 많다.</strong></p>
<p>백엔드의 CORS 설정에서 <code>exposedHeaders</code> 값으로 노출하는 값을 변경할 수 있다.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// 백엔드</span>
<span class="hljs-keyword">const</span> cors = <span class="hljs-built_in">require</span>(<span class="hljs-string">"cors"</span>);
<span class="hljs-built_in">exports</span>.options = cors({
  <span class="hljs-attr">origin</span>: [
    <span class="hljs-string">"http://localhost:5173"</span>,
  ],
  <span class="hljs-attr">exposedHeaders</span>: [<span class="hljs-string">"Authorization"</span>],
  <span class="hljs-attr">credentials</span>: <span class="hljs-literal">true</span>,
});
</code></pre>
<blockquote>
<h3 id="heading-7lwc7kkfioy9loutna">최종 코드</h3>
</blockquote>
<p>참고로, set-Cookie에 보낸 refreshToken은 브라우저에서 자동으로 쿠키에 저장해준다.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> API.post(<span class="hljs-string">"/auth/login"</span>, userInput);      
    <span class="hljs-comment">// access token 저장</span>
    <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">"accessToken"</span>, res.headers.get(<span class="hljs-string">"Authorization"</span>));
    <span class="hljs-comment">// refresh token 저장 : 브라우저 자동</span>
    <span class="hljs-comment">// ... 유저 정보 저장 / redirect 등</span>
  } 
<span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-comment">// status에 따른 Error Handling</span>
    <span class="hljs-keyword">const</span> errorResponse = error.response;
    <span class="hljs-keyword">const</span> statusCode = errorResponse.status;
    <span class="hljs-built_in">console</span>.log(statusCode);
    <span class="hljs-keyword">switch</span> (statusCode) {
      <span class="hljs-keyword">case</span> <span class="hljs-number">404</span>:
        alert(<span class="hljs-string">"해당 이메일로된 아이가 없습니다"</span>);
        <span class="hljs-keyword">break</span>;
      <span class="hljs-keyword">case</span> <span class="hljs-number">401</span>:
        alert(<span class="hljs-string">"비밀번호가 틀렸습니다."</span>);
        <span class="hljs-keyword">break</span>;
      <span class="hljs-keyword">case</span> <span class="hljs-number">400</span>:
        alert(<span class="hljs-string">"Bad request (요청 형식이 잘못됨)"</span>);
        <span class="hljs-keyword">break</span>;
    }
  }
</code></pre>
<h3 id="heading-no2"><mark>[삽질 No.2] 로그아웃 기능</mark></h3>
<p>로그아웃 기능을 구현할때, ✏️<strong>access Token과 refresh Token을 삭제</strong>하고 싶다.</p>
<p>access Token은 localStorage에 있으므로, 쉽게 삭제 가능하다.</p>
<p><strong>😡문제는 refresh Token이다.</strong></p>
<p>쿠키에 있는 refresh token에 접근해서 삭제하고 싶었다.</p>
<p>암만 찾아봐도 방법이 없었다. 실제로 불가능했기 때문이다.</p>
<p><strong>⭐백엔드에서 refresh Token을 쿠키에 담아서 보낼때</strong> <code>httpOnly:true</code> <strong>를 달아서 보냈는데, 해당 속성을 달아주면 프론트에서 암만 노력해도 접근할 수 없다. 일부러 막아둔 것이다.</strong></p>
<p>잘 생각해보면 저장할 때도 자동으로 저장하는 이유가, 프론트에서 접근하지 못하니까 친절하게 알아서 저장해주는 것이였다.</p>
<p>❓그러면.. 어떻게 삭제해???</p>
<p>쿠키에는 2가지 종류가 있었다.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>세션 쿠키</td><td>사용자가 사이트 탐색 시에 관련한 설정들과 선호사항을 저장하는 임시 쿠키정도라고 생각하면 된다. 브라우저를 닫는 순간 삭제된다.</td></tr>
</thead>
<tbody>
<tr>
<td>지속 쿠키</td><td>지속쿠키는 세션쿠키와 다르게 삭제되지 않고 더 길게 유지가 가능하다. 지속쿠키는 디스크에 저장되며, 브라우저를 닫거나 컴퓨터를 재시작해도 남아닜다. 사용자 로그인 항상 유지와 같은 곳에 사용한다</td></tr>
<tr>
<td>차이점</td><td>세션쿠키와 지속쿠키는 파기시점 외에 차이점이 없다. Discard라는 파라미터가 설정되어 있거나, 파기까지 남은시간인 Expires또는 Max-Age라는 파라미터가 없으면 세션쿠키이다.</td></tr>
</tbody>
</table>
</div><p>즉, 우리는 지금까지 세션 쿠키를 사용하였지만, <code>httpOnly</code> 속성으로 인해 직접 쿠키를 제어할 수 없으므로, <strong>지속 쿠키</strong>를 생성하기로 하였다.</p>
<p>즉, <code>refresh-token</code>을 삭제하는 대신 ⭐<strong>만료기간을 설정하는 방식</strong>으로 진행하였다.</p>
<p>백엔드분들한테 <code>refreshToken</code>의 기간을 설정해달라고 요청하였다.</p>
<pre><code class="lang-tsx">return res
    .status(403)
    .cookie('refreshToken', refreshToken, {
        expires: new Date(Date.now() + 259200), // 기간 설정
        httpOnly: true,
    })
    .header('Authorization', newAccessToken)
    .json({ message: 'Renewed expired access token' });
</code></pre>
<p>지금까지, JWT를 구현할 때 삽질했던 기록들을 정리했습니다.</p>
<p>나중에 또 삽질할때 봐야겠다 :)</p>
<hr />
<p>개인 블로그: <a target="_blank" href="https://velog.io/@jihostudy/posts">https://velog.io/@jihostudy/posts</a></p>
<p>edited by 김지호</p>
]]></content:encoded></item><item><title><![CDATA[C/C++ 연결 리스트(Linked List)-단순 연결 리스트]]></title><description><![CDATA[개요
연결 리스트의 기본이 되는 단순 연결 리스트에 대해 알아보자. 단순 연결 리스트는 다음 노드에 대한 참조만을 가진 가장 단순한 형태의 연결 리스트이다. 가장 마지막 원소를 찾으려면 리스트 끝까지 찾아가야 하기 때문에(O(n)), 마지막 원소를 가리키는 참조를 따로 가지는 형태의 변형도 있다. 또한, 한 번 연결이 끊기거나, 체인이 잘 못된 경우, 모든 뒤의 데이터가 유실되는 단점이 있다.
Node구성
struct Node
{
    int...]]></description><link>https://blog.skku-comit.dev/cc-linked-list</link><guid isPermaLink="true">https://blog.skku-comit.dev/cc-linked-list</guid><category><![CDATA[C++]]></category><category><![CDATA[data structures]]></category><category><![CDATA[#linkedlists]]></category><dc:creator><![CDATA[Gon Tae]]></dc:creator><pubDate>Fri, 19 Jan 2024 08:50:16 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-6rcc7jqu">개요</h3>
<p>연결 리스트의 기본이 되는 단순 연결 리스트에 대해 알아보자. 단순 연결 리스트는 다음 노드에 대한 참조만을 가진 가장 단순한 형태의 연결 리스트이다. 가장 마지막 원소를 찾으려면 리스트 끝까지 찾아가야 하기 때문에(<a target="_blank" href="https://namu.wiki/w/%EC%A0%90%EA%B7%BC%20%ED%91%9C%EA%B8%B0%EB%B2%95">O(n)</a>), 마지막 원소를 가리키는 참조를 따로 가지는 형태의 변형도 있다. 또한, 한 번 연결이 끊기거나, 체인이 잘 못된 경우, 모든 뒤의 데이터가 유실되는 단점이 있다.</p>
<h3 id="heading-node">Node구성</h3>
<pre><code class="lang-cpp"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Node</span>
{</span>
    <span class="hljs-keyword">int</span> data;
    Node* next;
} *first = <span class="hljs-literal">NULL</span>;

Node* last = <span class="hljs-literal">NULL</span>;
</code></pre>
<p>first는 노드의 시작을, last는 노드의 끝을 나타낸다. struct로 생성한 Node는 실제 요소 값을 담는 data와 다음 노드를 향한 주소 값을 담는 포인터로 구성된다.</p>
<h3 id="heading-create">create함수-연결 리스트 생성</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">create</span><span class="hljs-params">(<span class="hljs-keyword">int</span> A[], <span class="hljs-keyword">int</span>  n)</span>
</span>{
    Node* t;
    first = <span class="hljs-keyword">new</span> Node;
    first-&gt;data = A[<span class="hljs-number">0</span>];
    first-&gt;next = <span class="hljs-literal">NULL</span>;
    last = first;

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span>; i &lt; n; i++)
    {
        t = <span class="hljs-keyword">new</span> Node;
        t-&gt;data = A[i];
        t-&gt;next = <span class="hljs-literal">NULL</span>;
        last-&gt;next = t;
        last = t;
    }
}
</code></pre>
<p>first와 last에 대한 노드 할당을 먼저 한다. 하나의 노드이므로 first와 last가 일치하며, last의 next 노드는 없으므로 NULL이다. for루프에서 루프마다 새로운 노드를 힙에 생성하여, data에 요소 값을 넣은 뒤, last의 next 참조로 이어서 연결 리스트의 끝에 추구한다. 새로운 노드마다 끝에 붙으면 last가 되므로, 루프마다 last의 위치를 옮기는 작업이 필요하다.(last = t;)</p>
<h3 id="heading-count">Count함수-요소의 개수 계산</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">Count</span><span class="hljs-params">(Node* t)</span>
</span>{
    <span class="hljs-comment">//if (t == 0)</span>
    <span class="hljs-comment">//    return 0;</span>
    <span class="hljs-comment">//return Count(t-&gt;next) + 1;</span>

    <span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">if</span> (t)
    {
        x = Count(t-&gt;next) + <span class="hljs-number">1</span>;
        <span class="hljs-keyword">return</span> x;
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-keyword">return</span> x;
    }
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">lCOunt</span><span class="hljs-params">(Node* t)</span>
</span>{
    <span class="hljs-keyword">int</span> l = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">while</span> (t)
    {
        l++;
        t = t-&gt;next;
    }
    <span class="hljs-keyword">return</span> l;
}
</code></pre>
<p>재귀함수를 사용한 버전과 루프를 활용한 버전이 있다. 둘 다 동일하게, 각 노드를 p-&gt;next를 통해 순차적으로 접근(traversing)하며, NULL(연결 리스트의 끝)이 아닐 경우, count를 1씩 늘려 반환한다. 일반적으로, t= t-&gt;next;를 통해 다음 노드에 접근한다.</p>
<h3 id="heading-max">Max함수-요소 들 중 가장 큰 값을 반환</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">Max</span><span class="hljs-params">(Node* p)</span>
</span>{
    <span class="hljs-keyword">int</span> x = INT32_MIN;
    <span class="hljs-keyword">while</span> (p)
    {
        <span class="hljs-keyword">if</span> (p-&gt;data &gt; x)
            x = p-&gt;data;
        p = p-&gt;next;
    }
    <span class="hljs-keyword">return</span> x;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">RMax</span><span class="hljs-params">(Node* p)</span>
</span>{
    <span class="hljs-keyword">int</span> x = INT_MIN;
    <span class="hljs-keyword">if</span> (p == <span class="hljs-literal">NULL</span>)
        <span class="hljs-keyword">return</span> x;
    x = RMax(p-&gt;next);
    <span class="hljs-keyword">return</span> (p-&gt;data &gt; x) ? p-&gt;data : x;

}
</code></pre>
<p>루프를 이용한 구현은 그저 요소를 traversing하며 큰 값이 있으면 x에 담는 방식이며, 재구 방식을 살펴보면, 주요한 계산인 (p-&gt;data &gt; x) ? p-&gt;data : x; 전에 재귀함수를 호출하여, 함수의 반환을 노드의 끝으로 미룬 뒤, 노드가 되돌아오며, 그 전의 요소의 값과 현재 노드의 값 중 큰 것을 반환한다.</p>
<h3 id="heading-display">Display함수-모든 노드의 요소를 출력</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Display</span><span class="hljs-params">(Node* t)</span>
</span>{
    <span class="hljs-keyword">while</span> (t != <span class="hljs-literal">NULL</span>)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d "</span>, t-&gt;data);
        t = t-&gt;next;
    }
}
</code></pre>
<p>while루프로 노드의 끝까지 진행하며, data를 printf한다.</p>
<h3 id="heading-search">Search함수-해당 값을 가진 노드 반환</h3>
<pre><code class="lang-cpp"><span class="hljs-comment">// move to head, q를 꼬리 포인터, p를 헤드 포인터로해서 전 후의 노드를 알 수 있다.</span>
<span class="hljs-function">Node* <span class="hljs-title">Search</span><span class="hljs-params">(Node* p, <span class="hljs-keyword">int</span> key)</span>
</span>{
    Node* q = <span class="hljs-literal">NULL</span>;

    <span class="hljs-keyword">while</span> (p)
    {
        <span class="hljs-keyword">if</span> (key == p-&gt;data)
        {
            q-&gt;next = p-&gt;next;
            p-&gt;next = first;
            first = p;
            <span class="hljs-keyword">return</span> p;
        }
        q=p;
        p = p-&gt;next;
    }
}
</code></pre>
<p>이 함수부터 특이하게, 두 개의 포인터를 사용한다. 머리 포인터와 꼬리 포인터로, 노드 사이를 옮겨 다니며, 해당 노드와 방금 전에 접근한 노드를 담는다. 일반적인 search는 그냥 하나의 포인터를 이용해 노드를 옮겨 다니며, 해당 노드의 data가 key와 일치하면 그 노드를 반환하면 된다. 하지만, 위에 구현한 것은 해당 key를 search 한 다음, 그 노드를 가장 맨 앞으로 옮겨와 다음 같은 key를 search할 때, 바로 첫 노드에서 반환할 수 있도록 한 "효율적인 search"방식이다.</p>
<pre><code class="lang-cpp">q-&gt;next = p-&gt;next;
</code></pre>
<p>옮기려는 key와 일치하는 노드가 p이며, 그 전 노드의 next를 p가 아닌 p의 다음 노드로 연결하여, 노드 p를 연결 리스트에서 뺀다.</p>
<pre><code class="lang-cpp">p-&gt;next = first;
p = first;
</code></pre>
<p>p의 다음 노드를 first로 하고, first를 p로 설정함으로써 p를 가장 맨 앞의 노드로 설정한다.</p>
<p>이 방식으로 다음 같은 key의 search를 한 번에 끝낼 수 있게 된다.</p>
<h3 id="heading-insert">Insert함수-원하는 위치에 새로운 노드 추가</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Insert</span><span class="hljs-params">(Node* p, <span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> index)</span>
</span>{
    Node* t;
    <span class="hljs-keyword">if</span> (index &lt; <span class="hljs-number">0</span> || index &gt; Count(p))
        <span class="hljs-keyword">return</span>;

    t = <span class="hljs-keyword">new</span> Node;
    t-&gt;data = x;

    <span class="hljs-keyword">if</span> (index == <span class="hljs-number">0</span>)
    {
        t-&gt;next = first;
        first = t;
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span> ; i &lt; index - <span class="hljs-number">1</span>; i++)
        {
            p = p-&gt;next;
        }
        t-&gt;next = p-&gt;next;
        p-&gt;next = t;
    }
}
</code></pre>
<p>노드를 insert하는 경우는 크게 둘로 나뉜다. 맨 앞에 추가하는 경우와 그 외의 원하는 위치에 삽입하는 경우이다. 먼저 맨 앞에 삽입하는 방식은 새로운 노드로의 first의 이동이 필요하하다.</p>
<pre><code class="lang-cpp">     <span class="hljs-keyword">if</span> (index == <span class="hljs-number">0</span>)
    {
        t-&gt;next = first;
        first = t;
    }
</code></pre>
<p>원하는 위치에 삽입하는 경우는 해당 index로 노드 이동을 한 뒤,(여기서 index는 맨 처음 노드가 1일 때의 index이다). 그 index의 노드 뒤에 새로운 노드를 추가한다.( 1 2 3의 노드 배열에 index = 3에 추가하면 1 2 3 new node가 된다.)</p>
<pre><code class="lang-cpp">        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span> ; i &lt; index - <span class="hljs-number">1</span>; i++)
        {
            p = p-&gt;next;
        }
        t-&gt;next = p-&gt;next;
        p-&gt;next = t;
</code></pre>
<h3 id="heading-sortedinsert">SortedInsert함수-값의 순서대로 노드를 삽입</h3>
<p>이미 오름차순에 따라 배열된 연결 리스트에서 값을 가진 새로운 노드를 적절한 위치에 삽입한다.</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">SortedInsert</span><span class="hljs-params">(Node* p, <span class="hljs-keyword">int</span> x)</span>
</span>{
    Node* t, * q = <span class="hljs-literal">NULL</span>;
    t = <span class="hljs-keyword">new</span> Node;
    t-&gt;data = x;
    t-&gt;next = <span class="hljs-literal">NULL</span>;

    <span class="hljs-keyword">if</span> (first == <span class="hljs-literal">NULL</span>)
        first = t;
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-keyword">while</span> (p &amp;&amp; p-&gt;data &lt; x)
        {
            q = p;
            p = p-&gt;next;
        }
        <span class="hljs-keyword">if</span> (p == first)
        {
            t-&gt;next = first;
            first = t;
        }
        <span class="hljs-keyword">else</span>
        {
            t-&gt;next = q-&gt;next;
            q-&gt;next = t;
        }
    }
}
</code></pre>
<p>두 개의 포인터를 이용해 전 후의 노드를 할당하고, p-&gt;data가 원하는 값 x보다 큰 경우까지 traversing한다.</p>
<pre><code class="lang-cpp">        <span class="hljs-keyword">while</span> (p &amp;&amp; p-&gt;data &lt; x)
        {
            q = p;
            p = p-&gt;next;
        }
</code></pre>
<p>q에 p의 노드를 할당하고, p는 한 칸 뒤로 이동함으로써 연속적인 노드를 할당 받게 된다.</p>
<pre><code class="lang-cpp">        <span class="hljs-keyword">if</span> (p == first)
        {
            t-&gt;next = first;
            first = t;
        }
        <span class="hljs-keyword">else</span>
        {
            t-&gt;next = q-&gt;next;
            q-&gt;next = t;
        }
</code></pre>
<p>p가 first노드 즉, 한 개의 노드밖에 없는 연결 리스트면 새로 생성한 노드를 앞에 추가하고, first를 해당 노드로 바꾼다.</p>
<p>그 외에는 새로운 노드를 p의 앞에 추가하므로, p 앞의 노드, q의 다음 포인터를 t로 설정하고, t의 다음 노드를 p로 설정한다.</p>
<h3 id="heading-delete">Delete함수-원하는 노드 삭제</h3>
<p>원하는 index의 노드를 삭제하고, 삭제하는 노드의 data를 반환한다.</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">Delete</span><span class="hljs-params">(Node* p, <span class="hljs-keyword">int</span> index)</span>
</span>{
    Node* q = <span class="hljs-literal">NULL</span>;
    <span class="hljs-keyword">int</span> x = <span class="hljs-number">-1</span>;

    <span class="hljs-keyword">if</span> (index &lt; <span class="hljs-number">1</span> || index &gt; Count(p))
        <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
    <span class="hljs-keyword">if</span> (index == <span class="hljs-number">1</span>)
    {
        q = first;
        x = first-&gt;data;
        first = first-&gt;next;
        <span class="hljs-keyword">delete</span> q;
        <span class="hljs-keyword">return</span> x;
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; index - <span class="hljs-number">1</span> &amp;&amp; p; i++)
        {
            q = p;
            p = p-&gt;next;
        }
        q-&gt;next = p-&gt;next;
        x = p-&gt;data;
        <span class="hljs-keyword">delete</span> p;
        <span class="hljs-keyword">return</span> x;
    }
}
</code></pre>
<p>이 것도 동일하게, 맨 처음의 노드를 삭제하는가, 중간에 껴있는 노드를 삭제 하는 경우에 따라 나뉜다. 첫 노드를 삭제하는 경우,</p>
<pre><code class="lang-cpp">    <span class="hljs-keyword">if</span> (index == <span class="hljs-number">1</span>)
    {
        q = first;
        x = first-&gt;data;
        first = first-&gt;next;
        <span class="hljs-keyword">delete</span> q;
        <span class="hljs-keyword">return</span> x;
    }
</code></pre>
<p>첫 노드의 data를 저장하여 반환하며, first를 한 칸 뒤의 노드로 옮긴 후, 해당 노드를 삭제한다.</p>
<pre><code class="lang-cpp">        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; index - <span class="hljs-number">1</span> &amp;&amp; p; i++)
        {
            q = p;
            p = p-&gt;next;
        }
        q-&gt;next = p-&gt;next;
        x = p-&gt;data;
        <span class="hljs-keyword">delete</span> p;
        <span class="hljs-keyword">return</span> x;
</code></pre>
<p>꼬리, 머리 포인터를 통해 해당 index의 노드로 움직인 뒤, p의 노드를 빼기 위해, 꼬리 포인터(q)의 다음 노드를 p 다음의 노드로 설정하여, p를 향한 연결 고리를 끊는다. 그 후, p의 data를 반환하고, 메모리 해제한다.</p>
<h3 id="heading-reverse">Reverse함수-연결 리스트의 순서 반전</h3>
<p>연결 리스트의 요소 연결 순서를 뒤집는 함수이다. 방식은 크게 두 가지이다. 포인터의 연결은 그대로 놓고, 요소의 값을 임의의 배열에 저장하고, 값 자체를 뒤집어서 새롭게 저장하는 방식이 있고, 포인터의 연결 순서 자체를 바꾸는 방식(노드의 next가 다음 노드가 아니라, 그 전 노드를 향하도록 하는 방식)이 있다. 아래의 Reverse1은 값을 바꾸고, Reverse2는 포인터를 바꾼다. 일반적으로, pointer 변경 방식이 더 효율적이다.</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Reverse1</span><span class="hljs-params">(Node* p)</span>
</span>{
    <span class="hljs-keyword">int</span>* A, i = <span class="hljs-number">0</span>;
    Node* q = p;
    A = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[Count(p)];
    <span class="hljs-keyword">while</span> (q!= <span class="hljs-literal">NULL</span>)
    {
        A[i] = q-&gt;data;
        q = q-&gt;next;
        i++;
    }
    i--;
    <span class="hljs-keyword">while</span> (q!= <span class="hljs-literal">NULL</span>)
    {
        q-&gt;data = A[i];
        q = q-&gt;next;
        i--;
    }
}
</code></pre>
<p>A라는 새로운 배열을 생성하여, 임시로 연결 리스트의 요소 값들을 저장한다. 그 후, 거꾸로 루프를 돌며 노드 이동을 하며, 각각의 노드에 배열 값을 넣는다.</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Reverse2</span><span class="hljs-params">(Node* p)</span>
</span>{
    Node* q = <span class="hljs-literal">NULL</span>, * r = <span class="hljs-literal">NULL</span>;
    <span class="hljs-keyword">while</span> (p!= <span class="hljs-literal">NULL</span>)
    {
        r = q;
        q = p;
        p = p-&gt;next;
        q-&gt;next = r;
    }
    first = q;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Reverse3</span><span class="hljs-params">(Node* q, Node* p)</span>
</span>{
    <span class="hljs-keyword">if</span> (p)
    {
        Reverse3(p, p-&gt;next);
        p-&gt;next = q;
    }
    <span class="hljs-keyword">else</span>
        first = q;
}
</code></pre>
<p>3개의 포인터를 사용하고, 3개의 포인터는 연속적인 노드를 나타낸다. 그리고, 그 3개의 포인터는 p=p-&gt;next를 통해 미끄러지듯 순차적으로 뒤의 노드로 이동한다. 이를 sliding기법이라고 한다.</p>
<pre><code class="lang-cpp">        r = q;
        q = p;
        p = p-&gt;next;
</code></pre>
<p>이러한 방식으로, sliding한다. r이 첫 노드, q가 그 다음 노드, p가 마지막 노드이며 순차적이다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705651037606/74c111cb-eea8-48df-b676-5bcb8a6eba86.jpeg" alt class="image--center mx-auto" /></p>
<p>각 루프마다 q-&gt;next를 그 전의 노드인 r로 향하게 함으로써 노드의 포인터 연결을 뒤집는다. 왜 3개의 포인터가 필요한지 헷갈릴 수 있다. r은 당연하게도 연결을 뒤집을 그 전 노드이므로 필요하다. p는 다음 노드로의 이동을 위해 필요하다. q의 경우에는 루프마다 next가 r로 바뀌므로, q를 통해 다음 노드로 이동하려고 하면, 제대로 된 방향으로 움직이지 못한다. 그렇기에 p를 통해 노드 이동, q,r을 이용해 노드 사이의 연결을 뒤집는다.</p>
<p>Reverse3 함수는 같은 함수의 구현을 재귀 함수로 표현한 것 뿐이다.</p>
<h3 id="heading-merge">Merge함수-연결 리스트의 병합</h3>
<p>연결 리스트의 병합은 쉽다. 하지만, 오름차순으로 나열된 두 개의 연결 리스트를 병합하려면 방법이 꽤 복잡하다.</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Merge</span><span class="hljs-params">(Node* p, Node* q)</span>
</span>{
    Node* last;
    Node* third;
    <span class="hljs-keyword">if</span> (p-&gt;data &lt; q-&gt;data)
    {
        third = last = p;
        p = p-&gt;next;
        third-&gt;next = <span class="hljs-literal">NULL</span>;
    }
    <span class="hljs-keyword">else</span>
    {
        third = last = q;
        q = q-&gt;next;
        third-&gt;next = <span class="hljs-literal">NULL</span>;
    }
    <span class="hljs-keyword">while</span> (p &amp;&amp; q)
    {
        <span class="hljs-keyword">if</span> (p-&gt;data &lt; q-&gt;data)
        {
            last-&gt;next = p;
            last = p;
            p = p-&gt;next;
            last-&gt;next = <span class="hljs-literal">NULL</span>;
        }
        <span class="hljs-keyword">else</span>
        {
            last-&gt;next = q;
            last = q;
            q = q-&gt;next;
            last-&gt;next = <span class="hljs-literal">NULL</span>;
        }
    }
    <span class="hljs-keyword">if</span> (p)
        last-&gt;next = p;
    <span class="hljs-keyword">if</span> (q)
        last-&gt;next = q;
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705652517360/0afcddb0-62de-479e-9c57-00b4877d1319.jpeg" alt class="image--center mx-auto" /></p>
<p>그림에서 보듯이, 두 개의 연결 리스트에 대해 노드 포인터 p와 q를 이용해 p와 q의 노드 값을 비교해 작은 노드에 last-&gt;next를 통해 연결을 만들고, 해당 p또는 q 노드를 한 칸 뒤로 옮기는 방식을 사용한다. 마지막에는 하나 남은 노드를 연결하여 마무리한다.</p>
<pre><code class="lang-cpp">    <span class="hljs-keyword">if</span> (p-&gt;data &lt; q-&gt;data)
    {
        third = last = p;
        p = p-&gt;next;
        third-&gt;next = <span class="hljs-literal">NULL</span>;
    }
    <span class="hljs-keyword">else</span>
    {
        third = last = q;
        q = q-&gt;next;
        third-&gt;next = <span class="hljs-literal">NULL</span>;
    }
</code></pre>
<p>첫 노드 두 개를 비교하여, 작은 쪽을 third라는 새로운 연결 리스트의 시작을 할당한다.</p>
<pre><code class="lang-cpp">    <span class="hljs-keyword">while</span> (p &amp;&amp; q)
    {
        <span class="hljs-keyword">if</span> (p-&gt;data &lt; q-&gt;data)
        {
            last-&gt;next = p;
            last = p;
            p = p-&gt;next;
            last-&gt;next = <span class="hljs-literal">NULL</span>;
        }
        <span class="hljs-keyword">else</span>
        {
            last-&gt;next = q;
            last = q;
            q = q-&gt;next;
            last-&gt;next = <span class="hljs-literal">NULL</span>;
        }
    }
</code></pre>
<p>p와 q의 노드가 끝날 때까지 계속 비교하며, 값이 작은 쪽으로 last-&gt;next로 연결을 만들고, 그 노드에 last를 할당한다.</p>
<h3 id="heading-isloop">IsLoop함수-연결 리스트가 루프 인지 파악</h3>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">isLoop</span><span class="hljs-params">(Node* f)</span>
</span>{
    Node* p, * q;

    p = q = f;
    <span class="hljs-keyword">do</span>
    {
        p = p-&gt;next;
        q = q-&gt;next;
        q = q ? q-&gt;next : q;

    } <span class="hljs-keyword">while</span> (p &amp;&amp; q &amp;&amp; p != q);

    <span class="hljs-keyword">if</span> (p == q)
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    <span class="hljs-keyword">else</span>
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>무언가가 루프인지 끝이 있는지는 이 방법으로 쉽게 알 수 있다. 예를 들어, 원형 트랙에 두 명의 사람이 뛰고 있고, 각 사람이 뛰는 속도가 다른 경우를 보자. 만약 트랙이 일자라면 두 사람이 만날 일은 없다. 하지만, 원형이라면 언젠가 속도가 빠른 사람이 속도가 느린 사람을 다시 만나고 추월하게 된다. 이를 활용하여 루프마다 p는 한 번만 이동, q는 두 번 이동함으로써 언젠가 p와q가 만나는 지를 파악한다.</p>
<h3 id="heading-66ei66y066as">마무리</h3>
<p>이번 글에서는 단순 연결 리스트가 무엇 인지부터 해서, 단순 연결 리스트에 필요한 다양한 함수를 구현해 보았다. 모든 함수에서 알 수 있듯이 루프를 통해 처음 노드에서 필요한 노드 또는 끝까지 매 번 traversing을 해야 했다. 구현이 단순해 지기는 하지만, 탐색이나, 조정에서 시간이 걸리기 때문에 효율적인 연결 리스트라고 하기 어려울 수 있다. 다음 글에서는 C++로 이 함수들을 구현해보고자 한다.</p>
]]></content:encoded></item><item><title><![CDATA[C/C++ 연결 리스트(Linked List) (1)]]></title><description><![CDATA[개요
추상적 자료형인 리스트를 구현한 자료구조로, 일반적인 리스트는 stack 또는 heap에 연속적인 메모리 구조에 데이터를 집어넣는 방식이다. 반면에, 연결 리스트는 리스트의 요소를 담고 있는 데이터 덩어리인 Node를 기준으로, Node를 포인터를 이용해 데이터를 이어서 구현한 리스트이다. Node에는 다음의 요소 Node를 향한 포인터를 담고 있는 것이 큰 특징이다. 예를 들어 한 반에 있는 학생들의 자료를 저장한다면, 학생 하나하나의 ...]]></description><link>https://blog.skku-comit.dev/cc-linked-list-1</link><guid isPermaLink="true">https://blog.skku-comit.dev/cc-linked-list-1</guid><category><![CDATA[C++]]></category><category><![CDATA[data structures]]></category><dc:creator><![CDATA[Gon Tae]]></dc:creator><pubDate>Thu, 18 Jan 2024 15:05:44 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-6rcc7jqu">개요</h3>
<p>추상적 자료형인 리스트를 구현한 자료구조로, 일반적인 리스트는 stack 또는 heap에 연속적인 메모리 구조에 데이터를 집어넣는 방식이다. 반면에, 연결 리스트는 리스트의 요소를 담고 있는 데이터 덩어리인 Node를 기준으로, Node를 포인터를 이용해 데이터를 이어서 구현한 리스트이다. Node에는 다음의 요소 Node를 향한 포인터를 담고 있는 것이 큰 특징이다. 예를 들어 한 반에 있는 학생들의 자료를 저장한다면, 학생 하나하나의 신상명세 자료를 노드로 만들고, 1번 학생의 신상명세 자료에 2번 학생 신상명세가 어디있는지 표시를 해 놓는 방식이다. 쉽게 생각하면 자료를 비엔나 소시지마냥 줄줄이 엮어놓은 것이다.</p>
<h3 id="heading-vs">연결 리스트 vs 리스트</h3>
<p>연결 리스트</p>
<ul>
<li><p>데이터 길이에 있어 유연하다. 데이터를 넣을 필요가 있을 때, heap공간에 노드를 생성해 잇기만 하면 된다.</p>
</li>
<li><p>heap공간에만 생성할 수 있다. 포인터를 사용하기 때문에 stack에 저장하지 않는다.</p>
</li>
<li><p>중간에 데이터를 삽입, 데이터 삭제가 용이하다. 노드를 생성해서 이어 붙이거나, heap에서 메모리를 해제하기만 하면 된다.</p>
</li>
<li><p>데이터에 접근하기 위해서는 무조건 처음 노드부터 포인터를 통해 traversing해야 하기 때문에 데이터 탐색에 있어 일반 리스트에 비해 속도/효율이 좋지 않다.</p>
</li>
</ul>
<p>리스트</p>
<ul>
<li><p>데이터를 stack에 생성하거나 heap 생성할 수 있으며, 연속적인 메모리를 차지한다.</p>
</li>
<li><p>데이터 길이를 처음 선언할 때 정할 수 있어, 동적으로 데이터의 양이 변할 때, 리스트의 공간이 남거나, 부족한 현상이 발생한다.</p>
</li>
<li><p>데이터를 중간에 삽입하거나, 삭제할 때, 생성, 삭제 후, 인덱스를 변화시켜야 하는 불편함이 있다.</p>
</li>
<li><p>데이터 접근이 용이하다. 인덱스로 접근하면, 코드 한 줄로 접근할 수 있어 속도/효율 면에서 좋다.</p>
</li>
</ul>
<h3 id="heading-7jew6rkwioumroykpo2kuoydmcdsooxrpzg">연결 리스트의 종류</h3>
<p>노드 끼리의 연결 구조에 따라 3가지 종류로 나뉜다.</p>
<ol>
<li><p>단순 연결 리스트(Singly Linked List)</p>
</li>
<li><p>이중 연결 리스트(Doubly Linked List)</p>
</li>
<li><p>순환 연결 리스트(Circular Linked List)</p>
</li>
</ol>
<p>단순 연결 리스트는 데이터를 한 방향으로만 연결하여, 처음과 끝이 존재하는 형태이다.</p>
<p>이중 연결 리스트는 노드가 양 방향으로 연결되어 있다. 순환 연결 리스트는 처음과 끝이 이어져서 끝의 다음 포인터가 처음이 되는 형태이다.</p>
<h3 id="heading-7jew6rkwioumroykpo2kuoydmcdtlytsmpqg6rws7zieioycro2vrq">연결 리스트의 필요 구현 사항</h3>
<ol>
<li><p>Node</p>
</li>
<li><p>create함수-연결 리스트 생성</p>
</li>
<li><p>Display함수-연결 리스트 요소 프린트</p>
</li>
<li><p>Count함수-리스트의 요소의 수 계산</p>
</li>
<li><p>Search함수-해당 요소의 위치/노드 반환</p>
</li>
<li><p>Insert함수-요소를 추가로 필요한 위치에 추가</p>
</li>
<li><p>Delete함수-지우고 싶은 요소를 삭제</p>
</li>
<li><p>Reverse함수-요소의 순서를 반전</p>
</li>
</ol>
<p>추가적인 함수는 해당 리스트 유형에 따라 유연하게 작성해볼 것이며, 위의 함수들이 필수적인 함수들이라고 생각된다.</p>
<h3 id="heading-7jew6rkwioumroykpo2kuoydmcdquldrs7jsoihsnbgg6rws7kgw">연결 리스트의 기본적인 구조</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705590190050/c77bca5b-4a69-448f-bcaa-9277034eeab8.webp" alt class="image--center mx-auto" /></p>
<p>위의 사진은 연결 리스트의 가장 기초적인 구조를 시각화 한 것이다. 앞으로의 구현은 위의 그림을 기반으로 작성하며, 함수 구현마다 원리를 그림으로 모두 그려 올리지 못하기에 위의 사진을 참고하여 코드를 분석해주길 바란다.</p>
<p>다음 글에서, 단순 연결 리스트부터 순차적으로 구현해보도록 하겠다.</p>
]]></content:encoded></item><item><title><![CDATA[[Flutter] Riverpod - Performing side effect]]></title><description><![CDATA[본 포스트는 Riverpod 패키지 공식 문서 중 performing side effects 관련 내용을 번역하며 공부한 내용이며, 번역 중 누락된 부분이 존재하며 개인적인 생각이 포함되어있습니다.
Performing side effects
단순한 GET HTTP request의 경우는 사용하기가 쉽고 복잡하지 않다. 하지만 POST request와 같은 side-effects가 발생하는 경우는 어떻게 처리해야할까?
어플리케이션에서는 CRUD를...]]></description><link>https://blog.skku-comit.dev/flutter-riverpod-performing-side-effect</link><guid isPermaLink="true">https://blog.skku-comit.dev/flutter-riverpod-performing-side-effect</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Riverpod]]></category><category><![CDATA[State Management ]]></category><dc:creator><![CDATA[Donghan Kim]]></dc:creator><pubDate>Thu, 18 Jan 2024 04:06:31 GMT</pubDate><content:encoded><![CDATA[<p>본 포스트는 Riverpod 패키지 공식 문서 중 performing side effects 관련 내용을 번역하며 공부한 내용이며, 번역 중 누락된 부분이 존재하며 개인적인 생각이 포함되어있습니다.</p>
<h1 id="heading-performing-side-effects">Performing side effects</h1>
<p>단순한 GET HTTP request의 경우는 사용하기가 쉽고 복잡하지 않다. 하지만 POST request와 같은 side-effects가 발생하는 경우는 어떻게 처리해야할까?</p>
<p>어플리케이션에서는 CRUD를 모두 구현하는 경우가 많으며, 업데이트가 발생하는 경우 local cache 또한 업데이트하여 UI에 새로운 state를 반영해야한다. 즉, POST요청을 통해 특정 데이터에 변화가 발생하면, 새로 수정된 최신의 데이터를 클라이언트에도 동기화 해주는 작업이 필요하다.</p>
<p>consumer 내 provider의 state는 어떻게 업데이트해야할까? Riverpod에서는 provider가 state를 수정하는 방법을 제공하지 않는다.</p>
<p>이러한 처리를 위해서는 단순한 Provider를 사용하지 않고 Notifier를 활용해야한다. Notifier는 provider의 stateful widget이라고 할 수 있다. Notifier를 작성하는 코드는 다음과 같다.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// global class which will create notifier provider automatically</span>
<span class="hljs-comment">// using code generation</span>
<span class="hljs-meta">@riverpod</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyNotifier</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">_</span>$<span class="hljs-title">MyNotifier</span> </span>{
    <span class="hljs-meta">@override</span>
    Result build(){
        <span class="hljs-comment">// your logic here</span>
    }

    <span class="hljs-comment">// your method (change states) here</span>
}
</code></pre>
<ul>
<li>모든 Notifier는 반드시 <code>build()</code>매서드를 오버라이드해야한다. non-Notifier에서 사용하던 provider 생성 관련 로직을 해당 매서드 내에 작성한다.</li>
</ul>
<p>본론으로 돌아와서, POST 요청 이후 새로운 데이터를 클라이언트에 적용하기 위해서 기존 state는 어떻게 업데이트를 해야할까? 이를 위해서는 다양한 방법을 사용할 수 있다. 총 세가지의 방법을 알아보며 장단점을 알아보자.</p>
<h3 id="heading-updating-our-local-cache-to-match-the-api-response">Updating our local cache to match the API response</h3>
<p>첫번째 방법은 말그대로 POST 요청에 대한 응답값 자체가 최신 데이터인 경우이다. 만약 서버 로직에서 리턴 값으로 수정된 데이터를 보내준다면 이를 받아서 기존에 있던 provider state에 저장하면 된다.</p>
<pre><code class="lang-dart">
Future&lt;<span class="hljs-keyword">void</span>&gt; addTodo(Todo todo) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.post(
        <span class="hljs-built_in">Uri</span>.https(<span class="hljs-string">'your_api.com'</span>,<span class="hljs-string">'/todos'</span>),
        headers : {<span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>},
        body: jsonEncode(todo.toJson()),
    )

    <span class="hljs-built_in">List</span>&lt;Todo&gt; newTodos = (jsonDecode(response.body) <span class="hljs-keyword">as</span> <span class="hljs-built_in">List</span>)
        .cast&lt;<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>,<span class="hljs-built_in">Object?</span>&gt;&gt;()
        .map(Todo.fromJson)
        .toList();

    <span class="hljs-comment">// 리턴값으로 받은 최신의 데이터를 기존의 state에 저장한다.</span>
    state = AsyncData(newTodos);
}
</code></pre>
<p>해당 방식은 추가적인 GET 요청없이도 provider의 state를 업데이트하여 UI에 반영할 수 있다는 장점이 있으나 서버에서 자체적으로 리턴 값이 존재하는 경우에만 활용할 수 있고, 또한 GET request가 복잡한 경우(filtering/sorting)에는 적합하지 않을 수 있다.</p>
<h3 id="heading-using-refinvalidateself-to-refresh-the-provider">Using <code>ref.invalidateSelf()</code> to refresh the provider</h3>
<p>또 다른 방법 중 하나는 <code>ref.invalidateSelf()</code>매서드를 활용하는 것이다. POST요청에 대한 리턴 값이 따로 정해져있지 않다면 사용할 수 있는 방법이며, 간단하게 <code>invalidateSelf()</code> 매서드를 통해서 GET 요청을 추가적으로 하는 것이다(Notifier의 <code>build()</code> 매서드가 다시 실행됨)</p>
<p>예제에서는 Todo를 담고 있는 Notifier의 <code>build()</code>매서드 내에서 서버로부터 <code>List&lt;Todo&gt;</code>를 받아오는 것을 가정하고 있다.</p>
<pre><code class="lang-dart">Future&lt;<span class="hljs-keyword">void</span>&gt; addTodo(Todo todo) <span class="hljs-keyword">async</span> {
  <span class="hljs-comment">// We don't care about the API response</span>
  <span class="hljs-keyword">await</span> http.post(
    <span class="hljs-built_in">Uri</span>.https(<span class="hljs-string">'your_api.com'</span>, <span class="hljs-string">'/todos'</span>),
    headers: {<span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>},
    body: jsonEncode(todo.toJson()),
  );

  <span class="hljs-comment">// Once the post request is done, we can mark the local cache as dirty.</span>
  <span class="hljs-comment">// This will cause "build" on our notifier to asynchronously be called again,</span>
  <span class="hljs-comment">// and will notify listeners when doing so.</span>
  ref.invalidateSelf();

  <span class="hljs-comment">// (Optional) We can then wait for the new state to be computed.</span>
  <span class="hljs-comment">// This ensures "addTodo" does not complete until the new state is available.</span>
  <span class="hljs-keyword">await</span> future;
}
</code></pre>
<p>이전의 방식과 동일하게 POST 요청을 보낸 뒤 리턴 값을 따로 받아오지 않고 기존에 존재하던 <code>List&lt;Todo&gt;</code>를 담고 있는 provider를 업데이트해주는 방식이다.</p>
<p>해당 방식은 직접적인 GET 요청이 있기 때문에 most up-to-date state를 유지할 수 있게 해준다. (해당 시점에 다른 유저가 todo를 추가하는 것도 반영이 가능함. 최신 상태의 데이터를 유지할 수 있음)</p>
<p>그러나 이러한 방식은 추가적인 GET request가 필요하다는 단점이 존재하며 이는 최적화 관련 이슈를 발생시킬 수 있다.</p>
<h3 id="heading-updating-the-local-cache-manually">Updating the local cache manually</h3>
<p>마지막으로 local cache를 수동으로 업데이트해주는 방법이 있다. 해당 방식은 백엔드의 동작을 모방하려는 시도가 존재할 수 있다.(앞서 살펴본 방식대로 추가적인 GET요청을 보내지 않고 업데이트를 하기 위해서 백엔드에서 처리하는 로직을 동일하게 프론트 단에서도 처리하도록 하는 것)</p>
<pre><code class="lang-dart">Future&lt;<span class="hljs-keyword">void</span>&gt; addTodo(Todo todo) <span class="hljs-keyword">async</span> {
  <span class="hljs-comment">// We don't care about the API response</span>
  <span class="hljs-keyword">await</span> http.post(
    <span class="hljs-built_in">Uri</span>.https(<span class="hljs-string">'your_api.com'</span>, <span class="hljs-string">'/todos'</span>),
    headers: {<span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>},
    body: jsonEncode(todo.toJson()),
  );

  <span class="hljs-comment">// We can then manually update the local cache. For this, we'll need to</span>
  <span class="hljs-comment">// obtain the previous state.</span>
  <span class="hljs-comment">// Caution: The previous state may still be loading or in error state.</span>
  <span class="hljs-comment">// A graceful way of handling this would be to read `this.future` instead</span>
  <span class="hljs-comment">// of `this.state`, which would enable awaiting the loading state, and</span>
  <span class="hljs-comment">// throw an error if the state is in error state.</span>
  <span class="hljs-keyword">final</span> previousState = <span class="hljs-keyword">await</span> future;

  <span class="hljs-comment">// We can then update the state, by creating a new state object.</span>
  <span class="hljs-comment">// This will notify all listeners.</span>
  state = AsyncData([...previousState, todo]);
}
</code></pre>
<p>이전의 state 값을 따로 변수에 저장해주고, 이를 포함하여 새로운 todo를 뒤에 추가한 <code>List&lt;Todo&gt;</code>값을 state에 할당하여 UI를 업데이트한다. 예제에서는 immutable state를 사용했으나, 이는 필수 사항이 아닌 권장사항이다.</p>
<p>immutability에 대한 상세 내용은 다음의 문서에서 깊게 다룬다.</p>
<p><a target="_blank" href="https://riverpod.dev/docs/concepts/why_immutability">Riverpod - Why Immutability</a></p>
<p>local cache를 수동으로 업데이트하는 방식은 단 하나의 요청만 사용한다는 장점이 존재하지만 실제 서버의 데이터와 일치하지 않을 수 있다는 단점이 존재한다. 만약 동일한 시점에 다른 사용자가 데이터를 업데이트한다면(예제에서는 Todo) 이는 local cache에 반영되지 못한다.</p>
<p>또한 실제 백엔드의 로직을 모방하여 작성하려면 더욱 복잡해질 수 있다는 단점 또한 존재한다.</p>
<h1 id="heading-6rkw66gg">결론</h1>
<p>POST 요청 이후에 기존에 존재하던 provider 내 상태를 어떻게 업데이트해야하는지에 대해서 알아봤다. 앱 개발을 하다보면 반드시 마주하게 되는 고민인데, 이를 패키지 문서에서 꽤나 깊게 다뤄준 점이 인상깊었다.</p>
<p>시나리오에 따라서 방법을 적절하게 사용하면 되지만 개인적인 생각으로는 POST 요청 이후 provider를 리프레시해주는 방식이 범용적으로 사용될 수 있는 방법인 것 같다.</p>
<p>개발을 처음 공부하던 시점에서는 이렇게 POST요청을 처리한 이후에 추가적으로 GET 요청을 보내는 것이 서버에 엄청난 과부하를 일으키는 건 아닐지, 너무 말도 안되는 방식은 아닌지 걱정이 많았는데, 이번에 관련 내용을 공부하면서 해당 시나리오도 일반적인 상황이라는 것을 알 수 있었다.</p>
<p>edited by 김동한</p>
]]></content:encoded></item><item><title><![CDATA[[Flutter] ListView.builder에 Footer 구현하기]]></title><description><![CDATA[들어가며
ListView.builder 또는 ListView.separated 등을 활용할 때 itemBuilder를 통해서 출력할 item widget을 리턴하게 되는데, 이때 마지막 부분에 footer를 넣거나 margin을 주어야하는 경우가 있다.
기본적인 ListView와는 다르게 builder 함수를 사용하는 경우 단순하게 위젯을 넣기가 곤란하다. 이는 간단하게 해당 아이템이 마지막 아이템인지 또는 첫번째 아이템인지를 체크하여 분기시...]]></description><link>https://blog.skku-comit.dev/flutter-listviewbuilder-footer</link><guid isPermaLink="true">https://blog.skku-comit.dev/flutter-listviewbuilder-footer</guid><category><![CDATA[Flutter]]></category><dc:creator><![CDATA[Donghan Kim]]></dc:creator><pubDate>Thu, 18 Jan 2024 03:30:54 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-65ok7ja06rca66mw">들어가며</h1>
<p><code>ListView.builder</code> 또는 <code>ListView.separated</code> 등을 활용할 때 itemBuilder를 통해서 출력할 item widget을 리턴하게 되는데, 이때 마지막 부분에 footer를 넣거나 margin을 주어야하는 경우가 있다.</p>
<p>기본적인 ListView와는 다르게 builder 함수를 사용하는 경우 단순하게 위젯을 넣기가 곤란하다. 이는 간단하게 해당 아이템이 마지막 아이템인지 또는 첫번째 아이템인지를 체크하여 분기시키는 로직만 추가하면 된다.</p>
<p>다음은 itemBuilder를 활용하여 리스트의 마지막에 FooterWidget을 추가하는 간단한 예제 코드다.</p>
<h2 id="heading-6rws7zie">구현</h2>
<pre><code class="lang-dart"><span class="hljs-comment">// data - item List</span>
ListView.separated(
itemCount : data.length
itemBuilder : (context, index){
    <span class="hljs-comment">// last item인지 확인하는 조건문 사용</span>
    <span class="hljs-comment">// 마지막 아이템인 경우에만 FooterWidget이 위젯트리에 생성된다.</span>
    <span class="hljs-keyword">if</span>(index == data.length <span class="hljs-number">-1</span>){
        <span class="hljs-keyword">return</span> Column(
            children: [
                ItemWidget(item: data[index]),
                FooterWidget(),
            ],
        );
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> ItemWidget(item: data[index])
    }
}
)
</code></pre>
<p>다음과 같이 구현하면 마지막 아이템 아래에만 필요한 Footer 위젯이 생성된다. 동일한 방법으로 첫번째 아이템 위쪽에만 <code>SizedBox()</code>를 활용해서 margin을 주는 방법도 존재한다.</p>
<h1 id="heading-6rkw66gg">결론</h1>
<p>Footer가 아니더라도 보통 리스트의 마지막 공간에는 마지막 아이템임을 표시하는 indicator를 두거나 margin을 두는 경우가 있는데 이럴 때도 활용할 수 있으며, Infinite scroll pagination 패키지를 사용할 때도 builder를 통해서 위젯을 생성하기 때문에 알아두면 여기저기 유용하게 써먹을 수 있는 방법인 것 같다.</p>
]]></content:encoded></item><item><title><![CDATA[[Xcode/Swift] Format On Save 설정]]></title><description><![CDATA[들어가며
Xcode가 기본적으로 제공하는 포맷팅은 꽤나 빈약한 편입니다.
이를 개선하기 위해서 추가적인 익스텐션을 설치하여 저장 시 자동 코드 포맷팅을 설정해보겠습니다.
Swift format for Xcode Extension 설치
우리 애증의 Xcode는 익스텐션을 설치하는 방법도 vscode 또는 Android Studio에 비해서 괴랄한 편인데요. Mac OS 패키지 매니저인 Homebrew를 통해서 설치해보도록 하겠습니다.

Form...]]></description><link>https://blog.skku-comit.dev/xcodeswift-format-on-save</link><guid isPermaLink="true">https://blog.skku-comit.dev/xcodeswift-format-on-save</guid><category><![CDATA[Swift]]></category><dc:creator><![CDATA[Donghan Kim]]></dc:creator><pubDate>Thu, 18 Jan 2024 03:06:00 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-65ok7ja06rca66mw">들어가며</h1>
<p>Xcode가 기본적으로 제공하는 포맷팅은 꽤나 빈약한 편입니다.</p>
<p>이를 개선하기 위해서 추가적인 익스텐션을 설치하여 저장 시 자동 코드 포맷팅을 설정해보겠습니다.</p>
<h2 id="heading-swift-format-for-xcode-extension">Swift format for Xcode Extension 설치</h2>
<p>우리 애증의 Xcode는 익스텐션을 설치하는 방법도 vscode 또는 Android Studio에 비해서 괴랄한 편인데요. Mac OS 패키지 매니저인 Homebrew를 통해서 설치해보도록 하겠습니다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704029860979/31e9760e-922b-42ed-af79-c1664aded6be.png" alt class="image--center mx-auto" /></p>
<p>Format on save를 적용하기 위해서 SwiftFormat 익스텐션을 설치해야합니다. command-line tool로도 해당 익스텐션을 사용할 수 있으나, 저는 Xcode 에디터 내 익스텐션으로 활용하는 것을 추천드립니다.<s>(편해요)</s> 다음은 Xcode 에디터 익스텐션을 설치하는 방법입니다.</p>
<p>터미널을 키고 Homebrew를 통해 파일을 설치합니다.</p>
<pre><code class="lang-bash">brew install --cask swiftformat-for-xcode
</code></pre>
<p>설치가 완료되면 해당 파일을 열어줍니다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704030159265/6dfd5fdc-bada-427b-b67d-7e2010048038.png" alt class="image--center mx-auto" /></p>
<p>파일을 열면 다음과 같은 창이 나오는데요. 주어진 설명대로 그대로 설치해줍니다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704030228892/bb484f8d-8571-4c38-a832-f460e9f02e95.png" alt class="image--center mx-auto" /></p>
<p>성공적으로 설치를 완료했다면 설치된 확장프로그램 목록에 표시가 됩니다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705544310774/0ccc0d8d-b4d8-4bfb-9619-3acaa15d5f39.png" alt class="image--center mx-auto" /></p>
<p>이제 <code>cmd + s</code>로 저장을 하게 되면 못난이 swift 코드를 자동으로 이쁘게 포맷팅 해줍니다.</p>
<h2 id="heading-6rkw66gg">결론</h2>
<p>이제까지 vscode만 사용해왔기 때문에 자동 포맷팅이나 여러가지 확장자를 편리하게 설치할 수 있던 환경과는 조금 달라서 적응이 어려운 것 같습니다.</p>
<p>edited by 김동한</p>
]]></content:encoded></item><item><title><![CDATA[[Flutter] setState() called after dispose()]]></title><description><![CDATA[경고 메세지 분석
Unhandled Exception: setState() called after dispose(): _NoMoreItemState#dc583(lifecycle state: defunct, not mounted)

This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g.,...]]></description><link>https://blog.skku-comit.dev/flutter-setstate-called-after-dispose</link><guid isPermaLink="true">https://blog.skku-comit.dev/flutter-setstate-called-after-dispose</guid><category><![CDATA[Flutter]]></category><dc:creator><![CDATA[Donghan Kim]]></dc:creator><pubDate>Thu, 18 Jan 2024 02:11:14 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-6rk96rogiouployeuoyngcdrtotshj0">경고 메세지 분석</h1>
<pre><code class="lang-plaintext">Unhandled Exception: setState() called after dispose(): _NoMoreItemState#dc583(lifecycle state: defunct, not mounted)

This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.

The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.

This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
</code></pre>
<p>앱에 타이머를 추가하거나, 애니메이션 위젯을 구현하다보면 종종 발견할 수 있는 warning 메세지이다. setState()가 실행되는 시점에 이미 해당 위젯이 <code>dispose()</code>된 상태인 경우, 이는 메모리 누수가 발생할 수 있다는 warning 메시지가 발생하게 된다.</p>
<p>이를 해결하기 위해서는 Timer를 <code>onDispose()</code>시 함께 dispose 해주거나 해당 위젯이 mounted인 상태일 경우에만 <code>setState()</code>를 호출할 수 있도록 로직을 수정해주어야한다.</p>
<p>다음 예제는 아이템 리스트를 불러오다가 마지막 아이템을 읽어온 후에 해당 아이템이 마지막 아이템임을 표시해주는 <code>Text()</code> 위젯이 <code>AnimatedOpacity()</code>위젯을 활용해 애니메이션 효과와 함께 출력되도록 하는 로직이다.</p>
<pre><code class="lang-dart">
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NoMoreItem</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String?</span> label;
  <span class="hljs-keyword">const</span> NoMoreItem({<span class="hljs-keyword">super</span>.key, <span class="hljs-keyword">this</span>.label});

  <span class="hljs-meta">@override</span>
  State&lt;NoMoreItem&gt; createState() =&gt; _NoMoreItemState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_NoMoreItemState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">NoMoreItem</span>&gt; <span class="hljs-title">with</span> <span class="hljs-title">AfterLayoutMixin</span>&lt;<span class="hljs-title">NoMoreItem</span>&gt; </span>{
  <span class="hljs-keyword">var</span> _op = <span class="hljs-number">0.0</span>;

  <span class="hljs-meta">@override</span>
  FutureOr&lt;<span class="hljs-keyword">void</span>&gt; afterFirstLayout(BuildContext context) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">await</span> Future.delayed(<span class="hljs-keyword">const</span> <span class="hljs-built_in">Duration</span>(milliseconds: <span class="hljs-number">400</span>), () {
      <span class="hljs-keyword">if</span> (mounted) {
        setState(() {
          _op = <span class="hljs-number">1.0</span>;
        });
      }
    });
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    <span class="hljs-keyword">super</span>.dispose();
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Container(
      width: <span class="hljs-built_in">double</span>.infinity,
      margin: <span class="hljs-keyword">const</span> EdgeInsets.only(top: <span class="hljs-number">16</span>, bottom: <span class="hljs-number">10</span>),
      alignment: Alignment.center,
      child: AnimatedOpacity(
        duration: <span class="hljs-keyword">const</span> <span class="hljs-built_in">Duration</span>(milliseconds: <span class="hljs-number">400</span>),
        opacity: _op,
        child: Text(
          widget.label ?? <span class="hljs-string">'That\'s all for now!'</span>.hardcoded,
          style: AppTextStyles.black.copyWith(
            color: Palette.gray6B,
            fontSize: <span class="hljs-number">17.</span>sp,
            fontWeight: FontWeight.w600,
          ),
        ),
      ),
    );
  }
}
</code></pre>
<p>예제에서 사용한 <code>AfterLayout</code>과 관련된 내용은 패키지 설명을 통해 확인할 수 있다.</p>
<p><a target="_blank" href="https://pub.dev/packages/after_layout">After Layout Package</a></p>
<p>예제에서는 <code>afterLayout()</code> 내에서 위젯이 위젯 트리에 생성되고 UI가 display될 때 애니메이션이 실행될 수 있도록 일정한 딜레이를 주고 <code>setState()</code>를 실행한다.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@override</span>
  FutureOr&lt;<span class="hljs-keyword">void</span>&gt; afterFirstLayout(BuildContext context) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">await</span> Future.delayed(<span class="hljs-keyword">const</span> <span class="hljs-built_in">Duration</span>(milliseconds: <span class="hljs-number">400</span>), () {
      <span class="hljs-keyword">if</span> (mounted) {
        setState(() {
          _op = <span class="hljs-number">1.0</span>;
        });
      }
    });
  }
</code></pre>
<p>해당 부분에서 만약 <code>mounted</code>를 체크해주지 않고 <code>setState()</code>를 호출하게 되면 만약 사용자가 <code>setState()</code>가 실행되는 시점에 다른 페이지로 이동하는 경우가 발생하여 <code>dispose()</code>가 일어나게 될 경우 setState() called after dispose() 경고가 출력될 수 있으며, 메모리 누수가 발생할 수 있다.</p>
<p>if(mounted)를 통해 분기처리를 해주면 <code>setState()</code>함수가 위젯이 <code>dispose()</code>되지 않은 경우에만 실행되도록 설정할 수 있다.</p>
<p>edited by 김동한</p>
]]></content:encoded></item></channel></rss>