<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>BeomBeomJoJo</title>
    <link>https://afsdzvcx123.tistory.com/</link>
    <description>프로그래밍 &amp;amp; IT 제품 리뷰 &amp;amp; 중국 생활 &amp;amp; 데이트 &amp;amp; 일상 생활 TIP!!</description>
    <language>ko</language>
    <pubDate>Fri, 17 Apr 2026 21:41:11 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>범범조조</managingEditor>
    <image>
      <title>BeomBeomJoJo</title>
      <url>https://tistory1.daumcdn.net/tistory/2112591/attach/88bdff2738594322be7ce7ff896779bd</url>
      <link>https://afsdzvcx123.tistory.com</link>
    </image>
    <item>
      <title>VS Code 에서 JavaScript 실행하기</title>
      <link>https://afsdzvcx123.tistory.com/entry/VS-Code-%EC%97%90%EC%84%9C-JavaScript-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oCksl/btsgChMOdSM/V0jOIa55y04hqSh8O5TFw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oCksl/btsgChMOdSM/V0jOIa55y04hqSh8O5TFw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oCksl/btsgChMOdSM/V0jOIa55y04hqSh8O5TFw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoCksl%2FbtsgChMOdSM%2FV0jOIa55y04hqSh8O5TFw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;560&quot; height=&quot;315&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;개요&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;VS Code 를 이용하여 JavaScript 실행하는 방법에 대해서 알아봅니다.&lt;/li&gt;
&lt;li&gt;여러가지 방법이 있지만, 매우 간편한 방법중 하나인 Code Runner 를 이용하여 사용하는 방법에 대해서 정리 진행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;1. Code Runner Extension 설치&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;VS Code 에서 아주 쉽게 JavaScript 를 실행하려면, Code Runner Extenstion 설치를 진행하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1341&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kJjCo/btsgEc4LoZy/wKKNfK6cnOIOrBguRc2hOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kJjCo/btsgEc4LoZy/wKKNfK6cnOIOrBguRc2hOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kJjCo/btsgEc4LoZy/wKKNfK6cnOIOrBguRc2hOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkJjCo%2FbtsgEc4LoZy%2FwKKNfK6cnOIOrBguRc2hOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1341&quot; height=&quot;474&quot; data-origin-width=&quot;1341&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;2. 테스트 진행할 JavaScript 파일 생성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;설치를 완료 하였다면, 테스트 진행할 자바스크립트 파일 하나를 생성합니다.&lt;/li&gt;
&lt;li&gt;그리고 아래와 같이 테스트 진행할 자바스크립트 코드를 입력합니다.&lt;/li&gt;
&lt;li&gt;저 같은 경우에는 &lt;code&gt;hello.js&lt;/code&gt; 프로젝트 파일 생성 후 아래의 코드를 작성하였습니다.&lt;/li&gt;
&lt;li&gt;그리고, &lt;strong&gt;&lt;code&gt;Ctrl + Alt + N&lt;/code&gt;&lt;/strong&gt; 키를 동시에 누르면 자바스크립트 파일이 실행되어 콘솔에 값이 출력되는 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;JavaScript 말고도 TypeScript, C, C++ 등의 다양한 언어들도 사용할 수 있으니까 간단히 코딩이 필요하신 분들은 Code Runner 사용하시면 될 듯 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const closure = () =&amp;gt; {
    const name = &amp;quot;test&amp;quot;;
    const displayName = () =&amp;gt; {
        console.log(name);
    };
    displayName();
};

closure();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/27gz4/btsgCBdb3HQ/U7tvu6ZbzQteSZ4nkjwCEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/27gz4/btsgCBdb3HQ/U7tvu6ZbzQteSZ4nkjwCEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/27gz4/btsgCBdb3HQ/U7tvu6ZbzQteSZ4nkjwCEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F27gz4%2FbtsgCBdb3HQ%2FU7tvu6ZbzQteSZ4nkjwCEK%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>웹 프로그래밍/JavaScript</category>
      <author>범범조조</author>
      <guid isPermaLink="true">https://afsdzvcx123.tistory.com/1525</guid>
      <comments>https://afsdzvcx123.tistory.com/entry/VS-Code-%EC%97%90%EC%84%9C-JavaScript-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0#entry1525comment</comments>
      <pubDate>Fri, 19 May 2023 21:05:23 +0900</pubDate>
    </item>
    <item>
      <title>[react] react 에서 배열 데이터 삭제하기</title>
      <link>https://afsdzvcx123.tistory.com/entry/react-react-%EC%97%90%EC%84%9C-%EB%B0%B0%EC%97%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%82%AD%EC%A0%9C%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYxrGG/btsbAV8TqM5/jli9Ft3zNpzhRBWpHU0ON1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYxrGG/btsbAV8TqM5/jli9Ft3zNpzhRBWpHU0ON1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYxrGG/btsbAV8TqM5/jli9Ft3zNpzhRBWpHU0ON1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYxrGG%2FbtsbAV8TqM5%2Fjli9Ft3zNpzhRBWpHU0ON1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;참고&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/collegewap/how-to-delete-an-item-from-the-state-array-in-react-kl&quot;&gt;https://dev.to/collegewap/how-to-delete-an-item-from-the-state-array-in-react-kl&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;개요&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;react 에서 Button 컴포넌트 하나를 추가 후, 해당 버튼을 클릭하면 데이터가 하나씩 삭제되어 화면에 출력되는 코드를 작성해 봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;리액트 프로젝트 생성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;먼저 리팩트 프로젝트를 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-npm&quot;&gt;npx create-react-app myapp&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 명령어를 통해 프로젝트 생성을 할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;App.js 코드 작성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;위에서 CRA 로 리액트 프로젝트가 성공적으로 생성되었다면, 기본으로 App.js 파일이 생성됩니다.&lt;/li&gt;
&lt;li&gt;해당 코드를 아래와 같이 작성해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useState } from &amp;quot;react&amp;quot;;

function App() {
    const array = [
        { id: 1, name: &amp;quot;  Apple&amp;quot; },
        { id: 2, name: &amp;quot;  Orange&amp;quot; },
        { id: 3, name: &amp;quot;  Banana&amp;quot; },
        { id: 4, name: &amp;quot;  Grapes&amp;quot; },
        { id: 5, name: &amp;quot;  Apple&amp;quot; },
        { id: 6, name: &amp;quot;  Orange&amp;quot; },
        { id: 7, name: &amp;quot;  Banana&amp;quot; },
        { id: 8, name: &amp;quot;  Grapes&amp;quot; },
    ];
    const [fruits, setFruits] = useState(array);
    const deleteById = (id) =&amp;gt; {
        setFruits((oldValues) =&amp;gt; {
            return oldValues.filter((fruit) =&amp;gt; fruit.id !== id);
        });
    };
    return (
        &amp;lt;div className=&amp;quot;App&amp;quot;&amp;gt;
            &amp;lt;ul&amp;gt;
                {fruits.map((fruit) =&amp;gt; {
                    return (
                        &amp;lt;li key={fruit.id}&amp;gt;
                            &amp;lt;span&amp;gt;{fruit.name}&amp;lt;/span&amp;gt;
                            &amp;lt;button onClick={() =&amp;gt; deleteById(fruit.id)}&amp;gt;Delete&amp;lt;/button&amp;gt;
                        &amp;lt;/li&amp;gt;
                    );
                })}
            &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 코드를 간단히 설명을 하자면, Button 컴포넌트 하나를 추가하였습니다.&lt;/li&gt;
&lt;li&gt;array 변수에는 과일 정보를 담고 있습니다.&lt;/li&gt;
&lt;li&gt;각각의 과일 객체에는 id 정보와 과일 name 속성을 가지고 있습니다.&lt;/li&gt;
&lt;li&gt;deleteById 함수에서는 삭제 대상의 객체의 id 값을 매개변수로 받아서 해당 id 값을 제외한 나머지의 데이터를 filter 하여 return 하도록 하였습니다.&lt;/li&gt;
&lt;li&gt;return 된 값을 최종적으로 setFruits state 함수가 처리하여 데이터가 재 랜더링되면서 데이터가 삭제되는 것을 확인하실 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;출력 결과&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bReCBK/btsbEiIyvi2/8zJ2bsdkBKm8kF6BUIM1X0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bReCBK/btsbEiIyvi2/8zJ2bsdkBKm8kF6BUIM1X0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bReCBK/btsbEiIyvi2/8zJ2bsdkBKm8kF6BUIM1X0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbReCBK%2FbtsbEiIyvi2%2F8zJ2bsdkBKm8kF6BUIM1X0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hu0Iz/btsbtdPNARG/J0XThDzHCq0I0If114UE11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hu0Iz/btsbtdPNARG/J0XThDzHCq0I0If114UE11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hu0Iz/btsbtdPNARG/J0XThDzHCq0I0If114UE11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHu0Iz%2FbtsbtdPNARG%2FJ0XThDzHCq0I0If114UE11%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>웹 프로그래밍/React</category>
      <author>범범조조</author>
      <guid isPermaLink="true">https://afsdzvcx123.tistory.com/1524</guid>
      <comments>https://afsdzvcx123.tistory.com/entry/react-react-%EC%97%90%EC%84%9C-%EB%B0%B0%EC%97%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%82%AD%EC%A0%9C%ED%95%98%EA%B8%B0#entry1524comment</comments>
      <pubDate>Thu, 20 Apr 2023 20:28:49 +0900</pubDate>
    </item>
    <item>
      <title>[react] Button 클릭 시, 데이터 추가하기</title>
      <link>https://afsdzvcx123.tistory.com/entry/react-Button-%ED%81%B4%EB%A6%AD-%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEaMr4/btsbCIHCMYM/g80rSiwEqlNql604uoKkYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEaMr4/btsbCIHCMYM/g80rSiwEqlNql604uoKkYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEaMr4/btsbCIHCMYM/g80rSiwEqlNql604uoKkYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEaMr4%2FbtsbCIHCMYM%2Fg80rSiwEqlNql604uoKkYK%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;개요&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;react 에서 Button 컴포넌트 하나를 추가 후, 해당 버튼을 클릭하면 데이터가 하나씩 추가되어 화면에 출력되는 코드를 작성해 봅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;리액트 프로젝트 생성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;먼저 리팩트 프로젝트를 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-npm&quot;&gt;npx create-react-app myapp&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 명령어를 통해 프로젝트 생성을 할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;App.js 코드 작성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;위에서 CRA 로 리액트 프로젝트가 성공적으로 생성되었다면, 기본으로 App.js 파일이 생성됩니다.&lt;/li&gt;
&lt;li&gt;해당 코드를 아래와 같이 작성해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import &amp;quot;./App.css&amp;quot;;
import { useState } from &amp;quot;react&amp;quot;;

function App() {
    const array = [
        { id: 1, name: &amp;quot;Alice&amp;quot; },
        { id: 2, name: &amp;quot;BoBo&amp;quot; },
        { id: 3, name: &amp;quot;BiBi&amp;quot; },
    ];
    const [names, setNames] = useState(array);
    const handleClick = () =&amp;gt; {
        const newItem = { id: 4, name: &amp;quot;BeomBeomJoJo&amp;quot; };
        setNames((current) =&amp;gt; [...current, newItem]);
    };
    return (
        &amp;lt;div&amp;gt;
            &amp;lt;div&amp;gt;
                &amp;lt;button onClick={handleClick}&amp;gt;Push to state array&amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;

            {names.map((element, index) =&amp;gt; {
                return (
                    &amp;lt;div key={index}&amp;gt;
                        &amp;lt;h2&amp;gt;
                            {element.id} / {element.name}
                        &amp;lt;/h2&amp;gt;
                    &amp;lt;/div&amp;gt;
                );
            })}
        &amp;lt;/div&amp;gt;
    );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 코드를 간단히 설명을 하자면, Button 컴포넌트 하나를 추가하였습니다.&lt;/li&gt;
&lt;li&gt;array 변수에는 기본으로 Alice, BoBo, BiBi 3개의 아이템이 들어 있습니다.&lt;/li&gt;
&lt;li&gt;handleClick 함수를 통해, Button 클릭 이벤트가 발생할 때 마다 BeomBeomJoJo 의 이름을 가진 사람이 추가되도록 합니다.&lt;/li&gt;
&lt;li&gt;map 을 통해 추가되는 학생들을 전부 출력해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;출력 결과&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baZnGT/btsbCHWe4h4/MEPxebukK5ywY7D2eiO6LK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baZnGT/btsbCHWe4h4/MEPxebukK5ywY7D2eiO6LK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baZnGT/btsbCHWe4h4/MEPxebukK5ywY7D2eiO6LK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaZnGT%2FbtsbCHWe4h4%2FMEPxebukK5ywY7D2eiO6LK%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKTMGI/btsbFHOJ1H6/pnttcDajmo6UEIxizbAA70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKTMGI/btsbFHOJ1H6/pnttcDajmo6UEIxizbAA70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKTMGI/btsbFHOJ1H6/pnttcDajmo6UEIxizbAA70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKTMGI%2FbtsbFHOJ1H6%2FpnttcDajmo6UEIxizbAA70%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>웹 프로그래밍/React</category>
      <author>범범조조</author>
      <guid isPermaLink="true">https://afsdzvcx123.tistory.com/1523</guid>
      <comments>https://afsdzvcx123.tistory.com/entry/react-Button-%ED%81%B4%EB%A6%AD-%EC%8B%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0#entry1523comment</comments>
      <pubDate>Thu, 20 Apr 2023 20:27:58 +0900</pubDate>
    </item>
    <item>
      <title>[React] react-query 정리</title>
      <link>https://afsdzvcx123.tistory.com/entry/React-react-query-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uLEzm/btr7Rp5RYTS/96xxlN80cDJkBElMZaGs40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uLEzm/btr7Rp5RYTS/96xxlN80cDJkBElMZaGs40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uLEzm/btr7Rp5RYTS/96xxlN80cDJkBElMZaGs40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuLEzm%2Fbtr7Rp5RYTS%2F96xxlN80cDJkBElMZaGs40%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;참고&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;해당 내용은 (&lt;a href=&quot;https://kyounghwan01.github.io/blog/React/react-query/basic/#%E1%84%89%E1%85%A1%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5&quot;&gt;https://kyounghwan01.github.io/blog/React/react-query/basic/#%E1%84%89%E1%85%A1%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5&lt;/a&gt;) 사이트 보고 공부한 내용을 정리한 곳입니다.&lt;/li&gt;
&lt;li&gt;보다 자세한 내용은 &lt;a href=&quot;https://kyounghwan01.github.io/blog/React/react-query/basic/#%E1%84%89%E1%85%A1%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5&quot;&gt;https://kyounghwan01.github.io/blog/React/react-query/basic/#%E1%84%89%E1%85%A1%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5&lt;/a&gt; 여기를 참고해 주세요~!&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;react-query api 모음&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;reat-query 에서 사용하는 api 들을 하나씩 정리 진행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;useQuery&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;데이터를 get 하기 위한 api입니다. post, update는 useMutation을 사용합니다.&lt;/li&gt;
&lt;li&gt;첫번째 파라미터로 unique Key가 들어가고, 두번째 파라미터로 비동기 함수(api호출 함수)가 들어갑니다.&lt;/li&gt;
&lt;li&gt;첫번째 파라미터로 설정한 unique Key는 다른 컴포넌트에서도 해당 키를 사용하면 호출 가능합니다. unique Key는 string과 배열을 받습니다. 배열로 넘기면 0번 값은 string값으로 다른 컴포넌트에서 부를 값이 들어가고 두번째 값을 넣으면 query 함수 내부에 파라미터로 해당 값이 전달됩니다.&lt;/li&gt;
&lt;li&gt;return 값은 api의 성공, 실패여부, api return 값을 포함한 객체입니다.&lt;/li&gt;
&lt;li&gt;useQuery는 비동기로 작동합니다. 즉, 한 컴포넌트에 여러개의 useQuery가 있다면 하나가 끝나고 다음 useQuery가 실행되는 것이 아닌 두개의 useQuery가 동시에 실행됩니다. 여러개의 비동기 query가 있다면 useQuery보다는 밑에 설명해 드릴 useQueries를 권유드립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;ul&gt;
&lt;li&gt;useQuery 사용 예제 코드입니다.&lt;/li&gt;
&lt;li&gt;우선, 저는 json-server 를 이용하여 가짜 서버에 json 형식으로 테이블 하나를 생성 및 데이터 추가를 해주었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &amp;quot;posts&amp;quot;: [
        {
            &amp;quot;id&amp;quot;: &amp;quot;37f251ea-93ef-49bb-9642-f4b5c2ab3f51&amp;quot;,
            &amp;quot;title&amp;quot;: &amp;quot;BeomBeomJoJo&amp;quot;,
            &amp;quot;body&amp;quot;: &amp;quot;Body Content&amp;quot;
        }
    ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;여기서 fetch api 를 이용하여 해당 데이터를 조회하는 함수를 추가해 주었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;export async function fetchPosts() {
    const response = await fetch(&amp;quot;http://localhost:3000/posts&amp;quot;);
    return response.json();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 코드는 &lt;code&gt;http://localhost:3000/posts&lt;/code&gt; 서버에 데이터를 요청하여 해당 데이터를 가져와 json 형식으로 return 해주는 함수입니다.&lt;/li&gt;
&lt;li&gt;이제 위 내용을 토대로, 처음 프로젝트가 시작할 때 서버의 데이터를 모두 조회하는 로직을 react-query 의 useQuery api 를 사용하여 구현해 보았습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useQuery } from &amp;quot;@tanstack/react-query&amp;quot;;
import { useParams } from &amp;quot;react-router-dom&amp;quot;;
import { fetchPost } from &amp;quot;../api/posts&amp;quot;;
import { useNavigate } from &amp;quot;react-router-dom&amp;quot;;

export default function Post() {
    const navigate = useNavigate();
    const { id } = useParams();
    const {
        isLoading,
        isError,
        data: post,
        error,
    } = useQuery({
        queryKey: [&amp;quot;posts&amp;quot;, id],
        queryFn: () =&amp;gt; fetchPost(id),
        refetchOnWindowFocus: false, // react-query는 사용자가 사용하는 윈도우가 다른 곳을 갔다가 다시 화면으로 돌아오면 이 함수를 재실행합니다. 그 재실행 여부 옵션 입니다.
        retry: 0, //실패시 재호출 몇 번 진행하지에 대한 여부
        onSuccess: (post) =&amp;gt; {
            // 성공시 호출
            console.log(post);
        },
        onError: (e) =&amp;gt; {
            // 실패시 호출 (401, 404 같은 error가 아니라 정말 api 호출이 실패한 경우만 호출됩니다.)
            // 강제로 에러 발생시키려면 api단에서 throw Error 날립니다.
            console.log(e.message);
        },
    });

    if (isLoading) return &amp;quot;loading...&amp;quot;;
    if (isError) return `Error: ${error.message}`;

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;button
                onClick={() =&amp;gt; {
                    navigate(&amp;quot;/&amp;quot;);
                }}
            &amp;gt;
                back to list posts
            &amp;lt;/button&amp;gt;
            &amp;lt;h1&amp;gt;{post.title}&amp;lt;/h1&amp;gt;
            &amp;lt;p&amp;gt;{post.author}&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;아래와 같이 json-server 의 데이터를 가져와서 출력해 주는 것을 볼 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dz28n3/btr7MV5Mt4H/37EMJMNcF4ovxFKXSpVdY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dz28n3/btr7MV5Mt4H/37EMJMNcF4ovxFKXSpVdY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dz28n3/btr7MV5Mt4H/37EMJMNcF4ovxFKXSpVdY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdz28n3%2Fbtr7MV5Mt4H%2F37EMJMNcF4ovxFKXSpVdY1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;useQueries&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;useQuery 를 비동기로 여러 개 실행할 경우, 다음 코드와 같이 귀찮은 경우들이 발생하게 됩니다.&lt;/li&gt;
&lt;li&gt;아래 코드를 보게 되면, 세 함수 모두 비동기로 실행합니다.&lt;/li&gt;
&lt;li&gt;하지만, 세 변수를 개발자가 다 기억을 해야함과 동시에 세 변수에 대한 로딩, 성공, 실패처리를 모두 한다는 번거로운 작업이 생깁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;const usersQuery = useQuery(&amp;quot;users&amp;quot;, fetchUsers);
const teamsQuery = useQuery(&amp;quot;teams&amp;quot;, fetchTeams);
const projectsQuery = useQuery(&amp;quot;projects&amp;quot;, fetchProjects);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이때 promise.all처럼 useQuery를 하나로 묶을 수 있는데, 그것이 useQueries입니다. promise.all과 마찬가지로 하나의 배열에 각 쿼리에 대한 상태 값이 객체로 들어옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// 아래 예시는 롤 룬과, 스펠을 받아오는 예시입니다.
const result = useQueries([
    {
        queryKey: [&amp;quot;getRune&amp;quot;, riot.version],
        queryFn: () =&amp;gt; api.getRunInfo(riot.version),
    },
    {
        queryKey: [&amp;quot;getSpell&amp;quot;, riot.version],
        queryFn: () =&amp;gt; api.getSpellInfo(riot.version),
    },
]);

useEffect(() =&amp;gt; {
    console.log(result); // [{rune 정보, data: [], isSucces: true ...}, {spell 정보, data: [], isSucces: true ...}]
    const loadingFinishAll = result.some((result) =&amp;gt; result.isLoading);
    console.log(loadingFinishAll); // loadingFinishAll이 false이면 최종 완료
}, [result]);&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;useMutation&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;값을 바꿀 때 사용하는 API 입니다.&lt;/li&gt;
&lt;li&gt;return 값은 useQUery 와 동일하고, 예시 코드를 통해 어떻게 사용 되는지 보여 드리도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import PostForm from &amp;quot;../components/PostForm&amp;quot;;
import { useMutation, useQuery, useQueryClient } from &amp;quot;@tanstack/react-query&amp;quot;;
import { useParams } from &amp;quot;react-router-dom&amp;quot;;
import { fetchPost, updatePost } from &amp;quot;../api/posts&amp;quot;;
import { useNavigate } from &amp;quot;react-router-dom&amp;quot;;

export default function EditPost() {
    const queryClient = useQueryClient();
    const navigate = useNavigate();
    const { id } = useParams();
    const {
        isLoading,
        isError,
        data: post,
        error,
    } = useQuery({
        queryKey: [&amp;quot;posts&amp;quot;, id],
        queryFn: () =&amp;gt; fetchPost(id),
    });
    const updatePostMutation = useMutation({
        mutationFn: updatePost,
        onSuccess: () =&amp;gt; {
            // react-query 장점으로, 데이터 update 후에 get 함수를 간단히 재실행 할 수 있습니다.
            // mutation 함수가 성공했다면, unique key 로 맵핑된 get 함수를 invalidateQueries 에 넣어주면 됩니다.
            queryClient.invalidateQueries({ queryKey: [&amp;quot;posts&amp;quot;] });
            navigate(&amp;quot;/&amp;quot;);
        },
    });

    if (isLoading) return &amp;quot;loading...&amp;quot;;
    if (isError) return `Error: ${error.message}`;

    const handleSubmit = (updatePost) =&amp;gt; {
        updatePostMutation.mutate({ id, ...updatePost });
    };

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;PostForm onSubmit={handleSubmit} initialValue={post} /&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위에서 보면, useMutation 을 이용하여 데이터를 Update 하는 것을 확인하실 수 있습니다.&lt;/li&gt;
&lt;li&gt;여기서 중요한 부분은, &lt;code&gt;invalidateQueries&lt;/code&gt; 함수 입니다.&lt;/li&gt;
&lt;li&gt;이 함수를 통해 mutation 함수가 성공 했다면, unique key 로 맵핑된 get 함수를 다시 재실행 시켜줘서, 업데이트 된 내용이 바로 재렌더링 되어 반영되게 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;이처럼, 데이터 변경 작업 혹은 추가 작업은 useMutation 함수를 이용하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>웹 프로그래밍/React</category>
      <author>범범조조</author>
      <guid isPermaLink="true">https://afsdzvcx123.tistory.com/1522</guid>
      <comments>https://afsdzvcx123.tistory.com/entry/React-react-query-%EC%A0%95%EB%A6%AC#entry1522comment</comments>
      <pubDate>Mon, 3 Apr 2023 20:40:55 +0900</pubDate>
    </item>
    <item>
      <title>[Web 팁] JSON-SERVER 사용법</title>
      <link>https://afsdzvcx123.tistory.com/entry/Web-%ED%8C%81-JSON-SERVER-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ048W/btr6YQ5mYtq/9OEfFiCC2hVBsUKc1BClF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ048W/btr6YQ5mYtq/9OEfFiCC2hVBsUKc1BClF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ048W/btr6YQ5mYtq/9OEfFiCC2hVBsUKc1BClF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ048W%2Fbtr6YQ5mYtq%2F9OEfFiCC2hVBsUKc1BClF0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;참고&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/typicode/json-server&quot;&gt;https://github.com/typicode/json-server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/json-server&quot;&gt;https://www.npmjs.com/package/json-server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;JSON-SERVER 개요&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;프론트엔트 개발을 할 떄 서버단의 데이터를 가져와서 테스트를 진행해야 하는 경우가 있습니다.&lt;/li&gt;
&lt;li&gt;하지만, 백엔드쪽 개발이 미완성이라면..데이터를 가져와서 테스트를 진행할 수가 없습니다..&lt;/li&gt;
&lt;li&gt;하지만, JSON-SERVER 를 이용하면 실무에서 사용하는 실 데이터와 최대한 비슷한 느낌으로 가짜 API 서버를 만들어서 테스트를 진행할 수있습니다.&lt;/li&gt;
&lt;li&gt;즉, JSON-SERVER 를 이용하여 연습용 서버를 쉽게 구성하여 백엔드쪽이 완성되지 않더라도 프론트엔드 개발에는 문제 없이 개발을 진행할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;참고로, JSON-SERVER 는 프로덕션에서 사용하기 위해 만들어진 것이 아닙니다. 떄문에 JSON-SERVER 를 이용하여 실제 프로젝트를 개발하면 안됩니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;JSON-SERVER 사용법&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;그럼 이제 JSON-SERVER 사용하는 방법에 대해서 알아 보도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h3&gt;&lt;strong&gt;JSON-SERVER 설치&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;먼저 JSON-SERVER 설치를 진행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-npm&quot;&gt;npm install -g json-server&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 명령어를 통해 json-server 설치를 진행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h3&gt;&lt;strong&gt;db.json 파일 생성&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;json-server 설치가 완료 되었으면, &lt;strong&gt;db.json&lt;/strong&gt; 파일 하나를 생성합니다.&lt;/li&gt;
&lt;li&gt;db.json 파일은 루트 디렉터리에서 생성을 해주고, 여기서 db.json 파일은 데이터베이스 역할을 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &amp;quot;todos&amp;quot;: [
        {
            &amp;quot;id&amp;quot;: 1,
            &amp;quot;content&amp;quot;: &amp;quot;HTML&amp;quot;,
            &amp;quot;completed&amp;quot;: true
        },
        {
            &amp;quot;id&amp;quot;: 2,
            &amp;quot;content&amp;quot;: &amp;quot;CSS&amp;quot;,
            &amp;quot;completed&amp;quot;: false
        },
        {
            &amp;quot;id&amp;quot;: 3,
            &amp;quot;content&amp;quot;: &amp;quot;Javascript&amp;quot;,
            &amp;quot;completed&amp;quot;: true
        }
    ],
    &amp;quot;users&amp;quot;: [
        {
            &amp;quot;id&amp;quot;: 1,
            &amp;quot;name&amp;quot;: &amp;quot;Lee&amp;quot;,
            &amp;quot;role&amp;quot;: &amp;quot;developer&amp;quot;
        },
        {
            &amp;quot;id&amp;quot;: 2,
            &amp;quot;name&amp;quot;: &amp;quot;Kim&amp;quot;,
            &amp;quot;role&amp;quot;: &amp;quot;designer&amp;quot;
        }
    ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;db.json 파일의 내용은 위와 같이 테스트 데이터 하나 작성 진행합니다.&lt;/li&gt;
&lt;li&gt;테스트 내용은 얼마든지 변경되도 무방합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h3&gt;&lt;strong&gt;json-server 실행&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이제 json-server 를 사용하기 위한 사전 준비는 모두 마쳤습니다.&lt;/li&gt;
&lt;li&gt;json-server 를 실행해서, 실제로 URL 주소에서 json 데이터가 보이는지 확인해 보겠습니다.&lt;/li&gt;
&lt;li&gt;json-server 를 실행하는 명령어는 다음과 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;json-server --watch db.json&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;위 명령어를 실행해서 정상 실행이 되면, 아래와 같이 로그가 출력되는 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;PS C:\Users\bh.cho\OneDrive\바탕 화면\jsonserver&amp;gt; json-server --watch db.json

  \{^_^}/ hi!

  Loading db.json
  Done

  Resources
  http://localhost:3000/todos
  http://localhost:3000/users

  Home
  http://localhost:3000

  Type s + enter at any time to create a snapshot of the database
  Watching...&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;앞서, db.json 파일에 todos, users 2개의 데이터를 추가 하였습니다.&lt;/li&gt;
&lt;li&gt;쉽게 생각해, todos, users 2개의 테이블이 생성되었고 그 내용을 &lt;code&gt;http://localhost:3000/todos&lt;/code&gt;, &lt;code&gt;http://localhost:3000/users&lt;/code&gt; URL 주소로 생성되는 것입니다.&lt;/li&gt;
&lt;li&gt;그럼 실제로, 해당 주소로 가서 json 데이터가 정상적으로 나오는지 확인해 보겠습니다.&lt;/li&gt;
&lt;li&gt;URL 주소 접속 결과, 아래와 같이 데이터 조회가 정상적으로 되는 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v5HUO/btr63lJIUCt/lJv5TFtaZ041GpV2Xdu4C0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v5HUO/btr63lJIUCt/lJv5TFtaZ041GpV2Xdu4C0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v5HUO/btr63lJIUCt/lJv5TFtaZ041GpV2Xdu4C0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv5HUO%2Fbtr63lJIUCt%2FlJv5TFtaZ041GpV2Xdu4C0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;JSON-SERVER 포트 변경하는 방법&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;json-server 의 기본 포트는 3000번 입니다.&lt;/li&gt;
&lt;li&gt;만약 포트를 변경하려면, port 옵션을 추가해주면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;json-server --watch db.json --port 5000&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위와 같이 매변 명령어를 통해 입력하는 것은 번거롭습니다.&lt;/li&gt;
&lt;li&gt;때문에, package.json 파일의 scripts 를 다음과 같이 수정하여 json-server 를 실행해주면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;name&amp;quot;: &amp;quot;json-server-exam&amp;quot;,
  &amp;quot;version&amp;quot;: &amp;quot;1.0.0&amp;quot;,
  &amp;quot;scripts&amp;quot;: {
    &amp;quot;start&amp;quot;: &amp;quot;json-server --watch db.json&amp;quot;
  },
  &amp;quot;devDependencies&amp;quot;: {
    &amp;quot;json-server&amp;quot;: &amp;quot;^0.15.0&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;npm start&lt;/code&gt;&lt;/pre&gt;</description>
      <category>웹 프로그래밍</category>
      <author>범범조조</author>
      <guid isPermaLink="true">https://afsdzvcx123.tistory.com/1520</guid>
      <comments>https://afsdzvcx123.tistory.com/entry/Web-%ED%8C%81-JSON-SERVER-%EC%82%AC%EC%9A%A9%EB%B2%95#entry1520comment</comments>
      <pubDate>Thu, 30 Mar 2023 21:18:48 +0900</pubDate>
    </item>
    <item>
      <title>[React] react-query 사용하기</title>
      <link>https://afsdzvcx123.tistory.com/entry/React-react-query-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oBv2N/btr64maxkRp/wNrkCJSyLjXbUA5tuCEEHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oBv2N/btr64maxkRp/wNrkCJSyLjXbUA5tuCEEHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oBv2N/btr64maxkRp/wNrkCJSyLjXbUA5tuCEEHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoBv2N%2Fbtr64maxkRp%2FwNrkCJSyLjXbUA5tuCEEHK%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;참고&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://kyounghwan01.github.io/blog/React/react-query/basic/#%E1%84%89%E1%85%A1%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%82%E1%85%B3%E1%86%AB-%E1%84%8B%E1%85%B5%E1%84%8B%E1%85%B2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;개요&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vite 을 통해 테스트 진행할 리액트 프로젝트 하나를 생성합니다.&lt;/li&gt;
&lt;li&gt;생성된 프로젝트에 react-query 를 다운받고, CRUD 기능을 통해 react-query 사용 방법에 대해서 익힙니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;사용 기술&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;react-query 테스트 프로젝트를 개발하기 위해, 사전에 필요한 라이브러리 정보는 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;vite&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;react-query&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;react-query-devtools&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;json-server&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;uuid&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;테스트 프로젝트에서 사용될 라이브러리 목록은 위와 같습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;각 라이브러리는 프로젝트 진행해 나가면서, 필요할 때 마다 설치 진행하겠습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;Vite 설치&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;저는 Vite 를 사용해서 프로젝트를 진행해 나가려고 합니다.&lt;/li&gt;
&lt;li&gt;Vite 는 빠르고 간결한 모던 웹 프로젝트 개발 경험에 맞춰 탄생항 빌드 도구입니다.&lt;/li&gt;
&lt;li&gt;자세한 내용은 &lt;a href=&quot;https://vitejs-kr.github.io/guide/&quot;&gt;https://vitejs-kr.github.io/guide/&lt;/a&gt; 해당 사이트에서 확인하시면 됩니다.&lt;/li&gt;
&lt;li&gt;앞서 생성한 리액트 프로젝트에서 Vite 를 설치 해줍니다.&lt;/li&gt;
&lt;li&gt;Vite 설치하는 방법은 아래와 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm create vite@latest ./&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B6x1I/btr6RBVco7w/G2n3fETEvW4fBMZHHmoLr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B6x1I/btr6RBVco7w/G2n3fETEvW4fBMZHHmoLr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B6x1I/btr6RBVco7w/G2n3fETEvW4fBMZHHmoLr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB6x1I%2Fbtr6RBVco7w%2FG2n3fETEvW4fBMZHHmoLr0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;위 명령어를 통해 Vite를 설치 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;정상적으로 설치가 되었다면, 아래와 같이 프로젝트가 생성된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqRD3T/btr61hBic7X/PerkJRoMhO7KJhEjGPZ4G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqRD3T/btr61hBic7X/PerkJRoMhO7KJhEjGPZ4G0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqRD3T/btr61hBic7X/PerkJRoMhO7KJhEjGPZ4G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqRD3T%2Fbtr61hBic7X%2FPerkJRoMhO7KJhEjGPZ4G0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;다음으로, &lt;code&gt;npm install&lt;/code&gt; 명령을 통해 package.json 파일의 의존이 되어있는 패키지들을 설치 진행해줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;여기까지 모두 완료 하였다면, 이제 정상적으로 실행 되는지 확인을 위해서 Vite 를 실행합니다.&lt;/li&gt;
&lt;li&gt;Vite 를 실행하는 명령어는 다음과 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm run dev&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;npm run dev&lt;/code&gt; 명령을 실행하게 되면, 아래와 같이 로그가 출력됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;
  VITE v4.2.1  ready in 1015 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h to show help
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;위에서 &lt;code&gt;http://localhost:5173&lt;/code&gt; 주소가 보이는데, 여기로 접속 합니다.&lt;/li&gt;
&lt;li&gt;문제 없이 설치가 되었다면, 아래와 같이 &lt;code&gt;Vite + React&lt;/code&gt; 화면이 나오는 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xixiK/btr6ZBHlJvO/pr3KK3Lc7a2t5D1XFkFx61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xixiK/btr6ZBHlJvO/pr3KK3Lc7a2t5D1XFkFx61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xixiK/btr6ZBHlJvO/pr3KK3Lc7a2t5D1XFkFx61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxixiK%2Fbtr6ZBHlJvO%2Fpr3KK3Lc7a2t5D1XFkFx61%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;불필요한 파일 삭제&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;다음으로, 프로젝트 개발에 앞서 필요없는 파일 3가지를 우선 삭제 하도록 하겠습니다.&lt;/li&gt;
&lt;li&gt;삭제 진행할 파일 목록은 다음과 같습니다.&lt;ul&gt;
&lt;li&gt;assets&lt;/li&gt;
&lt;li&gt;App.css&lt;/li&gt;
&lt;li&gt;index.css&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;앞서 Vite 를 통해 생성했던 프로젝트에서 위 3가지 파일을 삭제합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duz7Gp/btr64maxnX6/blKNm5LChAZT8nz4ky1zGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duz7Gp/btr64maxnX6/blKNm5LChAZT8nz4ky1zGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duz7Gp/btr64maxnX6/blKNm5LChAZT8nz4ky1zGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fduz7Gp%2Fbtr64maxnX6%2FblKNm5LChAZT8nz4ky1zGk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;위와 같이 삭제를 완료 하였다면, 남은 파일 목록은 아래와 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l0qVe/btr64lCHnFB/cE40ZEAMJBL6QheWxRkK1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l0qVe/btr64lCHnFB/cE40ZEAMJBL6QheWxRkK1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l0qVe/btr64lCHnFB/cE40ZEAMJBL6QheWxRkK1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl0qVe%2Fbtr64lCHnFB%2FcE40ZEAMJBL6QheWxRkK1K%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;Router 구성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;앞에서 불필요한 파일을 삭제 하였다면, 이제 라우터를 구성합니다.&lt;/li&gt;
&lt;li&gt;먼저, 라우터를 구성하기 위해 필요한 패키지인 &lt;code&gt;react-router-dom&lt;/code&gt; 패키지를 설치 합니다.&lt;/li&gt;
&lt;li&gt;설치 명령어는 아래와 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm i react-router-dom&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;설치가 완료 되었다면, &lt;strong&gt;main.jsx&lt;/strong&gt; 에 &lt;code&gt;BrowserRouter&lt;/code&gt; 를 추가해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import React from &amp;quot;react&amp;quot;;
import ReactDOM from &amp;quot;react-dom/client&amp;quot;;
import { BrowserRouter } from &amp;quot;react-router-dom&amp;quot;;
import App from &amp;quot;./App&amp;quot;;

ReactDOM.createRoot(document.getElementById(&amp;quot;root&amp;quot;)).render(
    &amp;lt;React.StrictMode&amp;gt;
        &amp;lt;BrowserRouter&amp;gt;
            &amp;lt;App /&amp;gt;
        &amp;lt;/BrowserRouter&amp;gt;
    &amp;lt;/React.StrictMode&amp;gt;
);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위와 같이 &lt;code&gt;BrowserRouter&lt;/code&gt; 를 추가해 주었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;App.jsx 에 페이지 라우터 구성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;React-Query 테스트를 위해, 총 3개의 페이지를 구성합니다.&lt;/li&gt;
&lt;li&gt;아직 페이지를 생성하지는 않았지만, 아래와 같이 PostLists, Post, EditPost 3개의 페이지 Router 를 구성해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useState } from &amp;quot;react&amp;quot;;

function App() {
    return (
        &amp;lt;div className=&amp;quot;App&amp;quot;&amp;gt;
            &amp;lt;h1&amp;gt;Awesome blog&amp;lt;/h1&amp;gt;
            &amp;lt;Routes&amp;gt;
                &amp;lt;Route path=&amp;quot;/&amp;quot; element={&amp;lt;PostLists /&amp;gt;} /&amp;gt;
                &amp;lt;Route path=&amp;quot;/post/:id&amp;quot; element={&amp;lt;Post /&amp;gt;} /&amp;gt;
                &amp;lt;Route path=&amp;quot;/post/:id/edit&amp;quot; element={&amp;lt;EditPost /&amp;gt;} /&amp;gt;
            &amp;lt;/Routes&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위에서 PostLists, Post, EditPost 페이지는 하나씩 구현해 나갈 예정입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;react-query 설치&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;App.jsx 에 Router 구성이 완료 되었다면, 페이지와 컴포넌트를 추가하기에 앞서 react-query 를 먼저 설치를 진행합니다.&lt;/li&gt;
&lt;li&gt;react query 의 공식 사이트는 &lt;a href=&quot;https://tanstack.com/query/v3/&quot;&gt;https://tanstack.com/query/v3/&lt;/a&gt; 해당 링크를 통해 확인하실 수 있습니다.&lt;/li&gt;
&lt;li&gt;react-query 설치하는 명령어는 아래와 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm i @tanstack/react-query&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;react-qeury 를 추가 완료 하였다면, react-query 전용 devtools 도 설치해줍니디다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm i @tanstack/react-query-devtools&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위와 같이 설치가 모두 완료 되었다면, react-query 를 사용할 준비는 모두 마쳤습니다.&lt;/li&gt;
&lt;li&gt;react-query, react-query devtools 를 사용하려면, &lt;strong&gt;main.jsx&lt;/strong&gt; 에 관련 속성을 추가해 주어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import React from &amp;quot;react&amp;quot;;
import ReactDOM from &amp;quot;react-dom/client&amp;quot;;
import { BrowserRouter } from &amp;quot;react-router-dom&amp;quot;;
import App from &amp;quot;./App&amp;quot;;
import { QueryClient, QueryClientProvider } from &amp;quot;@tanstack/react-query&amp;quot;;
import { ReactQueryDevtools } from &amp;quot;@tanstack/react-query-devtools&amp;quot;;

// create a client
const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById(&amp;quot;root&amp;quot;)).render(
    &amp;lt;React.StrictMode&amp;gt;
        &amp;lt;BrowserRouter&amp;gt;
            &amp;lt;QueryClientProvider client={queryClient}&amp;gt;
                &amp;lt;App /&amp;gt;
                &amp;lt;ReactQueryDevtools initialIsOpen={false} /&amp;gt;
            &amp;lt;/QueryClientProvider&amp;gt;
        &amp;lt;/BrowserRouter&amp;gt;
    &amp;lt;/React.StrictMode&amp;gt;
);&lt;/code&gt;&lt;/pre&gt;
&lt;br&gt;

&lt;h2&gt;&lt;strong&gt;json-server 설치 및 구성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;react-query 를 사용하기에 앞서, client 에서 서버쪽으로 데이터를 요청해서 조회해야 할 서버 쪽 데이터가 필요합니다.&lt;/li&gt;
&lt;li&gt;하지만, 서버를 구성하기에는 워낙 시간적 비용이 많이 들기 떄문에 원활한 테스트를 위해 json-server 를 이용하여 가짜 서버 역학을 하려고 합니다.&lt;/li&gt;
&lt;li&gt;json-server 설치는 다음 명령어를 통해 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install -g json-server&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;설치가 완료 되었으면, 프로젝트 root 경로에 &lt;strong&gt;db.json&lt;/strong&gt; 파일을 하나 생성해 줍니다.&lt;/li&gt;
&lt;li&gt;그리고 아래와 같이 테스트 데이터를 json 형식으로 추가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &amp;quot;posts&amp;quot;: [
        {
            &amp;quot;id&amp;quot;: &amp;quot;1&amp;quot;,
            &amp;quot;title&amp;quot;: &amp;quot;title&amp;quot;,
            &amp;quot;body&amp;quot;: &amp;quot;body&amp;quot;
        }
    ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;우선 posts 이름을 가진 데이터 Array 가 있고, 여기에는 1개의 item 을 가지고 있는 형태의 json 데이터를 하나 생성하였습니다.&lt;/li&gt;
&lt;li&gt;그리고 json-server 를 실행시켜 주면 됩니다.&lt;/li&gt;
&lt;li&gt;json-server 실행 시켜주는 명령어는 아래와 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;json-server --watch db.json&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 명령어를 통해, json-server 를 실행시킬 수 있습니다.&lt;/li&gt;
&lt;li&gt;정상적으로 실행 되었다면, 아래와 같이 로그가 출력됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;\{^_^}/ hi!

  Loading db.json
  Done

  Resources
  http://localhost:3000/posts

  Home
  http://localhost:3000

  Type s + enter at any time to create a snapshot of the database
  Watching...&lt;/code&gt;&lt;/pre&gt;&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;CRUD API 서비스 추가&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;다음으로 react-query 를 이용하여 서버의 데이터를 CRUD 하기 위한 api 파일 하나를 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/api/posts.jsx&lt;/code&gt; 경로에 posts.jsx 파일을 하나 생성 후, 서버의 데이터를 요청하는 로직을 추가해 줍니다.&lt;/li&gt;
&lt;li&gt;여기서 저는 fetch API 를 사용하였습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;export async function fetchPosts() {
    const response = await fetch(&amp;quot;http://localhost:3000/posts&amp;quot;);
    return response.json();
}

export async function fetchPost(id) {
    const response = await fetch(`http://localhost:3000/posts/${id}`);
    return response.json();
}

export async function createPost(newPost) {
    const response = await fetch(`http://localhost:3000/posts`, {
        method: &amp;quot;POST&amp;quot;,
        headers: {
            &amp;quot;Content-Type&amp;quot;: &amp;quot;application/json&amp;quot;,
        },
        body: JSON.stringify(newPost),
    });
    return response.json();
}

export async function updatePost(updatedPost) {
    const response = await fetch(`http://localhost:3000/posts/${updatedPost.id}`, {
        method: &amp;quot;PUT&amp;quot;,
        headers: {
            &amp;quot;Content-Type&amp;quot;: &amp;quot;application/json&amp;quot;,
        },
        body: JSON.stringify(updatedPost),
    });
    return response.json();
}

export async function deletePost(id) {
    const response = await fetch(`http://localhost:3000/posts/${id}`, {
        method: &amp;quot;DELETE&amp;quot;,
    });
    return response.json();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위에서 작성한 함수들을 바로 뒤에서 만들 페이지 및 컴포넌트에서 사용할 예정입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;페이지 및 컴포넌트 추가&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;앞서, react-query 와 react-query devtools 설정을 완료 하였습니다.&lt;/li&gt;
&lt;li&gt;또한, App.jsx 에 3개의 페이지 전환을 위한 라우터 구성도 완료하였습니다.&lt;/li&gt;
&lt;li&gt;이제 앞서 추가했던, PostLists, Post, EditPost 3개의 페이지 및 관련 컴포넌트들을 추가하도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h3&gt;&lt;strong&gt;Post 페이지 생성&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/pages/Post.jsx&lt;/code&gt; 경로로 해서 Post 페이지를 추가하였습니다.&lt;/li&gt;
&lt;li&gt;Post.jsx 코드는 다음과 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useQuery } from &amp;quot;@tanstack/react-query&amp;quot;;
import { useParams } from &amp;quot;react-router-dom&amp;quot;;
import { fetchPost } from &amp;quot;../api/posts&amp;quot;;
import { useNavigate } from &amp;quot;react-router-dom&amp;quot;;

export default function Post() {
    const navigate = useNavigate();
    const { id } = useParams();
    const {
        isLoading,
        isError,
        data: post,
        error,
    } = useQuery({
        queryKey: [&amp;quot;posts&amp;quot;, id],
        queryFn: () =&amp;gt; fetchPost(id),
    });

    if (isLoading) return &amp;quot;loading...&amp;quot;;
    if (isError) return `Error: ${error.message}`;

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;button
                onClick={() =&amp;gt; {
                    navigate(&amp;quot;/&amp;quot;);
                }}
            &amp;gt;
                back to list posts
            &amp;lt;/button&amp;gt;
            &amp;lt;h1&amp;gt;{post.title}&amp;lt;/h1&amp;gt;
            &amp;lt;p&amp;gt;{post.author}&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Post.jsx 페이지 코드를 보시면, useQuery 를 이용하여 데이터를 불러오는 것을 확인하실 수 있습니다.&lt;/li&gt;
&lt;li&gt;useQuery 는 데이터를 get 하기 위한 api 입니다.&lt;/li&gt;
&lt;li&gt;post, update, delete 는 &lt;strong&gt;useMutation&lt;/strong&gt; 을 사용합니다.&lt;/li&gt;
&lt;li&gt;첫 번째 파라미터로 unique key 가 들어가고, 두 번째 파라미터로 비동기 함수(api 호출 함수) 가 들어갑니다. (두 번째 파라미터는 promise 가 들어가야 합니다.)&lt;/li&gt;
&lt;li&gt;첫 번째 파라미터로 설정한 unique Key 는 다른 컴포넌트에서도 해당 키를 사용하면 호출 가능합니다. unique key 는 string 배열을 받습니다. 배열로 넘기면 0번 값은 string 값으로 다른 컴포넌트에서 부를 값이 들어가고 두 번째 값을 넣으면 query 함수 내부에 파리미터로 해당 값이 전달됩니다.&lt;/li&gt;
&lt;li&gt;return 값은 api의 성공, 실패여부, api return 값을 포함한 객체입니다.&lt;/li&gt;
&lt;li&gt;useQuery는 비동기로 작동합니다. 즉, 한 컴포넌트에 여러개의 useQuery가 있다면 하나가 끝나고 다음 useQuery가 실행되는 것이 아닌 두개의 useQuery가 동시에 실행됩니다. 여러개의 비동기 query가 있다면 useQuery보다는 밑에 설명해 드릴 useQueries를 권유드립니다.&lt;/li&gt;
&lt;li&gt;enabled를 사용하면 useQuery를 동기적으로 사용 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h3&gt;&lt;strong&gt;PostList 페이지 생성&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/pages/PostList.jsx&lt;/code&gt; 경로로 해서 PostList 페이지를 추가하였습니다.&lt;/li&gt;
&lt;li&gt;PostList.jsx 코드는 다음과 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import AddPost from &amp;quot;../components/AddPost&amp;quot;;
import { QueryClient, useQueryClient, useMutation, useQuery } from &amp;quot;@tanstack/react-query&amp;quot;;
import { deletePost, fetchPosts } from &amp;quot;../api/posts&amp;quot;;
import { useNavigate } from &amp;quot;react-router-dom&amp;quot;;
import { useEffect, useMemo, useState } from &amp;quot;react&amp;quot;;

export default function PostLists() {
    const queryClient = useQueryClient();
    const navigate = useNavigate();
    const {
        isLoading,
        isError,
        data: posts,
        error,
    } = useQuery({
        queryKey: [&amp;quot;posts&amp;quot;],
        queryFn: fetchPosts,
    });

    const deletePostMutation = useMutation({
        mutationFn: deletePost,
        onSuccess: () =&amp;gt; {
            queryClient.invalidateQueries({ queryKey: [&amp;quot;posts&amp;quot;] });
        },
    });

    const handleDelete = (id) =&amp;gt; {
        deletePostMutation.mutate(id);
    };

    if (isLoading) return &amp;quot;loading...&amp;quot;;
    if (isError) return `Error: ${error.message}`;

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;AddPost /&amp;gt;
            {posts.map((post) =&amp;gt; (
                &amp;lt;div key={post.id} style={{ background: &amp;quot;#777&amp;quot; }}&amp;gt;
                    &amp;lt;h4 style={{ cursor: &amp;quot;pointer&amp;quot; }} onClick={() =&amp;gt; navigate(`/post/${post.id}`)}&amp;gt;
                        {post.title}
                    &amp;lt;/h4&amp;gt;
                    &amp;lt;button onClick={() =&amp;gt; navigate(`/post/${post.id}/edit`)}&amp;gt;Edit&amp;lt;/button&amp;gt;
                    &amp;lt;button onClick={() =&amp;gt; handleDelete(post.id)}&amp;gt;Delete&amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
            ))}
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h3&gt;&lt;strong&gt;EditPost 페이지 생성&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/pages/EditPost.jsx&lt;/code&gt; 경로로 해서 EditPost 페이지를 추가하였습니다.&lt;/li&gt;
&lt;li&gt;EditPost.jsx 코드는 다음과 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import PostForm from &amp;quot;../components/PostForm&amp;quot;;
import { useMutation, useQuery, useQueryClient } from &amp;quot;@tanstack/react-query&amp;quot;;
import { useParams } from &amp;quot;react-router-dom&amp;quot;;
import { fetchPost, updatePost } from &amp;quot;../api/posts&amp;quot;;
import { useNavigate } from &amp;quot;react-router-dom&amp;quot;;

export default function EditPost() {
    const queryClient = useQueryClient();
    const navigate = useNavigate();
    const { id } = useParams();
    const {
        isLoading,
        isError,
        data: post,
        error,
    } = useQuery({
        queryKey: [&amp;quot;posts&amp;quot;, id],
        queryFn: () =&amp;gt; fetchPost(id),
    });
    const updatePostMutation = useMutation({
        mutationFn: updatePost,
        onSuccess: () =&amp;gt; {
            queryClient.invalidateQueries({ queryKey: [&amp;quot;posts&amp;quot;] });
            navigate(&amp;quot;/&amp;quot;);
        },
    });

    if (isLoading) return &amp;quot;loading...&amp;quot;;
    if (isError) return `Error: ${error.message}`;

    const handleSubmit = (updatePost) =&amp;gt; {
        updatePostMutation.mutate({ id, ...updatePost });
    };

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;PostForm onSubmit={handleSubmit} initialValue={post} /&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h3&gt;&lt;strong&gt;AddPost.jsx 컴포넌트 생성&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;앞서 페이지에서 사용하는 공통 컴포넌트 중 하나인 &lt;strong&gt;AddPost.jsx&lt;/strong&gt; 컴포넌트 코드입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useMutation, useQueryClient } from &amp;quot;@tanstack/react-query&amp;quot;;
import { createPost } from &amp;quot;../api/posts&amp;quot;;
import PostForm from &amp;quot;./PostForm&amp;quot;;
import { v4 as uuidv4 } from &amp;quot;uuid&amp;quot;;

export default function AddPost() {
    const queryClient = useQueryClient();

    const createPostMutation = useMutation({
        mutationFn: createPost,
        onSuccess: () =&amp;gt; {
            queryClient.invalidateQueries({ queryKey: [&amp;quot;posts&amp;quot;] });
            console.log(&amp;quot;success bro!&amp;quot;);
        },
    });

    const handleAddPost = (post) =&amp;gt; {
        createPostMutation.mutate({
            id: uuidv4(),
            ...post,
        });
    };

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;h2&amp;gt;Add new post&amp;lt;/h2&amp;gt;
            &amp;lt;PostForm onSubmit={handleAddPost} initialValue={{}} /&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 코드에서 핵심은 &lt;strong&gt;useMutation&lt;/strong&gt; react-query 에서 제공해주는 hook 을 이용하여 데이터를 추가해주고 있는 부분입니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;useMutation 을 값을 바꿀때 사용하는 api 입니다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;앞서, &lt;code&gt;src/api/posts.jsx&lt;/code&gt; API 에서 만든 createPost 함수를 &lt;code&gt;mutationFn&lt;/code&gt; 에 넣어 주었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h3&gt;&lt;strong&gt;PostForm.jsx 컴포넌트 생성&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;앞서 페이지에서 사용하는 공통 컴포넌트 중 하나인 &lt;strong&gt;PostForm.jsx&lt;/strong&gt; 컴포넌트 코드입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useState } from &amp;quot;react&amp;quot;;

export default function PostForm({ onSubmit, initialValue }) {
    const [post, setPost] = useState({
        title: initialValue.title || &amp;quot;&amp;quot;,
        body: initialValue.body || &amp;quot;&amp;quot;,
    });

    const handleChangeInput = (e) =&amp;gt; {
        setPost({
            ...post,
            [e.target.name]: e.target.value,
        });
    };

    const handleSubmit = (e) =&amp;gt; {
        e.preventDefault();
        onSubmit(post);
        setPost({
            title: &amp;quot;&amp;quot;,
            body: &amp;quot;&amp;quot;,
        });
    };

    const renderField = (label) =&amp;gt; (
        &amp;lt;div&amp;gt;
            &amp;lt;label&amp;gt;{label}&amp;lt;/label&amp;gt;
            &amp;lt;input onChange={handleChangeInput} type=&amp;quot;text&amp;quot; name={label.toLowerCase()} value={post[label.toLowerCase()]} /&amp;gt;
        &amp;lt;/div&amp;gt;
    );

    return (
        &amp;lt;form onSubmit={handleSubmit}&amp;gt;
            {renderField(&amp;quot;Title&amp;quot;)}
            {renderField(&amp;quot;Body&amp;quot;)}
            &amp;lt;button type=&amp;quot;submit&amp;quot;&amp;gt;Submit&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;전체 프로젝트 구조&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이제 코드는 모두 작성 완료 하였습니다.&lt;/li&gt;
&lt;li&gt;완성된 프로젝트의 폴더 구조는 아래와 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmlQFr/btr63Zl882B/q4CdonAlV45GXSjei7wV1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmlQFr/btr63Zl882B/q4CdonAlV45GXSjei7wV1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmlQFr/btr63Zl882B/q4CdonAlV45GXSjei7wV1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmlQFr%2Fbtr63Zl882B%2Fq4CdonAlV45GXSjei7wV1k%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;실행 결과&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsT0vP/btr610MCrCZ/fkOlIhzsMF5uaawbKESL0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsT0vP/btr610MCrCZ/fkOlIhzsMF5uaawbKESL0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsT0vP/btr610MCrCZ/fkOlIhzsMF5uaawbKESL0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsT0vP%2Fbtr610MCrCZ%2FfkOlIhzsMF5uaawbKESL0K%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>웹 프로그래밍/React</category>
      <author>범범조조</author>
      <guid isPermaLink="true">https://afsdzvcx123.tistory.com/1519</guid>
      <comments>https://afsdzvcx123.tistory.com/entry/React-react-query-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0#entry1519comment</comments>
      <pubDate>Thu, 30 Mar 2023 20:17:44 +0900</pubDate>
    </item>
    <item>
      <title>[React] React Query 란?</title>
      <link>https://afsdzvcx123.tistory.com/entry/React-React-Query-%EB%9E%80</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Db4wF/btr60GafY7X/k5HCGSoinmDc0n6cocCko0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Db4wF/btr60GafY7X/k5HCGSoinmDc0n6cocCko0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Db4wF/btr60GafY7X/k5HCGSoinmDc0n6cocCko0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDb4wF%2Fbtr60GafY7X%2Fk5HCGSoinmDc0n6cocCko0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;참고&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tanstack.com/query/v3/&quot;&gt;https://tanstack.com/query/v3/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@94applekoo/React-Query-%EC%B4%9D-%EC%A0%95%EB%A6%AC&quot;&gt;https://velog.io/@94applekoo/React-Query-%EC%B4%9D-%EC%A0%95%EB%A6%AC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;React-Query 개요&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;React-Query 는 리액트 애플리케이션에서 &lt;code&gt;서버 상태 가져오기&lt;/code&gt;, &lt;code&gt;캐싱&lt;/code&gt;, &lt;code&gt;동기화 및 업데이트&lt;/code&gt;를 보다 쉽게 다룰 수 있도록 도와주는 라이브러리 입니다.&lt;/li&gt;
&lt;li&gt;클라이언트 상태와 서버 상태를 명확히 구분하기 위해 만들어 졌습니다.&lt;/li&gt;
&lt;li&gt;React-Query 에서는 기존 상태 관리 라이브러리인 &lt;code&gt;redux&lt;/code&gt;, &lt;code&gt;mobX&lt;/code&gt; 가 클라이언트 상태 작업에 적합하지만, 비동기 또는 서버 상태 작업에는 그다지 좋지는 않다고 언급 했습니다.&lt;/li&gt;
&lt;li&gt;클라이언트 상태와 서버 상태는 완전히 다른 개념이며, 클라이언트 상태는 각각의 input 값으로 예를 들 수 있고, 서버 상태는 데이터 베이스에 저장되어 있는 데이터로 예를 들 수 있습니다.&lt;/li&gt;
&lt;li&gt;Axios나 Fetch API와 같은 데이터 fetching 라이브러리와 원활하게 작동하도록 설계되어 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;React-Query 기능&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;React-Query 기능은 다음과 같습니다.&lt;ul&gt;
&lt;li&gt;캐싱&lt;/li&gt;
&lt;li&gt;동일한 데이터에 대한 중복 요청을 단일 요청으로 통합&lt;/li&gt;
&lt;li&gt;백그라운드에서 오래된 데이터 업데이트&lt;/li&gt;
&lt;li&gt;데이터가 얼마나 오래 되었는지 확인 가능&lt;/li&gt;
&lt;li&gt;데이터 업데이트 빠르게 반영&lt;/li&gt;
&lt;li&gt;페이지네이션 및 데이터 지연 로드와 같은 성능 최적화&lt;/li&gt;
&lt;li&gt;서버 상태의 메모리 및 가비지 수집 관리&lt;/li&gt;
&lt;li&gt;구조 공유를 사용하여 쿼리 결과를 메모화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;캐싱이란? 데이터를 임시로 저장하여 다음에 필요할 때 빠르게 가져올 수 있도록 하는 기법을 얘기 합니다. 데이터를 요청할 때 마다 API 에 요청하지 않고, 만약 이전에 저장해둔 데이터의 이력이 존재하면 해당 데이터를 사용할 수 있어서 응답 속도가 빠르다는 장점이 있습니다. React-Query 는 캐싱 기능을 제공하여 외부 API로부터 가져온 데이터를 캐싱하고 이를 재사용할 수 있다는 장점이 존재합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;React-Query 장단점&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;React-Query 가 주장하는 장단점은 다음과 같습니다.&lt;ul&gt;
&lt;li&gt;server-state (DB 데이터)를 프론트엔드에서 실시간 동기화해주는걸 도와준다고 합니다.&lt;/li&gt;
&lt;li&gt;하지만 ajax 요청을 몇 초마다 계속 날려서 가져오는 방식이라 좀 비효율적일 수도 있습니다.&lt;/li&gt;
&lt;li&gt;실시간 서버에서 데이터를 자주 보내려면 웹소켓이나 Server-sent events 같은 가벼운 방식들도 있습니다.&lt;/li&gt;
&lt;li&gt;그래서 React-Query는 ajax 관련 기능개발 편하게 할 수 있는데 더 의의가 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;타 라이브러리&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;React-Query 말고도 RTK Query 라는 라이브러리도 있습니다.&lt;/li&gt;
&lt;li&gt;Redux Toolkit 설치한 경우 RTK Query 라는 것도 기본적으로 사용이 가능합니다.&lt;/li&gt;
&lt;li&gt;하지만 RTK Query 는 React-Query 와 차이점이 있어서 해당 부분은 다음에 다시 정리 진행해 보도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>웹 프로그래밍/React</category>
      <author>범범조조</author>
      <guid isPermaLink="true">https://afsdzvcx123.tistory.com/1518</guid>
      <comments>https://afsdzvcx123.tistory.com/entry/React-React-Query-%EB%9E%80#entry1518comment</comments>
      <pubDate>Thu, 30 Mar 2023 20:13:45 +0900</pubDate>
    </item>
    <item>
      <title>[gRPC] C# gRPC Server/Client 생성하기</title>
      <link>https://afsdzvcx123.tistory.com/entry/gRPC-C-gRPC-ServerClient-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q3chf/btr0RIYMDaB/asZkgGZCpjDsOLkEnQfZi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q3chf/btr0RIYMDaB/asZkgGZCpjDsOLkEnQfZi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q3chf/btr0RIYMDaB/asZkgGZCpjDsOLkEnQfZi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq3chf%2Fbtr0RIYMDaB%2FasZkgGZCpjDsOLkEnQfZi1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;목적&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;C# 으로 gRPC 통신을 구현합니다.&lt;/li&gt;
&lt;li&gt;Server/Client 두 서비스 모두 C# 으로 작성합니다.&lt;/li&gt;
&lt;li&gt;그럼 C# 으로 간단한 gRPC 서비스를 만들어 보도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;개발 환경&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;개발 환경은 다음과 같습니다.&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OS : Windows 11&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.NET Version : .NET 6&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IDE : Visual Studio 2022&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;gRPC Server 구현&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRenJc/btr0RQ3xHJk/oB2PM692jNe7Fybp0EYhKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRenJc/btr0RQ3xHJk/oB2PM692jNe7Fybp0EYhKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRenJc/btr0RQ3xHJk/oB2PM692jNe7Fybp0EYhKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRenJc%2Fbtr0RQ3xHJk%2FoB2PM692jNe7Fybp0EYhKk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;그럼 먼저 gRPC C# Server 를 구현하는 방법에 대해서 알려 드리도록 하겠습니다.&lt;/li&gt;
&lt;li&gt;Visual Studio 2022 를 실행한 후, &lt;strong&gt;ASP.NET Core gRPC 서비스&lt;/strong&gt; 프로젝트를 생성합니다.&lt;/li&gt;
&lt;li&gt;gRPC 서비스를 생성하게 되면, 기본으로 &lt;strong&gt;Protos&lt;/strong&gt; 폴더가 생깁니다.&lt;/li&gt;
&lt;li&gt;Default 로 &lt;strong&gt;greet.proto&lt;/strong&gt; 라고 해서 프로토콜 버퍼가 생깁니다.&lt;/li&gt;
&lt;li&gt;저는 &lt;strong&gt;greet.proto&lt;/strong&gt; 파일을 사용하지 않고, 직접 간단한 Customer 프로토콜 버퍼를 생성해 해당 파일로 gRPC 통신을 해보려고 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2fjRj/btr0JAUQIF9/VDpZqCyxadJgQjwNKPNK50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2fjRj/btr0JAUQIF9/VDpZqCyxadJgQjwNKPNK50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2fjRj/btr0JAUQIF9/VDpZqCyxadJgQjwNKPNK50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2fjRj%2Fbtr0JAUQIF9%2FVDpZqCyxadJgQjwNKPNK50%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;customers.proto 파일 생성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;gRPC 서버/클라이언트 프로그램을 작성하기에 앞서, 예제로 사용하기 위한 &lt;strong&gt;customers.proto&lt;/strong&gt; 파일을 작성합니다.&lt;/li&gt;
&lt;li&gt;서비스의 이름은 Customer 이고, 현재 원격 메서드로 &lt;strong&gt;GetCustomerInfo&lt;/strong&gt; 메서드가 정의되어 있습니다.&lt;/li&gt;
&lt;li&gt;그럼 이제 해당 프로토콜 버퍼를 통해 통신하는 서버/클라이언트 프로그램을 만들어 보도록 하겠습니다.&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-proto&quot;&gt;syntax = &amp;quot;proto3&amp;quot;;

option csharp_namespace = &amp;quot;gRPCTest.Protos&amp;quot;;

service Customer{
   rpc GetCustomerInfo(CustomerLookupModel) returns (CustomerModel);
}

message CustomerLookupModel{
   int32 userId = 1;
}

message CustomerModel{
   string firstName = 1;
   string lastName = 2;
   string emailAddress = 3;
   bool isAlive = 4;
   int32 age = 5;
}&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;프로토콜 버퍼 제너레이터 하기&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;서버/클라이언트 프로그램을 작성하기에 앞서, 앞에서 만들었던 &lt;strong&gt;customers.proto&lt;/strong&gt; 파일을 컴파일 하여, C# 소스코드로 제너레이터 해야합니다.&lt;/li&gt;
&lt;li&gt;제너레이터 하는 방법은 &lt;strong&gt;customers.proto -&amp;gt; 마우스 우 클릭&lt;/strong&gt; 하여 속성 페이지에 접근합니다.&lt;/li&gt;
&lt;li&gt;그리고 &lt;strong&gt;Build Action 은 Protobuf Compiler, gRPC Stub Classes 는 Server Only&lt;/strong&gt; 로 설정하여 다시 재 빌드를 해주면 제너레이터가 된 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l5JmD/btr0JA1zsUq/FuSfYlGnkjxfkTFSfKwKR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l5JmD/btr0JA1zsUq/FuSfYlGnkjxfkTFSfKwKR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l5JmD/btr0JA1zsUq/FuSfYlGnkjxfkTFSfKwKR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl5JmD%2Fbtr0JA1zsUq%2FFuSfYlGnkjxfkTFSfKwKR1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;CustomerService 작성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;다음으로 CustomerService 클래스를 생성한 후, 코드를 작성합니다.&lt;/li&gt;
&lt;li&gt;해당 코드에서는 Client 로부터 받은 메시지를 Server 에서 처리하는 로직이 들어있습니다.&lt;/li&gt;
&lt;li&gt;아래 예제에서는, Client 로부터 받은 UserID 값에 따라 다른 사람의 이름을 Server 가 반환해 주고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;using Ecommerce;
using Grpc.Core;
using gRPCTest.Protos;

namespace gRPCTest.Services
{
    public class CustomerService : Customer.CustomerBase
    {
        private readonly ILogger&amp;lt;CustomerService&amp;gt; _logger;
        private List&amp;lt;Product&amp;gt; products = new();

        public CustomerService(ILogger&amp;lt;CustomerService&amp;gt; logger)
        {
            _logger = logger;
        }

        public override Task&amp;lt;CustomerModel&amp;gt; GetCustomerInfo(CustomerLookupModel request, ServerCallContext context)
        {
            CustomerModel output = new();

            if(request.UserId == 1)
            {
                output.FirstName = &amp;quot;Jamie&amp;quot;;
                output.LastName = &amp;quot;Smith&amp;quot;;
            }
            else if(request.UserId == 2)
            {
                output.FirstName = &amp;quot;Jane&amp;quot;;
                output.LastName = &amp;quot;Doe&amp;quot;;
            }
            else
            {
                output.FirstName = &amp;quot;Greg&amp;quot;;
                output.LastName = &amp;quot;Thomas&amp;quot;;
            }

            return Task.FromResult(output);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;파이프라인 추가&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이제 마지막으로 &lt;strong&gt;Program.cs&lt;/strong&gt; 에서 &lt;code&gt;app.MapGrpcService&amp;lt;CustomerServices&amp;gt;();&lt;/code&gt; 코드를 추가하여 CustomerServices 의 파이프라인을 추가합니다.&lt;/li&gt;
&lt;li&gt;이제 Server 쪽 준비는 모두 완료 되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;using gRPCTest.Services;

var builder = WebApplication.CreateBuilder(args);

// Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682

// Add services to the container.
builder.Services.AddGrpc();

var app = builder.Build();

// Configure the HTTP request pipeline.
app.MapGrpcService&amp;lt;CustomerService&amp;gt;();
app.MapGet(&amp;quot;/&amp;quot;, () =&amp;gt; &amp;quot;Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909&amp;quot;);

app.Run();&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;gRPC client 콘솔 프로젝트 생성 및 proto 파일 추가&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;앞서, gRPC Server 프로그램 로직을 모두 작성 완료 하였습니다.&lt;/li&gt;
&lt;li&gt;다음은 Client 프로그램을 만들 차례입니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;콘솔 프로젝트&lt;/strong&gt; 하나를 생성한 후, 앞서 Server 에서 사용하였던 &lt;strong&gt;customer.proto&lt;/strong&gt; 파일을 그대로 복사 붙여넣기 해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-proto&quot;&gt;syntax = &amp;quot;proto3&amp;quot;;

option csharp_namespace = &amp;quot;gRPCTest.Protos&amp;quot;;

service Customer{
   rpc GetCustomerInfo(CustomerLookupModel) returns (CustomerModel);
}

message CustomerLookupModel{
   int32 userId = 1;
}

message CustomerModel{
   string firstName = 1;
   string lastName = 2;
   string emailAddress = 3;
   bool isAlive = 4;
   int32 age = 5;
}&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;NuGet Package 설치&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Client 프로그램에서 gRPC 서비스를 사용하려면 총 3개의 gRPC 관련 NuGet Package를 설치해야 합니다.&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Google.Protobuf&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Grpc.Net.Client&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Grpc.Tools&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위 3개의 NuGet Package를 미리 설치해 주시기 바랍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dckKGU/btr0ObAchNQ/vrCintXZySEHv2rkAyF6jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dckKGU/btr0ObAchNQ/vrCintXZySEHv2rkAyF6jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dckKGU/btr0ObAchNQ/vrCintXZySEHv2rkAyF6jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdckKGU%2Fbtr0ObAchNQ%2FvrCintXZySEHv2rkAyF6jk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;client C# 프로젝트에서 GrpcService 속성 변경&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;다음으로 생성했던 gRPCClient.csproj 파일에 가서 &lt;code&gt;&amp;lt;Protobuf..&amp;gt;&lt;/code&gt; 로 시작하는 속성에서 &lt;code&gt;GrpcService&lt;/code&gt; 속성을 &lt;code&gt;Server -&amp;gt; Client&lt;/code&gt; 로 변경해 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVb1LG/btr0RPwPxmM/JjDujUlFROLQEb0Yc01qA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVb1LG/btr0RPwPxmM/JjDujUlFROLQEb0Yc01qA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVb1LG/btr0RPwPxmM/JjDujUlFROLQEb0Yc01qA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVb1LG%2Fbtr0RPwPxmM%2FJjDujUlFROLQEb0Yc01qA1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;프로토콜 버퍼 제너레이터 하기&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;서버와 마찬가지로, Client 에서 추가했던 &lt;strong&gt;customers.proto&lt;/strong&gt; 파일을 C# 코드로 작성되게 끔 제너레이터 작업을 진행해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;ul&gt;
&lt;li&gt;제너레이터 하는 방법은 &lt;strong&gt;customers.proto -&amp;gt; 마우스 우 클릭&lt;/strong&gt; 하여 속성 페이지에 접근합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그리고 &lt;strong&gt;Build Action 은 Protobuf Compiler, gRPC Stub Classes 는 Client Only&lt;/strong&gt; 로 설정하여 다시 재 빌드를 해주면 제너레이터가 된 것입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7dzQp/btr02yVu25P/1KTQ6K87esEdxCQVrRv2E0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7dzQp/btr02yVu25P/1KTQ6K87esEdxCQVrRv2E0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7dzQp/btr02yVu25P/1KTQ6K87esEdxCQVrRv2E0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7dzQp%2Fbtr02yVu25P%2F1KTQ6K87esEdxCQVrRv2E0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;Client 통신 코드 작성&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Program.cs&lt;/strong&gt; 에 Server 에게 request 를 보내고, 다시 Server 가 반환한 객체를 출력하는 것까지 소스코드를 작성해 보도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;using Grpc.Net.Client;
using gRPCTest.Protos;

var channel = GrpcChannel.ForAddress(&amp;quot;https://localhost:7223&amp;quot;);
var customerClient = new Customer.CustomerClient(channel);

var clientRequested = new CustomerLookupModel { UserId = 3 };

var customer = await customerClient.GetCustomerInfoAsync(clientRequested);

Console.WriteLine($&amp;quot;{customer.FirstName} {customer.LastName}&amp;quot;);

Console.ReadLine();&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;실행 방법&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;실행 방법은 &lt;strong&gt;Server&lt;/strong&gt; 가 우선 실행되어 있어야 합니다.&lt;/li&gt;
&lt;li&gt;그리고 나서, &lt;strong&gt;Client&lt;/strong&gt; 프로그램을 실행하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chP1UT/btr0RIEudlt/PSkWd4c93e5d2KHuxxalT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chP1UT/btr0RIEudlt/PSkWd4c93e5d2KHuxxalT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chP1UT/btr0RIEudlt/PSkWd4c93e5d2KHuxxalT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchP1UT%2Fbtr0RIEudlt%2FPSkWd4c93e5d2KHuxxalT0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;실행 결과&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;실행 결과, Client 가 Server 로 전송하는 UserId 값에 따라 그에 맞는 User 이름이 반환되어 출력되는 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Greg Thomas&lt;/code&gt;&lt;/pre&gt;</description>
      <category>gRPC</category>
      <author>범범조조</author>
      <guid isPermaLink="true">https://afsdzvcx123.tistory.com/1516</guid>
      <comments>https://afsdzvcx123.tistory.com/entry/gRPC-C-gRPC-ServerClient-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0#entry1516comment</comments>
      <pubDate>Tue, 21 Mar 2023 00:59:13 +0900</pubDate>
    </item>
    <item>
      <title>[PostgreSQL] PostgreSQL 에서 JSONB 사용한 CRUD</title>
      <link>https://afsdzvcx123.tistory.com/entry/PostgreSQL-PostgreSQL-%EC%97%90%EC%84%9C-JSONB-%EC%82%AC%EC%9A%A9%ED%95%9C-CRUD</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X3lOW/btr0RPwPugW/Fuao9v9QE9u8PVbjxqMqZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X3lOW/btr0RPwPugW/Fuao9v9QE9u8PVbjxqMqZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X3lOW/btr0RPwPugW/Fuao9v9QE9u8PVbjxqMqZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX3lOW%2Fbtr0RPwPugW%2FFuao9v9QE9u8PVbjxqMqZ0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;참조&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.idalko.com/crud-operations-postgres-jsonb/&quot;&gt;참고 사이트&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;소개&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;요즘 데이터를 전송하는 가장 일반적인 방법은 JSON(JavaScript Object Notation) 을 사용하는 것입니다.&lt;/li&gt;
&lt;li&gt;Postgres 9.5 이상 부터는 JSON을 저장 및 가져올 수 있을 뿐만 아니라 JSON 구조를 기반으로 작업을 수행할 수 있도록 새로운 &lt;strong&gt;JSONB&lt;/strong&gt; 유형의 Column이 새롭게 도입되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;Use case&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;다음 User를 등록하는 간단한 애플리케이션이 있습니다.&lt;/li&gt;
&lt;li&gt;응용프로그램을 개선하고 사용자 인터페이스(테마, 아이콘, 텍스트 크기)를 구성하는 기능을 추가한다고 가정합니다.&lt;/li&gt;
&lt;li&gt;각 사용자는 여러 개의 구성을 가질 수 있으며 이러한 구성을 전환할 수도 있습니다.&lt;/li&gt;
&lt;li&gt;위 내용을 토대로 작업할 JSON은 다음과 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
    &amp;quot;userid&amp;quot;:&amp;quot;artur@gmail.com&amp;quot;,
    &amp;quot;configurations&amp;quot;:[
        {
            &amp;quot;name&amp;quot;:&amp;quot;myconf&amp;quot;,
            &amp;quot;theme&amp;quot;:&amp;quot;light&amp;quot;,
            &amp;quot;icons&amp;quot;:&amp;quot;small&amp;quot;,
            &amp;quot;textsize&amp;quot;:&amp;quot;large&amp;quot;
        },
        {
            &amp;quot;name&amp;quot;:&amp;quot;myconf2&amp;quot;,
            &amp;quot;theme&amp;quot;:&amp;quot;dark&amp;quot;
        }
    ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;Creating a table&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;JSON 구성을 저장할 테이블을 생성합니다.&lt;/li&gt;
&lt;li&gt;저는 pgAdmin GUI 도구를 가지고 테이블을 생성하였습니다.&lt;/li&gt;
&lt;li&gt;만약 SQL 구문을 통해 테이블을 생성하려면 다음과 같이 SQL 쿼리를 작성하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE USER_CONFIGURATIONS (
  ID         BIGSERIAL PRIMARY KEY,
  DATA       JSONB
);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwiodo/btr0JBTGVSo/wNVcJiDqTH0ntEd3Aha4k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwiodo/btr0JBTGVSo/wNVcJiDqTH0ntEd3Aha4k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwiodo/btr0JBTGVSo/wNVcJiDqTH0ntEd3Aha4k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwiodo%2Fbtr0JBTGVSo%2FwNVcJiDqTH0ntEd3Aha4k1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U86pY/btr0NIEJFtg/zw6Ez07yhjD2i4PNkTFsb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U86pY/btr0NIEJFtg/zw6Ez07yhjD2i4PNkTFsb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U86pY/btr0NIEJFtg/zw6Ez07yhjD2i4PNkTFsb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU86pY%2Fbtr0NIEJFtg%2Fzw6Ez07yhjD2i4PNkTFsb1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcIvsa/btr0RPDypYa/yPRwT6ph5zhE5kgAZsUegk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcIvsa/btr0RPDypYa/yPRwT6ph5zhE5kgAZsUegk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcIvsa/btr0RPDypYa/yPRwT6ph5zhE5kgAZsUegk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcIvsa%2Fbtr0RPDypYa%2FyPRwT6ph5zhE5kgAZsUegk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;CRUD. Create&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;사용자가 응용 프로그램에 등록할 때 JSON 배열로 &lt;code&gt;USER_CONFIGRATIONS&lt;/code&gt; 테이블에 레코드를 생성한다고 가정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;INSERT INTO USER_CONFIGURATIONS
(DATA)
VALUES(&amp;#39;{&amp;quot;userid&amp;quot;:&amp;quot;artur@gmail.com&amp;quot;, &amp;quot;configurations&amp;quot;:[]}&amp;#39;::jsonb);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;데이터가 성공적으로 추가 되었는지 아래 SQL 문을 통해 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT * FROM USER_CONFIGURATIONS;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqPAnt/btr0KRotJeg/tqDqTgFzoRIRoZen5ZM1mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqPAnt/btr0KRotJeg/tqDqTgFzoRIRoZen5ZM1mk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqPAnt/btr0KRotJeg/tqDqTgFzoRIRoZen5ZM1mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqPAnt%2Fbtr0KRotJeg%2FtqDqTgFzoRIRoZen5ZM1mk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;테이블에 entity를 하나 더 추가해 보도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;INSERT INTO USER_CONFIGURATIONS
(DATA)
VALUES(&amp;#39;{&amp;quot;userid&amp;quot;:&amp;quot;ihor@gmail.com&amp;quot;,
&amp;quot;configurations&amp;quot;:[{ &amp;quot;name&amp;quot;:&amp;quot;myconf&amp;quot;, &amp;quot;theme&amp;quot;:&amp;quot;light&amp;quot;, &amp;quot;icons&amp;quot;:&amp;quot;small&amp;quot;, &amp;quot;textsize&amp;quot;:&amp;quot;large&amp;quot; }, { &amp;quot;name&amp;quot;:&amp;quot;myconf2&amp;quot;, &amp;quot;theme&amp;quot;:&amp;quot;dark&amp;quot; }]}&amp;#39;::jsonb);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;데이터가 성공적으로 추가된 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pV8aW/btr0WewjeyO/VuKpyuT7PTEHK71pCdcWu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pV8aW/btr0WewjeyO/VuKpyuT7PTEHK71pCdcWu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pV8aW/btr0WewjeyO/VuKpyuT7PTEHK71pCdcWu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpV8aW%2Fbtr0WewjeyO%2FVuKpyuT7PTEHK71pCdcWu0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;CRUD. Read&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;때에 따라서 사용자 ID 별로 모든 구성을 가져 올 수 있어야 합니다.&lt;/li&gt;
&lt;li&gt;예를 들어, &lt;strong&gt;&lt;a href=&quot;mailto:ihor@gmail.com&quot;&gt;ihor@gmail.com&lt;/a&gt;&lt;/strong&gt; 과 관련된 모든 구성을 가져오고 싶으면 다음과 같이 SQL문을 작성하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT DATA -&amp;gt; &amp;#39;configurations&amp;#39; AS configs
FROM USER_CONFIGURATIONS
WHERE 1 = 1
AND (DATA -&amp;gt;&amp;gt; &amp;#39;userid&amp;#39;) = &amp;#39;ihor@gmail.com&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;실행 결과, userid 가 &lt;code&gt;ihor@gmail.com&lt;/code&gt; 사람의 정보가 검색되어 나오는 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S1UGg/btr0KGApMfX/h6I1tjFEmS1nGKzc43QHbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S1UGg/btr0KGApMfX/h6I1tjFEmS1nGKzc43QHbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S1UGg/btr0KGApMfX/h6I1tjFEmS1nGKzc43QHbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS1UGg%2Fbtr0KGApMfX%2Fh6I1tjFEmS1nGKzc43QHbK%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Read 쿼리 분석&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;앞서 아래 쿼리문을 통해 JSONB 데이터를 검색하였습니다.&lt;/li&gt;
&lt;li&gt;좀 더 자세히 SQL 문을 분석해 보도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT DATA -&amp;gt; &amp;#39;configurations&amp;#39; AS configs
FROM USER_CONFIGURATIONS
WHERE 1 = 1
AND (DATA -&amp;gt;&amp;gt; &amp;#39;userid&amp;#39;) = &amp;#39;ihor@gmail.com&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drRyAK/btr0NNF4AWi/n08MKYESqI4qd8ulujJdfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drRyAK/btr0NNF4AWi/n08MKYESqI4qd8ulujJdfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drRyAK/btr0NNF4AWi/n08MKYESqI4qd8ulujJdfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrRyAK%2Fbtr0NNF4AWi%2Fn08MKYESqI4qd8ulujJdfk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JSONB 열 유형으로 작업할 때 &lt;code&gt;-&amp;gt;&lt;/code&gt; 및 &lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt; 와 같은 추가 기능을 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;둘 다 오른쪽에 지정된 이름으로 JSON 객체의 내용을 반환합니다.&lt;/li&gt;
&lt;li&gt;이러한 함수의 차이점은 반환의 유형입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-&amp;gt;&lt;/code&gt; 는 텍스트를 반환하고 &lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt; 는 JSONB 를 반환합니다.&lt;/li&gt;
&lt;li&gt;이 외에도 이름으로 사용자의 구성을 찾을 수 있어야 합니다.&lt;/li&gt;
&lt;li&gt;아래와 같이 SQL 문을 작성할 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT config as congifuration
FROM USER_CONFIGURATIONS
  CROSS JOIN jsonb_array_elements(DATA -&amp;gt; &amp;#39;configurations&amp;#39;) config
  WHERE (DATA -&amp;gt;&amp;gt; &amp;#39;userid&amp;#39;) = &amp;#39;ihor@gmail.com&amp;#39;
  AND (config -&amp;gt;&amp;gt; &amp;#39;name&amp;#39;) = &amp;#39;myconf&amp;#39;;&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;CRUD. Update&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;다음은 Update 구문 입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;artur@gmail.com&lt;/code&gt; 을 추가해 보도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;UPDATE USER_CONFIGURATIONS
SET DATA =
jsonb_set(DATA, &amp;#39;{configurations}&amp;#39;::text[], DATA -&amp;gt;&amp;#39;configurations&amp;#39; || &amp;#39;{&amp;quot;name&amp;quot;:&amp;quot;firstconf&amp;quot;, &amp;quot;theme&amp;quot;:&amp;quot;dark&amp;quot;, &amp;quot;textsize&amp;quot;:&amp;quot;large&amp;quot;}&amp;#39;::jsonb)
WHERE 1 = 1
AND (DATA -&amp;gt;&amp;gt; &amp;#39;userid&amp;#39;) = &amp;#39;artur@gmail.com&amp;#39;;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;데이터 조회 결과, 정상적으로 데이터가 Update 된 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuBQhw/btr0HzCiWCO/wkmIu5pMlYX9Kehrl3ejA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuBQhw/btr0HzCiWCO/wkmIu5pMlYX9Kehrl3ejA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuBQhw/btr0HzCiWCO/wkmIu5pMlYX9Kehrl3ejA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuBQhw%2Fbtr0HzCiWCO%2FwkmIu5pMlYX9Kehrl3ejA0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h3&gt;&lt;strong&gt;Update 쿼리 분석&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;UPDATE USER_CONFIGURATIONS
SET DATA =
jsonb_set(DATA, &amp;#39;{configurations}&amp;#39;::text[], DATA -&amp;gt;&amp;#39;configurations&amp;#39; || &amp;#39;{&amp;quot;name&amp;quot;:&amp;quot;firstconf&amp;quot;, &amp;quot;theme&amp;quot;:&amp;quot;dark&amp;quot;, &amp;quot;textsize&amp;quot;:&amp;quot;large&amp;quot;}&amp;#39;::jsonb)
WHERE 1 = 1
AND (DATA -&amp;gt;&amp;gt; &amp;#39;userid&amp;#39;) = &amp;#39;artur@gmail.com&amp;#39;;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위에서 &lt;code&gt;jsonb_set(target jsonb, path text[], new_value jsonb, create_missing boolean)&lt;/code&gt; 함수를 사용하고 있습니다.&lt;/li&gt;
&lt;li&gt;Data를 대상 JSONB 객체로 전달하고 &amp;#39;configurations&amp;#39; JSON 객체를 정보를 교체하려는 전달로 지정하고 &amp;#39;구성&amp;#39; JSON 객체에 대한 새 값을 정의합니다.&lt;/li&gt;
&lt;li&gt;함수 &lt;code&gt;||&lt;/code&gt; 는 연결 함수 입니다.&lt;/li&gt;
&lt;li&gt;따라서 여기에서 이전 &amp;#39;configurations&amp;#39; 값을 가져와 새 값과 연결합니다.&lt;/li&gt;
&lt;li&gt;그 후, 초기 DATA JSON에서 &amp;#39;configurations&amp;#39; 객체를 UPDATE 하고 DATA JSON을 업데이트 된 것으로 교체합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;CRUD. Delete&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;삭제 작업은 간단합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;DELETE FROM USER_CONFIGURATIONS
WHERE (DATA -&amp;gt;&amp;gt; &amp;#39;userid&amp;#39;) = &amp;#39;ihor@gmail.com&amp;#39;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Database(데이터베이스)</category>
      <author>범범조조</author>
      <guid isPermaLink="true">https://afsdzvcx123.tistory.com/1515</guid>
      <comments>https://afsdzvcx123.tistory.com/entry/PostgreSQL-PostgreSQL-%EC%97%90%EC%84%9C-JSONB-%EC%82%AC%EC%9A%A9%ED%95%9C-CRUD#entry1515comment</comments>
      <pubDate>Mon, 20 Mar 2023 00:56:41 +0900</pubDate>
    </item>
    <item>
      <title>[C#] EF Core - Fluent API 1:N 관계</title>
      <link>https://afsdzvcx123.tistory.com/entry/C-EF-Core-Fluent-API-1N-%EA%B4%80%EA%B3%84</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfJ1hl/btr0HtPBO1D/10hHLmk0RUg9wuzruEwOsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfJ1hl/btr0HtPBO1D/10hHLmk0RUg9wuzruEwOsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfJ1hl/btr0HtPBO1D/10hHLmk0RUg9wuzruEwOsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfJ1hl%2Fbtr0HtPBO1D%2F10hHLmk0RUg9wuzruEwOsk%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;참조&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.entityframeworktutorial.net/efcore/configure-one-to-many-relationship-using-fluent-api-in-ef-core.aspx&quot;&gt;참고 사이트&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;소개&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;일반적으로 EF Core 에는 자동으로 구성할 수 있는 충분한 규칙이 포함되어 있으므로 1:N 관계를 구성할 필요가 없습니다.&lt;/li&gt;
&lt;li&gt;그러나 쉬운 유지 관리를 위해 Fluent API 에 모든 EF 구성을 포함하기로 결정한 경우 Fluent API를 사용하여 1:N 관계를 구성할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;예제&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;EF Core를 사용하면 Fluent API를 사용하여 관계를 쉽게 구성할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Grade Entity가 많은 Student Entity를 포함하는 다음 Student 및 Grade 클래스를 보시면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int CurrentGradeId { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection&amp;lt;Student&amp;gt; Students { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;아래와 같이 Context 클래스에서 OnModelCreating 메서드를 재정의하여 Fluent API를 사용하여 위 Entity에 대한 1:N 관계를 구성할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;using Microsoft.EntityFrameworkCore;

namespace StudentApp.Data
{
    public class SchoolContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseNpgsql(@&amp;quot;Server=localhost;Database=JoBeomHee;Port=5432;User Id=JoBeomHee;Password=1234&amp;quot;);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity&amp;lt;Student&amp;gt;()
                .HasOne&amp;lt;Grade&amp;gt;(s =&amp;gt; s.Grade)
                .WithMany(g =&amp;gt; g.Students)
                .HasForeignKey(s =&amp;gt; s.CurrentGradeId);
        }

        public DbSet&amp;lt;Grade&amp;gt; Grades { get; set; }    
        public DbSet&amp;lt;Student&amp;gt; Students { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 예에서 다음 코드는 1:N 관계를 구성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;modelBuilder.Entity&amp;lt;Student&amp;gt;()
            .HasOne&amp;lt;Grade&amp;gt;(s =&amp;gt; s.Grade)
            .WithMany(g =&amp;gt; g.Students)
            .HasForeignKey(s =&amp;gt; s.CurrentGradeId);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이제 데이터베이스에 반영하기 위해 마이그레이션 명령인 &lt;strong&gt;Add-Migration &lt;name&gt;&lt;/strong&gt; 및 &lt;strong&gt;update-database&lt;/strong&gt; 를 실행합니다.&lt;/li&gt;
&lt;li&gt;마이그레이션이 정상적으로 실행 되었다면, 데이터베이스에는 아래와 같이 1:N 관계가 있는 2개의 Student, Grade 테이블이 생성된 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ9sxS/btr0JAHh5oX/ysjjiC6KGlmLXjybGYkK41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ9sxS/btr0JAHh5oX/ysjjiC6KGlmLXjybGYkK41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ9sxS/btr0JAHh5oX/ysjjiC6KGlmLXjybGYkK41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ9sxS%2Fbtr0JAHh5oX%2FysjjiC6KGlmLXjybGYkK41%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;&lt;strong&gt;코드 관계 해석&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;첫째로, Student 또는 Grade 중 하나의 Entity Class로 구성을 시작해야 합니다.&lt;/li&gt;
&lt;li&gt;따라서 &lt;code&gt;modelBuilder.Entity&amp;lt;student&amp;gt;()&lt;/code&gt; 는 Student Entity로 시작합니다.&lt;/li&gt;
&lt;li&gt;두 번째로, &lt;code&gt;.HasOne&amp;lt;Grade&amp;gt;(s =&amp;gt; s.Grade)&lt;/code&gt; 는 Student Entity에 Grade라는 Grade 유형 속성이 포함 되도록 지정합니다.&lt;/li&gt;
&lt;li&gt;세 번째로, 관계의 다른 쪽 끝인 Grade 엔티티를 구성해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.WithMany(g =&amp;gt; g.Stduents)&lt;/code&gt; 는 Grade Entity 클래스에 많은 Student Entity가 포함되도록 지정합니다.&lt;/li&gt;
&lt;li&gt;여기서 WithMany는 컬렉션 탐색 속성을 유추합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.HasForeignKey&amp;lt;int&amp;gt;(s =&amp;gt; s.CurrentGradeId);&lt;/code&gt; 외래 키 속성 CurrentGradeId의 이름을 지정합니다.&lt;/li&gt;
&lt;li&gt;외래키 지정은 선택사항 입니다. 종속 클래스에 외래 키 Id 속성이 있는 경우에만 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cs1HHu/btr0Ob7ZmNI/KAbYq5cL2qqPimwsGAwLW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cs1HHu/btr0Ob7ZmNI/KAbYq5cL2qqPimwsGAwLW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cs1HHu/btr0Ob7ZmNI/KAbYq5cL2qqPimwsGAwLW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcs1HHu%2Fbtr0Ob7ZmNI%2FKAbYq5cL2qqPimwsGAwLW1%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>C#</category>
      <author>범범조조</author>
      <guid isPermaLink="true">https://afsdzvcx123.tistory.com/1514</guid>
      <comments>https://afsdzvcx123.tistory.com/entry/C-EF-Core-Fluent-API-1N-%EA%B4%80%EA%B3%84#entry1514comment</comments>
      <pubDate>Sun, 19 Mar 2023 00:54:36 +0900</pubDate>
    </item>
  </channel>
</rss>