<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발일지</title>
    <link>https://make-somthing.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 8 May 2026 18:46:22 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>찰리-누나</managingEditor>
    <image>
      <title>개발일지</title>
      <url>https://tistory1.daumcdn.net/tistory/5772926/attach/5e630da335ac47aa89c5014eefee3aae</url>
      <link>https://make-somthing.tistory.com</link>
    </image>
    <item>
      <title>[ Vite + React + Vscode ] 절대 경로 별칭 @로 자동 import 하기</title>
      <link>https://make-somthing.tistory.com/113</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DiQ6n/btsLJLXYD7T/4EW6jTkZfTj0r4zwktw8v1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DiQ6n/btsLJLXYD7T/4EW6jTkZfTj0r4zwktw8v1/img.png&quot; data-alt=&quot;난리났음&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DiQ6n/btsLJLXYD7T/4EW6jTkZfTj0r4zwktw8v1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDiQ6n%2FbtsLJLXYD7T%2F4EW6jTkZfTj0r4zwktw8v1%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;591&quot; height=&quot;395&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;난리났음&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동 import의 상대경로들이 너무 지저분해보이기도 하고, 한번에 경로를 알아보기가 힘든 것 같아 import를 절대 경로로 바꾸어주기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분 글을 검색하면 tsconfig.json 과 vite.config.ts 파일 설정법만 알려주기에, vscode의 기능을 활용하여 자동 import 시에도 절대 경로를 설정하는 법을 기록한다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;b&gt;VS Code 설정 변경&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. typescript.preferences.importModuleSpecifier 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS Code는 기본적으로 &lt;b&gt;상대 경로&lt;/b&gt;를 선호하도록 설정되어 있다. 이를 &lt;b&gt;절대 경로&lt;/b&gt;로 변경하려면 VS Code 설정을 업데이트해야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;VS Code 설정 열기&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ctrl + , (Windows) 또는 Cmd + , (macOS)를 눌러 설정 창을 연다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;검색창에 &lt;b&gt;importModuleSpecifier&lt;/b&gt; 입력.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;typescript.preferences.importModuleSpecifier&lt;/b&gt;를&lt;b&gt; non-relative&lt;/b&gt;로 설정한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bS5qTZ/btsLKM2R19N/w2yq0pO8AMXmli3TMU43DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bS5qTZ/btsLKM2R19N/w2yq0pO8AMXmli3TMU43DK/img.png&quot; data-alt=&quot;typescript.preferences.importModuleSpecifier를 non-relative로 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bS5qTZ/btsLKM2R19N/w2yq0pO8AMXmli3TMU43DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS5qTZ%2FbtsLKM2R19N%2Fw2yq0pO8AMXmli3TMU43DK%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;759&quot; height=&quot;542&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;typescript.preferences.importModuleSpecifier를 non-relative로 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Vite.config.ts&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1.라이브러리 설치&amp;nbsp;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;먼저, 필요한 라이브러리를 설치한다. svgr은 svg를 컴포넌트처럼 사용할 수 있게 해주는 모듈인데, 거의 필수적으로 사용하는 편이라 함께 설치하였다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736712795499&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install @types/node 
npm install vite-tsconfig-paths // tsconfig의 paths를 적용시켜주는 플러그인
npm install --save-dev vite-plugin-svgr // svg 관련 플러그인&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1.vite.config.ts에 내용 작성&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;'@' 별칭이 'src' 폴더로 시작하는 경로들을 가리키도록 설정한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;따로 별칭을 지어주고 싶은 경로가 있다면 이 곳에서 하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736712497835&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { defineConfig } from &quot;vite&quot;;
import react from &quot;@vitejs/plugin-react-swc&quot;;
import svgr from &quot;vite-plugin-svgr&quot;;
import tsconfigPaths from &quot;vite-tsconfig-paths&quot;;
import path from &quot;path&quot;;

// https://vite.dev/config/
export default defineConfig({
  envDir: &quot;.env&quot;,
  plugins: [react(), svgr(), tsconfigPaths()],
  resolve: {
    alias: {
      &quot;@&quot;: path.resolve(__dirname, &quot;src&quot;),
    },
  },
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3.&lt;b&gt;&lt;span&gt; tsconfig.json&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1.파일 수정&amp;nbsp;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기본적으로 적혀있는 json 파일에, 아래 내용을 추가한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736713019276&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;.&quot;, // 프로젝트 루트를 기준으로 경로 설정
    &quot;paths&quot;: {
      &quot;@/*&quot;: [&quot;src/*&quot;] // '@' 별칭을 'src'로 매핑
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1736713060172&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;files&quot;: [],
  &quot;references&quot;: [
    { &quot;path&quot;: &quot;./tsconfig.app.json&quot; },
    { &quot;path&quot;: &quot;./tsconfig.node.json&quot; }
  ],
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;.&quot;, // 프로젝트 루트를 기준으로 경로 설정
    &quot;paths&quot;: {
      &quot;@/*&quot;: [&quot;src/*&quot;] // '@' 별칭을 'src'로 매핑
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4.&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;tsconfig.app.json&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1.파일 수정&amp;nbsp;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기본적으로 적혀있는 json 파일에, 아래 내용을 추가한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736713430068&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 추가설정 */
    &quot;baseUrl&quot;: &quot;.&quot;,
    &quot;paths&quot;: {
      &quot;@/*&quot;: [&quot;./src/*&quot;]
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736713441763&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;tsBuildInfoFile&quot;: &quot;./node_modules/.tmp/tsconfig.app.tsbuildinfo&quot;,
    &quot;target&quot;: &quot;ES2020&quot;,
    &quot;useDefineForClassFields&quot;: true,
    &quot;lib&quot;: [&quot;ES2020&quot;, &quot;DOM&quot;, &quot;DOM.Iterable&quot;],
    &quot;module&quot;: &quot;ESNext&quot;,
    &quot;skipLibCheck&quot;: true,

    /* Bundler mode */
    &quot;moduleResolution&quot;: &quot;bundler&quot;,
    &quot;allowImportingTsExtensions&quot;: true,
    &quot;isolatedModules&quot;: true,
    &quot;moduleDetection&quot;: &quot;force&quot;,
    &quot;noEmit&quot;: true,
    &quot;jsx&quot;: &quot;react-jsx&quot;,

    /* Linting */
    &quot;strict&quot;: true,
    &quot;noUnusedLocals&quot;: true,
    &quot;noUnusedParameters&quot;: true,
    &quot;noFallthroughCasesInSwitch&quot;: true,
    &quot;noUncheckedSideEffectImports&quot;: true,

    /* 추가설정 */
    &quot;baseUrl&quot;: &quot;.&quot;,
    &quot;paths&quot;: {
      &quot;@/*&quot;: [&quot;./src/*&quot;]
    }
  },
  &quot;include&quot;: [&quot;src&quot;]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/src/components 폴더에 Test.tsx 파일을 만들고, 테스트 컴포넌트를 import 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rmU9P/btsLJ9RPzLJ/Lamc3nKVnpAKOkrOKYaoB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rmU9P/btsLJ9RPzLJ/Lamc3nKVnpAKOkrOKYaoB1/img.png&quot; data-alt=&quot;자동 import에서 @/components 경로로 뜨는 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rmU9P/btsLJ9RPzLJ/Lamc3nKVnpAKOkrOKYaoB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrmU9P%2FbtsLJ9RPzLJ%2FLamc3nKVnpAKOkrOKYaoB1%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;849&quot; height=&quot;276&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자동 import에서 @/components 경로로 뜨는 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;663&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjapO1/btsLLuUX7uC/bLkr6KnpVOuVk1IpCa3Hp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjapO1/btsLLuUX7uC/bLkr6KnpVOuVk1IpCa3Hp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjapO1/btsLLuUX7uC/bLkr6KnpVOuVk1IpCa3Hp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjapO1%2FbtsLLuUX7uC%2FbLkr6KnpVOuVk1IpCa3Hp0%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;700&quot; height=&quot;663&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;663&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@ 별칭을 이용한 절대 경로로 잘 import 되는 것을 확인할 수 있다. 끝 ~&amp;nbsp;&lt;/p&gt;</description>
      <category>REACT/React-개념정리</category>
      <author>찰리-누나</author>
      <guid isPermaLink="true">https://make-somthing.tistory.com/113</guid>
      <comments>https://make-somthing.tistory.com/113#entry113comment</comments>
      <pubDate>Mon, 13 Jan 2025 05:30:18 +0900</pubDate>
    </item>
    <item>
      <title>[SpringBoot] Maria DB 로컬 호스팅 및 JDBC, JPA, lombok // CamelCase 유지하기</title>
      <link>https://make-somthing.tistory.com/112</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강의는 Azure를 이용한 MySQL 호스팅을 사용할 것으로 되어 있지만, 무료 데이터베이스 툴인 Maria가 Spring과 함께 많이 사용되고 있는데다 MySQL은 이미 전 회사에서도(ㅎㅎ) 지겹게 경험한 적이 있어 Maria로 실습하기 위해 과정을 남겨 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;! 시작하기 전에, lombok을 사용하면 마지막에 Getter와 Setter를 자동으로 만들게 해 주는 어노테이션을 사용할 수 있어 편하니 설치를 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntelliJ를 사용할 경우, Setting -&amp;gt; Plugins에서 lombok을 검색하면 Install 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3gQDf/btsLEYBrIaZ/vP0OTUs7K1STkt5nE8tfC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3gQDf/btsLEYBrIaZ/vP0OTUs7K1STkt5nE8tfC0/img.png&quot; data-alt=&quot;롬복 플러그인을 설치해준다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3gQDf/btsLEYBrIaZ/vP0OTUs7K1STkt5nE8tfC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3gQDf%2FbtsLEYBrIaZ%2FvP0OTUs7K1STkt5nE8tfC0%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;1234&quot; height=&quot;736&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;736&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;롬복 플러그인을 설치해준다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 롬복을 정상적으로 사용하기 위하여 annotation processors라는 메뉴에서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enable annotation processing 항목을 체크해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kyfJW/btsLDNt4TCW/0RQFxoJn6MwUFsifpkqVB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kyfJW/btsLDNt4TCW/0RQFxoJn6MwUFsifpkqVB0/img.png&quot; data-alt=&quot;들어가자마자 바로 보임!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kyfJW/btsLDNt4TCW/0RQFxoJn6MwUFsifpkqVB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkyfJW%2FbtsLDNt4TCW%2F0RQFxoJn6MwUFsifpkqVB0%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;1238&quot; height=&quot;738&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;738&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;들어가자마자 바로 보임!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사전 준비는 끝났다. 그럼 이제 마리아 DB를 설치하고, 데이터를 만든 다음, JDBC로 스프링 부트 프로젝트에 연결하여, JPA로 자바 언어를 이용해 SQL DB를 소스코드 내에서 다루어 보자.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 마리아 DB를 설치한다.&lt;/b&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마리아 DB는 아래 웹사이트에서 다운로드 할 수 있다.&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mariadb.com/kb/en/postdownload/mariadb-server-11-6-2/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mariadb.com/kb/en/postdownload/mariadb-server-11-6-2/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1735990064714&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;MariaDB Server 11.6.2&quot; data-og-description=&quot;&amp;lt;div class=&amp;quot;pdl-cta&amp;quot;&amp;gt; Thank you for downloading. Create your MariaDB account to receive download release notifications, product updates an...&quot; data-og-host=&quot;mariadb.com&quot; data-og-source-url=&quot;https://mariadb.com/kb/en/postdownload/mariadb-server-11-6-2/&quot; data-og-url=&quot;https://mariadb.com/kb/en/postdownload/mariadb-server-11-6-2/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://mariadb.com/kb/en/postdownload/mariadb-server-11-6-2/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mariadb.com/kb/en/postdownload/mariadb-server-11-6-2/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;MariaDB Server 11.6.2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;div class=&quot;pdl-cta&quot;&amp;gt; Thank you for downloading. Create your MariaDB account to receive download release notifications, product updates an...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mariadb.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1GX6D/btsLErKPZj5/mPryiLZCf6j4EkpEfVsbrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1GX6D/btsLErKPZj5/mPryiLZCf6j4EkpEfVsbrk/img.png&quot; data-alt=&quot;위 페이지에서 자신에게 맞는 운영체제를 선택해 Download를 클릭하면, 즉시 다운된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1GX6D/btsLErKPZj5/mPryiLZCf6j4EkpEfVsbrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1GX6D%2FbtsLErKPZj5%2FmPryiLZCf6j4EkpEfVsbrk%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;795&quot; height=&quot;730&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위 페이지에서 자신에게 맞는 운영체제를 선택해 Download를 클릭하면, 즉시 다운된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 진행 중 root 유저 정보를 설정하는 창에서 &lt;b&gt;password&lt;/b&gt;를 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 모든 char의 set을 기본적으로 UTF8로 설정한것인지 묻는 체크박스인데, 필요하다면 체크한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCtwja/btsLCrZM8J6/3W05JJSw9mzU4rJEGVE8JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCtwja/btsLCrZM8J6/3W05JJSw9mzU4rJEGVE8JK/img.png&quot; data-alt=&quot;root 유저 정보 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCtwja/btsLCrZM8J6/3W05JJSw9mzU4rJEGVE8JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCtwja%2FbtsLCrZM8J6%2F3W05JJSw9mzU4rJEGVE8JK%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;491&quot; height=&quot;379&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;root 유저 정보 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 설정을 완료하고 나면, &lt;b&gt;포트 번호&lt;/b&gt;를 설정하는 창이 뜬다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;필요할 경우 수정해도 괜찮지만, 일반적으로 집에서 실습할 때에는 수정할 필요가 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 포트가 사용 가능한 상태인지 보려면, cmd 창을 켜 아래 명령어를 입력하면 된다. 엔터를 쳤을 때 아무 결과가 뜨지 않는다면 사용 가능한 상태이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735990518028&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 3306 포트가 사용중인지 확인하는 cmd 명령어.
netstat -ano | findstr 3306&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bru4SM/btsLEiApBZu/j5CrF4ejSIcMFTSmX3JyW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bru4SM/btsLEiApBZu/j5CrF4ejSIcMFTSmX3JyW0/img.png&quot; data-alt=&quot;3306 포트로 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bru4SM/btsLEiApBZu/j5CrF4ejSIcMFTSmX3JyW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbru4SM%2FbtsLEiApBZu%2Fj5CrF4ejSIcMFTSmX3JyW0%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;491&quot; height=&quot;379&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;3306 포트로 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다만 나는 일 때문에 내 컴퓨터에 mySQL을 설치해 둔 상태이므로, 3307이라는 다른 포트를 할당해 주었다.&lt;b&gt; 3306 포트가 사용중&lt;/b&gt;일 경우, 아래와 같이 출력된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UTGIW/btsLD0GGD8o/kp7AKB5rGeSE2F9SWGXTGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UTGIW/btsLD0GGD8o/kp7AKB5rGeSE2F9SWGXTGK/img.png&quot; data-alt=&quot;3306 포트가 사용중인 모습. LISTENING은 해당 포트가 현재 열려 있으며, 외부의 연결을 기다리고 있는 상태임을 가리킨다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UTGIW/btsLD0GGD8o/kp7AKB5rGeSE2F9SWGXTGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUTGIW%2FbtsLD0GGD8o%2Fkp7AKB5rGeSE2F9SWGXTGK%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;731&quot; height=&quot;122&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;3306 포트가 사용중인 모습. LISTENING은 해당 포트가 현재 열려 있으며, 외부의 연결을 기다리고 있는 상태임을 가리킨다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마리아 DB를 설치하면 &lt;b&gt;HeidiSQL&lt;/b&gt;이라는, 데이터베이스 관리 도구가 함께 설치된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소에는 &lt;b&gt;DBeaver&lt;/b&gt;를 사용하지만, 설치된김에 사용해보기로 하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pvUNC/btsLEc1dcIQ/r6RFWrnNiTKsHS6KrBMIbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pvUNC/btsLEc1dcIQ/r6RFWrnNiTKsHS6KrBMIbK/img.png&quot; data-alt=&quot;HeidiSQL을 처음 실행한 모습.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pvUNC/btsLEc1dcIQ/r6RFWrnNiTKsHS6KrBMIbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpvUNC%2FbtsLEc1dcIQ%2Fr6RFWrnNiTKsHS6KrBMIbK%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;685&quot; height=&quot;500&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HeidiSQL을 처음 실행한 모습.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 신규 버튼을 누르면, 새로운 데이터베이스를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클릭된 세션을 우클릭하면 이름도 변경할 수 있는데, test로 변경해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 3307 포트를 사용하기로 하였으므로 포트 번호를 바꾸었고, 암호에는 처음 root 유저를 만들 때 설정하였던 비밀번호를 입력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPm0Yu/btsLEp7iaGo/6USEtYOyG4HvjT6Vnzsmgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPm0Yu/btsLEp7iaGo/6USEtYOyG4HvjT6Vnzsmgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPm0Yu/btsLEp7iaGo/6USEtYOyG4HvjT6Vnzsmgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPm0Yu%2FbtsLEp7iaGo%2F6USEtYOyG4HvjT6Vnzsmgk%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;682&quot; height=&quot;496&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;* DBeaver에서 MariaDB를 연결하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Connect to a database에서 마리아 DB를 검색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1049&quot; data-origin-height=&quot;955&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GYoor/btsLEq6d790/j0BCKIm2gKkP74LAvbKMdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GYoor/btsLEq6d790/j0BCKIm2gKkP74LAvbKMdk/img.png&quot; data-alt=&quot;mariaDB의 상징은 물개&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GYoor/btsLEq6d790/j0BCKIm2gKkP74LAvbKMdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGYoor%2FbtsLEq6d790%2Fj0BCKIm2gKkP74LAvbKMdk%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;1049&quot; height=&quot;955&quot; data-origin-width=&quot;1049&quot; data-origin-height=&quot;955&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;mariaDB의 상징은 물개&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 내가 설정했던 port 번호와 root의 비밀번호를 입력하고 완료를 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cop4In/btsLEsiGAxB/eo2tkAJ5T2roChN8wwgJoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cop4In/btsLEsiGAxB/eo2tkAJ5T2roChN8wwgJoK/img.png&quot; data-alt=&quot;Driver properties를 누르면 필요한 것들이 설치된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cop4In/btsLEsiGAxB/eo2tkAJ5T2roChN8wwgJoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcop4In%2FbtsLEsiGAxB%2Feo2tkAJ5T2roChN8wwgJoK%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;939&quot; height=&quot;868&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Driver properties를 누르면 필요한 것들이 설치된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZiwMF/btsLD07NHbZ/xq2tGJfSpzTOAawz9qLSUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZiwMF/btsLD07NHbZ/xq2tGJfSpzTOAawz9qLSUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZiwMF/btsLD07NHbZ/xq2tGJfSpzTOAawz9qLSUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZiwMF%2FbtsLD07NHbZ%2Fxq2tGJfSpzTOAawz9qLSUK%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;280&quot; height=&quot;246&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 설치한 mariaDB에 데이터베이스를 하나 만들고, SpringBoot 프로젝트에 연결한다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test 세션에 Base 데이터베이스를 만들고, User이라는 테이블을 만들 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsumYT/btsLE0lJa0p/VjM9HqOJFxo0a1QrxiTDi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsumYT/btsLE0lJa0p/VjM9HqOJFxo0a1QrxiTDi0/img.png&quot; data-origin-width=&quot;504&quot; data-origin-height=&quot;449&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.9075%; margin-right: 10px;&quot; data-widthpercent=&quot;48.47&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsumYT/btsLE0lJa0p/VjM9HqOJFxo0a1QrxiTDi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsumYT%2FbtsLE0lJa0p%2FVjM9HqOJFxo0a1QrxiTDi0%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;504&quot; height=&quot;449&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dO5XxR/btsLEIS8zDF/Ryk2SxEIoQWemfWZYMeEs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dO5XxR/btsLEIS8zDF/Ryk2SxEIoQWemfWZYMeEs0/img.png&quot; data-origin-width=&quot;321&quot; data-origin-height=&quot;269&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.9298%;&quot; data-widthpercent=&quot;51.53&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dO5XxR/btsLEIS8zDF/Ryk2SxEIoQWemfWZYMeEs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdO5XxR%2FbtsLEIS8zDF%2FRyk2SxEIoQWemfWZYMeEs0%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;321&quot; height=&quot;269&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Base라는 데이터베이스를 만든다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블은 명령어로도 생성할 수 있고, GUI 도구로 생성할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ljR7o/btsLFdE8boC/NrkTFhBXJkRDkNT0RlKmp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ljR7o/btsLFdE8boC/NrkTFhBXJkRDkNT0RlKmp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ljR7o/btsLFdE8boC/NrkTFhBXJkRDkNT0RlKmp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FljR7o%2FbtsLFdE8boC%2FNrkTFhBXJkRDkNT0RlKmp0%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;584&quot; height=&quot;586&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvozxR/btsLDonDzAF/ivmaQQYexKzTVdybauH2FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvozxR/btsLDonDzAF/ivmaQQYexKzTVdybauH2FK/img.png&quot; data-alt=&quot;GUI 도구도 잘 되어있기 때문에 편하다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvozxR/btsLDonDzAF/ivmaQQYexKzTVdybauH2FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvozxR%2FbtsLDonDzAF%2FivmaQQYexKzTVdybauH2FK%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;606&quot; height=&quot;187&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GUI 도구도 잘 되어있기 때문에 편하다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬럼 또한 HeidiSQL을 이용해 바로 넣을 수 있다. 테이블 생성하는 곳의 +추가 버튼을 클릭하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;userId, loginId, password, name, nickname, age를 생성하되 userId는 AutoIncrement가 적용된 PrimaryKey로 설정할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YSvDY/btsLFb1B2G3/0T36Q1JhXkjNFiqtmxNKa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YSvDY/btsLFb1B2G3/0T36Q1JhXkjNFiqtmxNKa0/img.png&quot; data-alt=&quot;userId에 primaryKey를 설정해주었다. 우클릭 하고 새 인덱스 생성에 마우스 오버 하면, Primary를 클릭할 수 있다. (현재는 이미 설정되어서 블록 처리된 상태)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YSvDY/btsLFb1B2G3/0T36Q1JhXkjNFiqtmxNKa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYSvDY%2FbtsLFb1B2G3%2F0T36Q1JhXkjNFiqtmxNKa0%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;622&quot; height=&quot;362&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;userId에 primaryKey를 설정해주었다. 우클릭 하고 새 인덱스 생성에 마우스 오버 하면, Primary를 클릭할 수 있다. (현재는 이미 설정되어서 블록 처리된 상태)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqe3I9/btsLFbUQHre/AoJmiKQyVNHmJ1cpxPsDSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqe3I9/btsLFbUQHre/AoJmiKQyVNHmJ1cpxPsDSk/img.png&quot; data-alt=&quot;또한 해당 userId의 '기본값' 란을 클릭하면, 위와 같이 선택 옵션 창이 뜬다. 여기서 AUTO_INCREMENT를 클릭하여 자동으로 증가하는, 그 테이블만의 고유한 id를 설정해줄 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqe3I9/btsLFbUQHre/AoJmiKQyVNHmJ1cpxPsDSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdqe3I9%2FbtsLFbUQHre%2FAoJmiKQyVNHmJ1cpxPsDSk%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;711&quot; height=&quot;289&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;또한 해당 userId의 '기본값' 란을 클릭하면, 위와 같이 선택 옵션 창이 뜬다. 여기서 AUTO_INCREMENT를 클릭하여 자동으로 증가하는, 그 테이블만의 고유한 id를 설정해줄 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;693&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QLCc7/btsLCJlwRPX/VtKex7bGlBWa4X2lWMC6Ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QLCc7/btsLCJlwRPX/VtKex7bGlBWa4X2lWMC6Ok/img.png&quot; data-alt=&quot;저장을 클릭해야 테이블이 진짜로 생성된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QLCc7/btsLCJlwRPX/VtKex7bGlBWa4X2lWMC6Ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQLCc7%2FbtsLCJlwRPX%2FVtKex7bGlBWa4X2lWMC6Ok%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;693&quot; height=&quot;428&quot; data-origin-width=&quot;693&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;저장을 클릭해야 테이블이 진짜로 생성된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 과정을 SQL 쿼리로 작성하면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1736002998282&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE `User` (
	`userId` INT NOT NULL AUTO_INCREMENT,
	`loginId` VARCHAR(50) NULL DEFAULT NULL,
	`password` VARCHAR(50) NULL DEFAULT NULL,
	`name` VARCHAR(50) NULL DEFAULT NULL,
	`nickname` VARCHAR(50) NULL DEFAULT NULL,
	`age` INT NULL DEFAULT NULL,
	PRIMARY KEY (`userId`)
)
COMMENT='유저의 고유 id, loginId, name, nikname 등 user 정보가 담겨있는 테이블.'
COLLATE='utf8mb4_uca1400_ai_ci'
;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 또한 GUI 도구로 만들어 삽입할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpWeqb/btsLCKkvNRZ/fBmVzdaN89UnWQNrerGk0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpWeqb/btsLCKkvNRZ/fBmVzdaN89UnWQNrerGk0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpWeqb/btsLCKkvNRZ/fBmVzdaN89UnWQNrerGk0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpWeqb%2FbtsLCKkvNRZ%2FfBmVzdaN89UnWQNrerGk0k%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;467&quot; height=&quot;601&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;601&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckTskC/btsLFgu4fvD/hNmTF9bMuyQXnkVea5QuQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckTskC/btsLFgu4fvD/hNmTF9bMuyQXnkVea5QuQ0/img.png&quot; data-alt=&quot;userId는 건드리지 않고, 나머지 데이터 열만 클릭하여 입력한 뒤 빈 공간을 누르면 자동으로userId가 설정된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckTskC/btsLFgu4fvD/hNmTF9bMuyQXnkVea5QuQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckTskC%2FbtsLFgu4fvD%2FhNmTF9bMuyQXnkVea5QuQ0%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;510&quot; height=&quot;108&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;userId는 건드리지 않고, 나머지 데이터 열만 클릭하여 입력한 뒤 빈 공간을 누르면 자동으로userId가 설정된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLD도 딴 겸.. SQL로 INSERT INTO 테이블명 (컬럼명) VALUES (값들) 을 통해 추가 데이터를 넣어주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1736003831705&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;INSERT INTO base.`user` (loginId, PASSWORD, NAME, nickname, age) VALUES ('nomal', 'nomal100', '노멀 유저', '일반 유저', 20);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;543&quot; data-origin-height=&quot;149&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhLDCa/btsLDHtOBJQ/hcCmotoqKMyJZxVzRvFolK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhLDCa/btsLDHtOBJQ/hcCmotoqKMyJZxVzRvFolK/img.png&quot; data-alt=&quot;두 행의 데이터가 생성된 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhLDCa/btsLDHtOBJQ/hcCmotoqKMyJZxVzRvFolK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhLDCa%2FbtsLDHtOBJQ%2FhcCmotoqKMyJZxVzRvFolK%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;543&quot; height=&quot;149&quot; data-origin-width=&quot;543&quot; data-origin-height=&quot;149&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;두 행의 데이터가 생성된 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 스프링부트에 JDBC를 이용하여 MariaDB를 연결한다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;* 여기서 잠깐, JDBC란 무엇일까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;JDBC (Java Database Connectivity)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의&lt;/b&gt;: &lt;b&gt;Java에서 데이터베이스와 연결하기 위한 표준 인터페이스&lt;/b&gt;. 자바 프로그램에서 SQL 쿼리를 실행하고 데이터베이스와 상호작용할 수 있게 해준다. 즉,&lt;b&gt; 자바 언어를 사용하여 데이터베이스를 다룰 수 있도록 하는 라이브러리&lt;/b&gt;라고 할 수 있다.&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747; text-align: left;&quot;&gt;Node.js로 따지자면, mongodb 라이브러리와 같다. 자바 프로그램과 SQL 데이터베이스를 연결하게 해 주며, 저수준의 API를 제공한다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터베이스 연결(Connection) 생성.&lt;/li&gt;
&lt;li&gt;SQL 쿼리 실행(SELECT, INSERT, UPDATE 등).&lt;/li&gt;
&lt;li&gt;결과(ResultSet) 처리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;: JDBC를 사용해 MySQL/MariaDB와 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치해야 할 최신 maria db용 JDBC 버전은 아래의 사이트에서 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트에 따르면 현재 최신 버전은 3.5.1이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;1167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zo4qc/btsLEjMPYaF/PKR7xfGouiyRqTZtQLPBKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zo4qc/btsLEjMPYaF/PKR7xfGouiyRqTZtQLPBKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zo4qc/btsLEjMPYaF/PKR7xfGouiyRqTZtQLPBKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZo4qc%2FbtsLEjMPYaF%2FPKR7xfGouiyRqTZtQLPBKK%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;1060&quot; height=&quot;1167&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;1167&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 위 사이트에서 3.5.1을 클릭하고, Gradle을 클릭하면 해당 JDBC 라이브러리를 Gradle를 이용해 다운로드 하게 하는 명령어를 복사할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bU1CVs/btsLD0mpF82/1k03UidRpnNKUPAk2KmSpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bU1CVs/btsLD0mpF82/1k03UidRpnNKUPAk2KmSpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bU1CVs/btsLD0mpF82/1k03UidRpnNKUPAk2KmSpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbU1CVs%2FbtsLD0mpF82%2F1k03UidRpnNKUPAk2KmSpk%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;828&quot; height=&quot;667&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;667&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 위 명령어를 build.gradle에 입력하고, gradle를 새로고침 해준다.&lt;/b&gt; npm install 과 같은 역할을 한다고 생각하면 편하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;799&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FzDnq/btsLErD4jts/1uXRUkwPaRIur9KXJFgbp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FzDnq/btsLErD4jts/1uXRUkwPaRIur9KXJFgbp1/img.png&quot; data-alt=&quot;JDBC 라이브러리를 설치해준다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FzDnq/btsLErD4jts/1uXRUkwPaRIur9KXJFgbp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFzDnq%2FbtsLErD4jts%2F1uXRUkwPaRIur9KXJFgbp1%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;846&quot; height=&quot;799&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;799&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JDBC 라이브러리를 설치해준다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span style=&quot;background-color: #ffffff; color: #454545; text-align: start;&quot;&gt;3. 이제, 데이터베이스 알맹이와 연결해주기 위해 application.properties 를 작성한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #454545; text-align: start;&quot;&gt;React로 치자면, 환경변수 파일인 .env와 비슷하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #454545; text-align: start;&quot;&gt;내가 만든 데이터베이스 이름은 base이고, 사용자 명은 root이며, 비밀번호는 예시로 0000을 입력해 두었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #454545; text-align: start;&quot;&gt;내 포트는 3307이지만, 기본적으로는 3306이므로 예시 코드는 아래와 같이 작성하였다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/base // 포트번호/데이터베이스 명
spring.datasource.username=root // 사용자명
spring.datasource.password=0000 // 비밀번호&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. JPA를 설치하여, mariaDB를 사용한다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;* JPA란 무엇일까?&lt;/b&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;JPA ( Java Persistence API )&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의&lt;/b&gt;:&lt;span&gt; 자바 애플리케이션에서 &lt;b&gt;데이터베이스와 객체 간의 매핑(ORM, Object-Relational Mapping)&lt;/b&gt;을 처리하기 위한 표준 인터페이스. Java 진영에서 제공하는 ORM 기술의 표준으로, 데이터베이스와 객체 모델 간의 변환을 간단히 처리할 수 있도록 설계되었다.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Node.js 개발자에게 이해하기 쉽게 설명해보자면, MongoDB를 사용할 때 이용했던&amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #474747; text-align: left;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;mongoose&lt;/b&gt; &lt;span style=&quot;color: #333333;&quot;&gt;의 역할을 한다고 할 수 있다&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체와 데이터베이스 간 매핑 .&lt;/li&gt;
&lt;li&gt;SQL 작성 및 실행 자동화.&lt;/li&gt;
&lt;li&gt;데이터 변경 관리.. 등&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 자바 언어를 활용해, 자바스크립트로 몽고 디비를 다룬 몽구스처럼, 자바 언어로 SQL DB를 다루기 위하여 JPA라는 라이브러리를 설치해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA에는 Hibernate, EclipseLink, OpenJPA가 존재한다. 나는 요즘 가장 많이 사용되는 Hibernate를 채택하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 먼저, build.gradle에 아래 문장을 추가하여 그래들을 새로고침 해 라이브러리를 설치한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736009881907&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;implementation 'org.springframework.boot:spring-boot-starter-data-jpa'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. application.properites에 아래 사항을 추가해준다.&lt;/b&gt; &lt;b&gt;'JPA 엔티티 설계' 와 '실제 테이블 상태' 를 비교해, 차이점이 있다면 그를 테이블에 반영하게 하는 옵션이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;shell&quot; style=&quot;color: #000000; text-align: left;&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;spring.jpa.hibernate.ddl-auto=update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-1. 만일 실행할 때 DB 입출력 시 SQL을 따로 실행창에 출력하고 싶다면, application.properties에 아래 문장을 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1736010115043&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.jpa.properties.hibernate.show_sql=true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로,&amp;nbsp; JPA는 CamelCase를 자동으로 snakeCase로 변환한다. 그 설정을 무시하기 위해 아래 사항을 추가해 주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1736020095011&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 아까 만든 User 테이블을, JPA 문법을 사용하여 소스코드로 옮긴다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA 문법에서, @Entity 어노테이션은 '테이블' 을 의미한다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;따라서&lt;b&gt; @Entity를 사용해 클래스를 만들면 그것이 바로 테이블이 된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그리고 클래스에 &lt;b&gt;변수들을 선언&lt;/b&gt;해주면, &lt;b&gt;그것이 바로 '컬럼' 이 되는 것이다&lt;/b&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 위에서 작성한 User 테이블은 아래와 같이 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736011837378&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User {

private int userId;
    private String loginId;
    private String password;
    private String name;
    private String nickname;
    private int age;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 PrimaryKey(기본키) 이면서, 자동으로 증가하는 수인 userId 는 어떻게 설정해 주어야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA 문법에서, 아래의 어노테이션은 '다음 변수가 PrimaryKey이며, AutoIncrement 임을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736012018410&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 userId 바로 위에 아래와 같이 작성해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736012032962&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int userId;

    private String loginId;
    private String password;
    private String name;
    private String nickname;
    private int age;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헌데, 현재 변수들을 class 안에서만 사용하기 위해 private라는 접근자를 붙였으므로 외부에서는 해당 변수의 값을 사용할 수 없는 상태이다. 따라서 해당 변수의 값을 가져오기 위하여 getter와 setter를 만들어 주어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736012154633&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int userId;

    private String loginId;
    private String password;
    private String name;
    private String nickname;
    private int age;

    // Getter와 Setter 추가
    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getLoginId() {
        return loginId;
    }

    public void setLoginId(String loginId) {
        this.loginId = loginId;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNikname() {
        return nickname;
    }

    public void setNikname(String nikname) {
        this.nikname = nikname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이와 같이 작성하면 매번 테이블을 만들 때마다 똑같은 Getter와 Setter를 만드는 일을 반복해야 하므로, lombok의 @Getter, @Setter 어노테이션을 이용해 이를 자동화시켜줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736012190642&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;

@Entity
@Setter
@Getter
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int userId;

    private String loginId;
    private String password;
    private String name;
    private String nickname;
    private int age;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. JPA 문법과 lombok을 이용하여 데이터를 조회한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블에서 데이터를 입력하거나 출력하려면, 항상 다음의 3-step을 밟아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. repository를 만들고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. DB 입출력을 하고자 하는 클래스에서 만든 repository를 등록한 다음,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. JPA 문법을 사용하여 데이터를 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 다음과 같은 User Repository를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1736017669740&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository&amp;lt;User, Integer&amp;gt; {


}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 데이터를 조회하고자 하는 페이지에서 이용할 수 있도록, Controller 파일에 데이터베이스를 조회하는 문장을 작성해 주었다. findeAll은 해당 테이블의 '모든' 정보를 가져오라는 JPA 문법이다.&lt;/p&gt;
&lt;pre id=&quot;code_1736021406409&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;User&amp;gt; result = userRepository.findAll();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 Lombok을 이용하여, @ &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;RequiredArgsConstructor&lt;span&gt;&amp;nbsp; 를 사용해 Constructor를 자동으로 만들도록 설정해 주면 편하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736021415969&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
@RequiredArgsConstructor
public class ItemController {

    private final UserRepository userRepository;

    @GetMapping(&quot;/list&quot;)
    String list(Model model) {
        List&amp;lt;User&amp;gt; result = userRepository.findAll();
        model.addAttribute(&quot;name&quot;, &quot;신발&quot;);
        return &quot;list.html&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 가져오려면, getter과 setter를 이용해야 한다. 우리는 lombok을 이용하여 자동으로 생성되도록 하였으므로, 자동완성에 get과 set이 뜬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736021459993&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;System.out.println(result.get(0).getLoginId());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1736017693876&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.List;

@Controller
@RequiredArgsConstructor
public class ItemController {

    private final UserRepository userRepository;

    @GetMapping(&quot;/list&quot;)
    String list(Model model) {
        List&amp;lt;User&amp;gt; result = userRepository.findAll();
        System.out.println(result.get(0).getLoginId());
        model.addAttribute(&quot;name&quot;, &quot;신발&quot;);
        return &quot;list.html&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 localhost:8080/list에 들어가면, 아래와 같이 터미널에 println에 출력한 내용이 잘 뜬다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m4Zoy/btsLE3QgxYK/nXMZNxw8Zu6fL6kghjuDaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m4Zoy/btsLE3QgxYK/nXMZNxw8Zu6fL6kghjuDaK/img.png&quot; data-alt=&quot;master 라는 loginId가 나타났다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m4Zoy/btsLE3QgxYK/nXMZNxw8Zu6fL6kghjuDaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm4Zoy%2FbtsLE3QgxYK%2FnXMZNxw8Zu6fL6kghjuDaK%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;1115&quot; height=&quot;300&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;master 라는 loginId가 나타났다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 행을 출력하고 싶다면, toString을 일일이 오버라이딩해야한다. 이를 방지하기 위해 @ToString 어노테이션을 사용해 주었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1736022088944&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Entity
@Setter
@Getter
@ToString // ToString 추가 
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer userId;
    private String loginId;
    private String password;
    private String name;
    private String nickName;
    private Integer age;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 통해 모든 내용을 출력해보자.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;result.forEach(System.out::println);
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러 전체 코드는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1736022097808&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.example.demo;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.List;

@Controller
@RequiredArgsConstructor
public class ItemController {

    private final UserRepository userRepository;

    @GetMapping(&quot;/list&quot;)
    String list() {
        List&amp;lt;User&amp;gt; result = userRepository.findAll();
        result.forEach(System.out::println);
        return &quot;list.html&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공~&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0rbGF/btsLDLXvem9/Z7KpoisBAHYlaUT3TFcuNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0rbGF/btsLDLXvem9/Z7KpoisBAHYlaUT3TFcuNk/img.png&quot; data-alt=&quot;한글 유니코드가 좀 깨지는 것 말고는 모두 잘 출력된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0rbGF/btsLDLXvem9/Z7KpoisBAHYlaUT3TFcuNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0rbGF%2FbtsLDLXvem9%2FZ7KpoisBAHYlaUT3TFcuNk%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;853&quot; height=&quot;76&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;한글 유니코드가 좀 깨지는 것 말고는 모두 잘 출력된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/SpringBoot</category>
      <author>찰리-누나</author>
      <guid isPermaLink="true">https://make-somthing.tistory.com/112</guid>
      <comments>https://make-somthing.tistory.com/112#entry112comment</comments>
      <pubDate>Sun, 5 Jan 2025 05:31:33 +0900</pubDate>
    </item>
    <item>
      <title>[SpringBoot] SpringBoot 프로젝트 구조</title>
      <link>https://make-somthing.tistory.com/111</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 부트로 만든 프로젝트의 기본 디렉토리 구조와, 각 파일의 역할을 알아보자 .&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 아주 기본적인 프로젝트를 생성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;695&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oqj0q/btsLAA2BV3s/CobkeX4cKyk2HxSvhhHLpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oqj0q/btsLAA2BV3s/CobkeX4cKyk2HxSvhhHLpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oqj0q/btsLAA2BV3s/CobkeX4cKyk2HxSvhhHLpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foqj0q%2FbtsLAA2BV3s%2FCobkeX4cKyk2HxSvhhHLpk%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;357&quot; height=&quot;695&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;695&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. src/main/java&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 코드가 위치하는 디렉토리로, 아래와 같은 서브 패키지 구조로 구성된다. 지금은 간단하게 작성하여 나뉘어져있지 않지만, 실제 프로젝트에서 사용되는 구조에는 다음과 같은 것들이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;controller/&lt;/b&gt;: REST API 또는 MVC 컨트롤러 클래스.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;service/&lt;/b&gt;: 비즈니스 로직을 처리하는 서비스 계층.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;repository/&lt;/b&gt;: 데이터베이스와의 상호작용을 처리하는 인터페이스(JpaRepository, CrudRepository).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;domain/&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(또는 model/): 데이터베이스 엔티티 클래스 및 DTO, VO 등이 위치.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ProjectApplication.java&lt;/b&gt;: 메인 실행 클래스(스프링 부트 애플리케이션의 진입점)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 것은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ProjectApplication.java&lt;/b&gt;: 메인 실행 클래스(스프링 부트 애플리케이션의 진입점)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다. 내가 demo로 프로젝트 명을 생성하였기 때문에 스프링에서 자동으로 DemoApplication.java 파일을 만들어주었는데, 이렇게 &lt;b&gt;프로젝트명+Application.java&lt;/b&gt; 로 되어있는 파일이 바로 &lt;b&gt;'스프링 부트로 만든 프로그램의 시작을 담당하는 파일'&lt;/b&gt; 이다.&lt;/p&gt;
&lt;pre id=&quot;code_1735484404265&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이와 같은 파일이 자동으로 생성되어 있다.

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;이 프로젝트명 + Application 클래스에는 반드시&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;@SpringBootApplication&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;애너테이션&lt;/b&gt;이 적용되어 있어야 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;@SpringBootApplication&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;애너테이션을 통해 스프링 부트 애플리케이션을 시작&lt;/b&gt;할 수 있기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. src/main/resources&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;static/&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 리소스(HTML, CSS, JavaScript, 이미지 파일 등)가 위치한다.&lt;/li&gt;
&lt;li&gt;/static 디렉토리의 파일들은 기본적으로 &lt;b&gt;/ 경로&lt;/b&gt;로 매핑된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;templates/&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 사이드 렌더링에 사용하는 템플릿 파일(예: &lt;b&gt;Thymeleaf&lt;/b&gt;, &lt;b&gt;Freemarker&lt;/b&gt; 등)이 위치한다.&lt;/li&gt;
&lt;li&gt;주로 HTML 파일이 들어간다.&lt;/li&gt;
&lt;li&gt;@Controller와 함께 동작하며 뷰를 반환할 때 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;application.properties&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션 설정 파일.&lt;/li&gt;
&lt;li&gt;환경변수, 데이터베이스 연결, 서버 포트, 로깅 수준 등 여러 설정을 정의할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mQdhE/btsLA52kSf0/jKrTgWYlHBv8KZtK5jPuCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mQdhE/btsLA52kSf0/jKrTgWYlHBv8KZtK5jPuCk/img.png&quot; data-alt=&quot;resources 의 static에 index.html을 작성하였다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mQdhE/btsLA52kSf0/jKrTgWYlHBv8KZtK5jPuCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmQdhE%2FbtsLA52kSf0%2FjKrTgWYlHBv8KZtK5jPuCk%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;926&quot; height=&quot;433&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;resources 의 static에 index.html을 작성하였다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; src/main/resources&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;디렉토리는 자바 파일을 제외한 HTML, CSS, 자바스크립트, 환경 파일 등을 저장하는 곳이다. 위에서 템플릿 파일이란 자바 코드를 삽입할 수 있는 HTML 형식의 파일을 뜻하는데, 스프링부트에서 생성한 자바 객체를 HTML 형태로 출력할 수 있다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. &lt;b&gt;src/test/java&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 코드를 작성하는 디렉토리이다.&lt;/li&gt;
&lt;li&gt;기본적으로 Spring Boot는 JUnit을 통해 테스트를 지원한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DemoApplicationTests&lt;/b&gt; 같은 클래스가 자동 생성되어 스프링 컨텍스트가 제대로 로드되는지 확인하는 기본 테스트 코드가 포함된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. &lt;b&gt;루트 디렉토리&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;build.gradle&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Gradle 빌드 스크립트.&lt;/li&gt;
&lt;li&gt;의존성과 빌드 설정을 정의한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;gradlew 및 gradlew.bat&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Gradle Wrapper 파일로, Gradle이 설치되지 않은 시스템에서도 Gradle 빌드 스크립트를 실행할 수 있도록 도와준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;.gitignore&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Git에서 버전 관리하지 않을 파일들을 정의한다(예: build/, .idea/ 등).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 것은 그래들이다. &lt;b&gt;build.gradle&lt;/b&gt;은 &lt;b&gt;Gradle&lt;/b&gt;이라는 빌드 도구가 사용하는 &lt;b&gt;설정 파일&lt;/b&gt;이다.&lt;br /&gt;Gradle은 프로젝트를 만들고 실행하기 위해 필요한 일을 대신 처리해주는 도구라고 생각하면 편하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Gradle이란?&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Gradle&lt;/b&gt;은 프로그래밍 작업을 자동화해주는 &lt;b&gt;도구&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;소스 코드를 &lt;b&gt;컴파일&lt;/b&gt;, 필요한 파일을 &lt;b&gt;다운로드&lt;/b&gt;, 프로젝트를 &lt;b&gt;패키징&lt;/b&gt;(묶기)하는 일을 도와준다.&lt;/li&gt;
&lt;li&gt;이전에 많이 사용되던 &lt;b&gt;Ant&lt;/b&gt;나 &lt;b&gt;Maven&lt;/b&gt;보다 &lt;b&gt;더 빠르고 유연&lt;/b&gt;하게 동작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. build.gradle 파일이 하는 일&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Gradle의 설정 파일&lt;/b&gt;로, 여기서 프로젝트에 필요한 것들을 적어둔다.&lt;/li&gt;
&lt;li&gt;예를 들어:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;플러그인 추가&lt;/b&gt; (특별한 기능을 추가하는 도구)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라이브러리 설치&lt;/b&gt; (프로젝트에 필요한 외부 기능)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1735485122781&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
    id 'java'  // 자바 프로젝트라는 걸 알려줌
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web' // 웹 서버 라이브러리
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Groovy란?&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Groovy&lt;/b&gt;는 Gradle의 스크립트를 작성하는 데 쓰는 &lt;b&gt;간단한 언어&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;문법이 간단하고 읽기 쉽기 때문에 개발자가 쉽게 설정을 쓸 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 빌드 도구가 하는 일&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 도구는 &lt;b&gt;코드를 실행 가능한 형태로 만드는 일&lt;/b&gt;을 자동화해준다.&lt;br /&gt;예를 들어, Gradle은 다음을 자동으로 실행한다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;코드 컴파일&lt;/b&gt;: 우리가 작성한 코드를 컴퓨터가 이해할 수 있는 언어로 변환.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;라이브러리 다운로드&lt;/b&gt;: 프로젝트에 필요한 외부 기능(라이브러리)을 인터넷에서 가져옴.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파일 묶기&lt;/b&gt;: 완성된 코드를 실행 파일(jar 파일)로 압축.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;배포&lt;/b&gt;: 완성된 파일을 서버에 올려서 실행 준비.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. 실생활 비유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gradle은 &lt;b&gt;요리사&lt;/b&gt; 이다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;build.gradle 파일은 &lt;b&gt;레시피&lt;/b&gt;에 해당한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 재료(라이브러리)를 쓸지.&lt;/li&gt;
&lt;li&gt;어떤 도구(플러그인)가 필요한지.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Gradle이 이 레시피를 읽고 **요리(컴파일, 다운로드, 패키징)**를 만든다.&lt;/li&gt;
&lt;li&gt;결과물은 우리가 서버에서 실행할 수 있는 **완성된 요리(jar 파일)**가 된다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;스프링 프로젝트의 흐름&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;클라이언트 요청&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 API를 호출하면, BasicController에서 해당 요청을 처리한다.&lt;/li&gt;
&lt;li&gt;예: @GetMapping(&quot;/hello&quot;) 같은 메서드를 통해 &quot;Hello, World!&quot;를 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컨트롤러 역할&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BasicController는 요청을 처리하고 필요한 데이터를 반환하거나, &lt;b&gt;서비스 로직&lt;/b&gt;을 호출하여 결과를 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;뷰 렌더링 (옵션)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;templates/에 HTML 파일이 있다면, 컨트롤러에서 해당 파일을 뷰로 렌더링할 수 있다.&lt;/li&gt;
&lt;li&gt;예: return &quot;index&quot;;는 templates/index.html을 렌더링한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정적 리소스 제공&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;static/ 디렉토리에 있는 파일은 /css/style.css와 같이 URL로 직접 접근할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/SpringBoot</category>
      <author>찰리-누나</author>
      <guid isPermaLink="true">https://make-somthing.tistory.com/111</guid>
      <comments>https://make-somthing.tistory.com/111#entry111comment</comments>
      <pubDate>Mon, 30 Dec 2024 00:12:56 +0900</pubDate>
    </item>
    <item>
      <title>[SpringBoot] Spring Boot Devtools - auto represh</title>
      <link>https://make-somthing.tistory.com/110</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링으로 개발을 할 때, 프로그램을 수정하면 서버 빌드도 다시 해야 하는 것이 불편하여 Spring Boot Devtools를 이용해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;Spring Boot Devtools를 설치하면 서버를 재시작하지 않아도 클래스를 변경할 때 서버가 자동으로 재가동된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Spring Boot Devtools를 사용하려면 Spring Boot Devtools를 &lt;b&gt;그래들(Gradle)&lt;/b&gt;로 설치해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #24292f; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Gradle&lt;/b&gt;은 Java 생태계에서 빌드와 관리를 담당하는 핵심 도구로, React 생태계에서 사용하는 &lt;b&gt;Vite&lt;/b&gt; 또는 &lt;b&gt;Webpack&lt;/b&gt;과 유사한 역할을 한다. &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;Gradle이 React의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Vite&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Webpack&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;과 정확히 동일하지는 않지만, &quot;개발을 돕는 빌드 및 구성 도구&quot;라는 공통점을 가지고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;다만, Gradle은 Java 생태계에서 &lt;b&gt;코드 컴파일부터 테스트, 배포 패키징까지 전체 프로세스&lt;/b&gt;를 아우르는 반면, React 생태계의 빌드 도구는 주로 &lt;b&gt;번들링 및 개발 서버 제공&lt;/b&gt;에 초점을 맞춘다는 차이점이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Gradle의 역할&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 애플리케이션을 &lt;b&gt;컴파일&lt;/b&gt;하고 &lt;b&gt;의존성을 관리&lt;/b&gt;하며 &lt;b&gt;패키징&lt;/b&gt;하는 전체 빌드 프로세스를 담당.&lt;/li&gt;
&lt;li&gt;Spring Boot와 같은 Java 프레임워크에서 Gradle은 필수적인 역할을 수행.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Vite/Webpack의 역할&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React 애플리케이션의 &lt;b&gt;자바스크립트 모듈을 번들링&lt;/b&gt;하고 &lt;b&gt;개발 서버를 제공&lt;/b&gt;하며, &lt;b&gt;의존성을 관리&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;JavaScript 프로젝트에서 빌드 및 번들링의 핵심 도구로 사용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vite를 다룰 때 vite.config.js를 사용하는 것 처럼, 그래들은 buld.gradle를 사용한다. &lt;b&gt;build.gradle&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;파일에는 프로젝트를 위해 필요한 플러그인과 라이브러리 등이 기술&lt;/b&gt;되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 developmentOnly의 &lt;b&gt;spring-boot-devtools&lt;/b&gt;를 추가해주면 된다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;developmentOnly: 해당 라이브러리는 개발 환경에만 적용된다는 의미로, 운영 환경에 배포되는 jar, war 파일에는 이 라이브러리가 포함되지 않는다.&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;적용법&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이에서 그래들을 다시 시작하려면 다음과 같이 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[View] -&amp;gt; [Gradle]&lt;/b&gt; 을 클릭하면 오른쪽에 그래들 관리 툴이 나타난다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEeLT4/btsLAWklwYE/NPONYrunkTOm2efDCp6kv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEeLT4/btsLAWklwYE/NPONYrunkTOm2efDCp6kv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEeLT4/btsLAWklwYE/NPONYrunkTOm2efDCp6kv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEeLT4%2FbtsLAWklwYE%2FNPONYrunkTOm2efDCp6kv0%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;1260&quot; height=&quot;532&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 실행하고자 하는 프로젝트를 우클릭하고,&lt;b&gt; [Refresh Gradle Dependencies]&lt;/b&gt; 를 클릭한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GoQL4/btsLAfSdMDQ/3rxgBKeaMSyQeKvBJt1sd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GoQL4/btsLAfSdMDQ/3rxgBKeaMSyQeKvBJt1sd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GoQL4/btsLAfSdMDQ/3rxgBKeaMSyQeKvBJt1sd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGoQL4%2FbtsLAfSdMDQ%2F3rxgBKeaMSyQeKvBJt1sd0%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;416&quot; height=&quot;423&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;423&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[File] -&amp;gt; [setting]&lt;/b&gt; 에서 &lt;b&gt;Build project automatically&lt;/b&gt;를 체크한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;745&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYiFUM/btsLzXcWXsU/Z7kLJV2s2JxQ6kNMH7iox0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYiFUM/btsLzXcWXsU/Z7kLJV2s2JxQ6kNMH7iox0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYiFUM/btsLzXcWXsU/Z7kLJV2s2JxQ6kNMH7iox0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYiFUM%2FbtsLzXcWXsU%2FZ7kLJV2s2JxQ6kNMH7iox0%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;1058&quot; height=&quot;745&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;745&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Advanced Setting]&lt;/b&gt; 의&lt;b&gt; [Compiler]&lt;/b&gt;의 &lt;b&gt;Allow auto-make to strat even if developed application is currently running&lt;/b&gt; 을 체크한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;739&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvfzQo/btsLBkkF0XC/5gKmTVJTOhJZIJlicyPKSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvfzQo/btsLBkkF0XC/5gKmTVJTOhJZIJlicyPKSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvfzQo/btsLBkkF0XC/5gKmTVJTOhJZIJlicyPKSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvfzQo%2FbtsLBkkF0XC%2F5gKmTVJTOhJZIJlicyPKSk%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;1244&quot; height=&quot;739&quot; data-origin-width=&quot;1244&quot; data-origin-height=&quot;739&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 삼아, GetMapping으로 메인 페이지에 보내는 String 값을 변경하고 저장해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1493&quot; data-origin-height=&quot;523&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EOUDk/btsLADLMicG/g299fhwuotcmYnYOn5mriK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EOUDk/btsLADLMicG/g299fhwuotcmYnYOn5mriK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EOUDk/btsLADLMicG/g299fhwuotcmYnYOn5mriK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEOUDk%2FbtsLADLMicG%2Fg299fhwuotcmYnYOn5mriK%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;1493&quot; height=&quot;523&quot; data-origin-width=&quot;1493&quot; data-origin-height=&quot;523&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/SpringBoot</category>
      <author>찰리-누나</author>
      <guid isPermaLink="true">https://make-somthing.tistory.com/110</guid>
      <comments>https://make-somthing.tistory.com/110#entry110comment</comments>
      <pubDate>Sun, 29 Dec 2024 23:06:30 +0900</pubDate>
    </item>
    <item>
      <title>[React + Zustand] persist, localstorage에 업데이트 된 새로운 값 가져오기</title>
      <link>https://make-somthing.tistory.com/109</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외주 프로젝트를 하는 중에 다음과 같은 문제가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 행동에 따라, ZuStand의 persist를 사용하여 localstorage에 저장된 값이 변화한다. 이 변화한 값을 페이지 이동 시 즉시 화면에 보여줘야 한다. 그러나 persist를 이용한 ZuStand는 예상과는 다르게 동작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt; 애플리케이션이 처음 실행될 때, persist 미들웨어는 지정된 스토리지에서 데이터를 읽어와 Zustand 스토어를 &quot;rehydrate&quot;합니다. 즉, 저장된 상태를 다시 불러와 초기 상태로 설정합니다.&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;persist를 이용해 만들어진 최초의 상태, 즉 아무 값이 저장되지 않은 상태가 zuStand에 저장된 상태라 localStorage에서 변화한 값이 useState를 쓸 때 처럼, 화면의 리렌더링이 되지 않았던 것! 새로고침을 하면 로컬스토리지를 다시 읽어오기에 최신의 값을 불러올 수 있게 되지만, 새로고침을 하기 전까지는 '이전의 값' 을 계속 갖고 있는 것이 문제였다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;해결은 의외로 간단했다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/pmndrs/zustand/discussions/1614&quot;&gt;https://github.com/pmndrs/zustand/discussions/1614&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1733758384418&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;How to get updated data from localStorage / sync tabs? &amp;middot; pmndrs zustand &amp;middot; Discussion #1614&quot; data-og-description=&quot;How can I sync tabs while using persist (with localStorage)? I've already checked the &amp;quot;shared-zustand&amp;quot;, but since I'm using Immer, it doesn't work: Tom-Julux/shared-zustand#4. Suppose I have a shop...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/pmndrs/zustand/discussions/1614&quot; data-og-url=&quot;https://github.com/pmndrs/zustand/discussions/1614&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OgTIm/hyXGGp3c7S/x9vk7N097Mfrcq5FCXpkEK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/6QnBP/hyXKnP00vD/YkP2MJHSfzIQhuxBEwQXl0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/pmndrs/zustand/discussions/1614&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/pmndrs/zustand/discussions/1614&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OgTIm/hyXGGp3c7S/x9vk7N097Mfrcq5FCXpkEK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/6QnBP/hyXKnP00vD/YkP2MJHSfzIQhuxBEwQXl0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;How to get updated data from localStorage / sync tabs? &amp;middot; pmndrs zustand &amp;middot; Discussion #1614&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How can I sync tabs while using persist (with localStorage)? I've already checked the &quot;shared-zustand&quot;, but since I'm using Immer, it doesn't work: Tom-Julux/shared-zustand#4. Suppose I have a shop...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://zustand.docs.pmnd.rs/integrations/persisting-store-data#how-can-i-rehydrate-on-storage-event&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://zustand.docs.pmnd.rs/integrations/persisting-store-data#how-can-i-rehydrate-on-storage-event&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1733758409742&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Persisting store data - Zustand&quot; data-og-description=&quot;&quot; data-og-host=&quot;zustand.docs.pmnd.rs&quot; data-og-source-url=&quot;https://zustand.docs.pmnd.rs/integrations/persisting-store-data#how-can-i-rehydrate-on-storage-event&quot; data-og-url=&quot;https://zustand.docs.pmnd.rs/integrations/persisting-store-data&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lZuE4/hyXKm4EA7e/VycK0PoGV5tdAMqpbyCFQ0/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/yWC9X/hyXKxLTMJP/1cqkINtJ46BsgCh5wWvbQ0/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://zustand.docs.pmnd.rs/integrations/persisting-store-data#how-can-i-rehydrate-on-storage-event&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://zustand.docs.pmnd.rs/integrations/persisting-store-data#how-can-i-rehydrate-on-storage-event&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lZuE4/hyXKm4EA7e/VycK0PoGV5tdAMqpbyCFQ0/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/yWC9X/hyXKxLTMJP/1cqkINtJ46BsgCh5wWvbQ0/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Persisting store data - Zustand&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;zustand.docs.pmnd.rs&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에 아래와 같이 적혀있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;how-can-i-rehydrate-on-storage-event&quot; data-ke-size=&quot;size23&quot;&gt;How can I rehydrate on storage event&lt;/h3&gt;
&lt;p style=&quot;background-color: #f7f9ff; color: #181c20; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;You can use the Persist API to create your own implementation, similar to the example below:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733758424445&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type StoreWithPersist = Mutate&amp;lt;StoreApi&amp;lt;State&amp;gt;, [[&quot;zustand/persist&quot;, unknown]]&amp;gt;

export const withStorageDOMEvents = (store: StoreWithPersist) =&amp;gt; {
  const storageEventCallback = (e: StorageEvent) =&amp;gt; {
    if (e.key === store.persist.getOptions().name &amp;amp;&amp;amp; e.newValue) {
      store.persist.rehydrate()
    }
  }

  window.addEventListener('storage', storageEventCallback)

  return () =&amp;gt; {
    window.removeEventListener('storage', storageEventCallback)
  }
}

const useBoundStore = create(persist(...))
withStorageDOMEvents(useBoundStore)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;store.persist.rehydrate()를 쓰면, storage 이벤트를 사용하여 변경된 값을 즉시 zuStand에 할당해 불러올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 페이지를 로드 할 때 (컴포넌트가 마운트 될 때) 직전에 변경된 로컬 스토리지의 값을 보여주는 기능을 구현해야 했으므로, 다음과 같이 사용했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 먼저, store를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733758528711&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { create } from &quot;zustand&quot;;
import { createJSONStorage, persist } from &quot;zustand/middleware&quot;;

type TelStoreType = {
  telId?: string; // 전화기 ID
  telno?: string; // 전화번호

  setTelInfo: (telId: string, telno: string) =&amp;gt; void;
  clear: () =&amp;gt; void;
};

export const useTelStore = create&amp;lt;TelStoreType&amp;gt;()(
  persist(
    (set) =&amp;gt; ({
      setTelInfo: (telId, telno) =&amp;gt; set({ telId, telno }),
      clear: () =&amp;gt;
        set(() =&amp;gt; ({
          telId: &quot;&quot;,
          telno: &quot;&quot;,
        })),
    }),
    {
      name: &quot;selected-tel&quot;,
      storage: createJSONStorage(() =&amp;gt; localStorage),
    }
  )
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 스토어를 사용하는 곳에서, persist의 rehydrate를 통해 항상 최신의 값을 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733758559326&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; useEffect(() =&amp;gt; {
    useTelStore.persist.rehydrate();
  }, []);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로젝트/React 프론트 프로젝트</category>
      <author>찰리-누나</author>
      <guid isPermaLink="true">https://make-somthing.tistory.com/109</guid>
      <comments>https://make-somthing.tistory.com/109#entry109comment</comments>
      <pubDate>Tue, 10 Dec 2024 00:36:27 +0900</pubDate>
    </item>
    <item>
      <title>[react-query v5] 공식 문서 번역 - useQuery</title>
      <link>https://make-somthing.tistory.com/108</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트로 react-query 의 v5버전을 쓰게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 공식 번역이 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영어 문서인 채로 읽는 것이 가장 좋지만... 빠른 이해를 위해 한번 번역해 기록해 두기로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tanstack.com/query/v5/docs/framework/react/reference/useQuery&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tanstack.com/query/v5/docs/framework/react/reference/useQuery&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1733218299988&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;useQuery | TanStack Query React Docs&quot; data-og-description=&quot;tsx const { data, dataUpdatedAt, error, errorUpdatedAt, failureCount, failureReason, fetchStatus, isError, isFetched, isFetchedAfterMount, isFetching, isInitialLoading, isLoading, isLoadingError, isPa...&quot; data-og-host=&quot;tanstack.com&quot; data-og-source-url=&quot;https://tanstack.com/query/v5/docs/framework/react/reference/useQuery&quot; data-og-url=&quot;https://tanstack.com/query/v5/docs/framework/react/reference/useQuery&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dy7xqE/hyXGN2PBwv/ZlAckyKMKaRjwxKQre7fZ0/img.png?width=3000&amp;amp;height=1704&amp;amp;face=0_0_3000_1704&quot;&gt;&lt;a href=&quot;https://tanstack.com/query/v5/docs/framework/react/reference/useQuery&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tanstack.com/query/v5/docs/framework/react/reference/useQuery&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dy7xqE/hyXGN2PBwv/ZlAckyKMKaRjwxKQre7fZ0/img.png?width=3000&amp;amp;height=1704&amp;amp;face=0_0_3000_1704');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;useQuery | TanStack Query React Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;tsx const { data, dataUpdatedAt, error, errorUpdatedAt, failureCount, failureReason, fetchStatus, isError, isFetched, isFetchedAfterMount, isFetching, isInitialLoading, isLoading, isLoadingError, isPa...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tanstack.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;파라미터1 (옵션)&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;queryKey: unknown[]&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;필수&lt;/b&gt;&lt;br /&gt;이 쿼리에 사용할 쿼리 키입니다.&lt;br /&gt;쿼리 키는 안정적인 해시로 변환됩니다. 자세한 내용은 &lt;a&gt;&lt;span&gt;Query&lt;/span&gt;&lt;span&gt; Keys&lt;/span&gt;&lt;/a&gt;를 참고하세요.&lt;br /&gt;이 키가 변경되면 쿼리는 자동으로 업데이트됩니다(단, enabled가 false로 설정되지 않은 경우).&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;queryFn: (context: QueryFunctionContext) =&amp;gt; Promise&amp;lt;TData&amp;gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;필수&lt;/b&gt; (기본 쿼리 함수가 정의되지 않은 경우)&lt;br /&gt;쿼리가 데이터를 요청할 때 사용할 함수입니다.&lt;br /&gt;QueryFunctionContext를 인자로 받습니다.&lt;br /&gt;반환값은 데이터가 해결되거나 오류가 발생하는 Promise여야 합니다. 반환 데이터는 undefined일 수 없습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;enabled: boolean | (query: Query) =&amp;gt; boolean&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동 실행을 비활성화하려면 false로 설정하세요.&lt;br /&gt;&lt;a href=&quot;https://tanstack.com/query/v5/docs/framework/react/guides/dependent-queries&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;종속 쿼리(Dependent Queries)&lt;/a&gt;에서 사용할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;networkMode: 'online' | 'always' | 'offlineFirst'&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;br /&gt;기본값은 'online'입니다.&lt;br /&gt;자세한 내용은 &lt;a href=&quot;https://tanstack.com/query/v5/docs/framework/react/guides/network-mode&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;Network&lt;/span&gt;&lt;span&gt; Mode&lt;/span&gt;&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;retry: boolean | number | (failureCount: number, error: TError) =&amp;gt; boolean&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본값&lt;/b&gt;: 클라이언트에서는 3, 서버에서는 0&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;false: 실패한 쿼리를 기본적으로 재시도하지 않습니다.&lt;/li&gt;
&lt;li&gt;true: 실패한 쿼리를 무한히 재시도합니다.&lt;/li&gt;
&lt;li&gt;숫자: 예를 들어 3으로 설정하면, 실패한 쿼리를 세 번 재시도합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;retryOnMount: boolean&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본값&lt;/b&gt;: true&lt;br /&gt;false로 설정하면 오류가 있는 경우 마운트 시 쿼리를 재시도하지 않습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;retryDelay: number | (retryAttempt: number, error: TError) =&amp;gt; number&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 재시도까지의 지연 시간을 밀리초 단위로 설정합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: attempt =&amp;gt; Math.min(attempt &amp;gt; 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)는 지수적 백오프를 적용합니다.&lt;/li&gt;
&lt;li&gt;예: attempt =&amp;gt; attempt * 1000는 선형 백오프를 적용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;staleTime: number | ((query: Query) =&amp;gt; number)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;br /&gt;&lt;b&gt;기본값&lt;/b&gt;: 0&lt;br /&gt;데이터가 오래되었다고 간주되기 전의 시간(밀리초 단위)입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Infinity로 설정하면 데이터는 절대 오래되지 않습니다.&lt;/li&gt;
&lt;li&gt;함수로 설정하면 쿼리를 사용하여 staleTime을 계산합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;gcTime: number | Infinity&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본값&lt;/b&gt;: 5분 (SSR 시 Infinity)&lt;br /&gt;미사용 또는 비활성 캐시 데이터가 메모리에 남아 있는 시간(밀리초 단위)입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Infinity로 설정하면 가비지 수집을 비활성화합니다.&lt;br /&gt;&lt;b&gt;참고&lt;/b&gt;: 최대 허용 시간은 약 24일입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;queryKeyHashFn: (queryKey: QueryKey) =&amp;gt; string&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;br /&gt;지정된 경우, 쿼리 키를 해시 문자열로 변환하는 데 사용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;refetchInterval: number | false | ((query: Query) =&amp;gt; number | false | undefined)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자: 지정된 주기(밀리초)마다 쿼리를 다시 가져옵니다.&lt;/li&gt;
&lt;li&gt;함수: 함수가 쿼리를 사용하여 주기를 계산합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;refetchIntervalInBackground: boolean&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;br /&gt;true로 설정하면, 탭/창이 백그라운드에 있을 때도 쿼리를 계속 다시 가져옵니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;refetchOnMount: boolean | &quot;always&quot; | ((query: Query) =&amp;gt; boolean | &quot;always&quot;)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;br /&gt;&lt;b&gt;기본값&lt;/b&gt;: true&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;true: 데이터가 오래되었으면 마운트 시 쿼리를 다시 가져옵니다.&lt;/li&gt;
&lt;li&gt;false: 마운트 시 쿼리를 다시 가져오지 않습니다.&lt;/li&gt;
&lt;li&gt;&quot;always&quot;: 항상 마운트 시 쿼리를 다시 가져옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;refetchOnWindowFocus: boolean | &quot;always&quot; | ((query: Query) =&amp;gt; boolean | &quot;always&quot;)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;br /&gt;&lt;b&gt;기본값&lt;/b&gt;: true&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;true: 데이터가 오래되었으면 창 포커스 시 쿼리를 다시 가져옵니다.&lt;/li&gt;
&lt;li&gt;false: 창 포커스 시 쿼리를 다시 가져오지 않습니다.&lt;/li&gt;
&lt;li&gt;&quot;always&quot;: 항상 창 포커스 시 쿼리를 다시 가져옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;refetchOnReconnect: boolean | &quot;always&quot; | ((query: Query) =&amp;gt; boolean | &quot;always&quot;)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;br /&gt;&lt;b&gt;기본값&lt;/b&gt;: true&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;true: 데이터가 오래되었으면 다시 연결 시 쿼리를 다시 가져옵니다.&lt;/li&gt;
&lt;li&gt;false: 다시 연결 시 쿼리를 다시 가져오지 않습니다.&lt;/li&gt;
&lt;li&gt;&quot;always&quot;: 항상 다시 연결 시 쿼리를 다시 가져옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;notifyOnChangeProps: string[] | &quot;all&quot; | (() =&amp;gt; string[] | &quot;all&quot; | undefined)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;br /&gt;특정 속성이 변경될 때만 컴포넌트를 다시 렌더링합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: ['data', 'error']로 설정하면 data나 error가 변경될 때만 렌더링합니다.&lt;/li&gt;
&lt;li&gt;&quot;all&quot;로 설정하면 스마트 추적을 비활성화하고 쿼리가 업데이트될 때마다 다시 렌더링합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;select: (data: TData) =&amp;gt; unknown&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-4o&quot; data-message-id=&quot;be90df64-56b2-4366-b3c5-879f6aedf814&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 옵션은 쿼리 함수에서 반환된 데이터의 일부를 변환하거나 선택하는 데 사용됩니다.&lt;/li&gt;
&lt;li&gt;반환되는 데이터 값에 영향을 주지만, 쿼리 캐시에 저장되는 데이터에는 영향을 주지 않습니다.&lt;/li&gt;
&lt;li&gt;select 함수는 데이터가 변경되거나 함수 참조가 변경될 때만 실행됩니다.&lt;/li&gt;
&lt;li&gt;최적화를 위해, useCallback으로 함수를 감싸는 것이 권장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;initialData: TData | () =&amp;gt; TData&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설정된 경우, 이 값이 쿼리 캐시의 초기 데이터로 사용됩니다. (쿼리가 생성되거나 캐시되지 않은 경우에만)&lt;/li&gt;
&lt;li&gt;함수로 설정된 경우, 공유/루트 쿼리 초기화 중 한 번 호출되며, initialData를 동기적으로 반환해야 합니다.&lt;/li&gt;
&lt;li&gt;초기 데이터는 기본적으로 오래된(stale) 상태로 간주되지만, staleTime이 설정된 경우는 예외입니다.&lt;/li&gt;
&lt;li&gt;initialData는 캐시에 저장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;initialDataUpdatedAt: number | (() =&amp;gt; number | undefined)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설정된 경우, 이 값이 초기 데이터가 마지막으로 업데이트된 시간을 나타내는 밀리초 단위의 값으로 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;placeholderData: TData | (previousValue: TData | undefined; previousQuery: Query | undefined) =&amp;gt; TData&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설정된 경우, 쿼리가 대기 상태(pending)일 동안 해당 쿼리 옵저버의 플레이스홀더 데이터로 사용됩니다.&lt;/li&gt;
&lt;li&gt;placeholderData는 캐시에 저장되지 않습니다.&lt;/li&gt;
&lt;li&gt;함수로 제공된 경우, 첫 번째 인수로 이전에 조회된 쿼리 데이터를, 두 번째 인수로 완전한 이전 쿼리 객체를 받습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;structuralSharing: boolean | (oldData: unknown | undefined, newData: unknown) =&amp;gt; unknown)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본값은 true입니다.&lt;/li&gt;
&lt;li&gt;false로 설정된 경우, 쿼리 결과 간의 구조적 공유가 비활성화됩니다.&lt;/li&gt;
&lt;li&gt;함수로 설정된 경우, 이전 데이터와 새 데이터 값을 전달받아 이를 결합한 결과를 반환해야 합니다. 이를 통해 직렬화할 수 없는 값이 포함된 경우에도 성능을 개선할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;throwOnError: undefined | boolean | (error: TError, query: Query) =&amp;gt; boolean&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본값은 글로벌 쿼리 설정의 throwOnError 값(기본적으로 undefined)입니다.&lt;/li&gt;
&lt;li&gt;true로 설정하면, 렌더링 단계에서 오류가 발생 시 이를 인근 오류 경계로 전파합니다.&lt;/li&gt;
&lt;li&gt;false로 설정하면, Suspense의 기본 동작(오류를 오류 경계로 전파)을 비활성화합니다.&lt;/li&gt;
&lt;li&gt;함수로 설정된 경우, 오류와 쿼리를 전달받아 오류 경계에서 오류를 표시할지(true) 또는 상태로 반환할지(false)를 결정해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;meta: Record&amp;lt;string, unknown&amp;gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설정된 경우, 쿼리 캐시 항목에 추가 정보를 저장합니다.&lt;/li&gt;
&lt;li&gt;이는 쿼리에서 접근 가능하며, queryFn에 제공되는 QueryFunctionContext의 일부로도 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Parameter2 (QueryClient)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;queryClient?: QueryClient&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 정의 QueryClient를 사용하려면 설정하십시오. 그렇지 않으면 가장 가까운 컨텍스트의 클라이언트가 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;반환값&lt;/b&gt;&lt;/h4&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;status: QueryStatus
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음 값 중 하나를 반환합니다:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pending: 캐시된 데이터가 없고 쿼리가 완료되지 않았을 때.&lt;/li&gt;
&lt;li&gt;error: 쿼리 시도가 오류로 끝났을 때. 오류는 error 속성에 포함됩니다.&lt;/li&gt;
&lt;li&gt;success: 오류 없이 응답을 수신한 경우.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;isPending: boolean&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 status에서 파생된 boolean 값입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;isSuccess: boolean&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;status에서 파생된 boolean 값입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;isError: boolean&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;status에서 파생된 boolean 값입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;isLoadingError: boolean&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리가 첫 번째 요청 중 실패했을 때 true입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;isRefetchError: boolean&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다시 요청하는 동안 실패한 경우 true입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;data: TData&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본값은 undefined입니다.&lt;/li&gt;
&lt;li&gt;쿼리의 마지막으로 성공적으로 해결된 데이터입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;dataUpdatedAt: number&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리가 마지막으로 성공 상태를 반환한 시간의 타임스탬프입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;error: null | TError&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본값은 null입니다.&lt;/li&gt;
&lt;li&gt;쿼리 중 발생한 오류 객체입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;errorUpdatedAt: number&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리가 마지막으로 오류 상태를 반환한 시간의 타임스탬프입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;isStale: boolean&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시의 데이터가 무효화되었거나, staleTime보다 오래된 경우 true입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;isPlaceholderData: boolean&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;표시된 데이터가 플레이스홀더 데이터인 경우 true입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;isFetched: boolean&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리가 실행된 경우 true입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;fetchStatus: FetchStatus&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fetching: 쿼리 함수가 실행 중일 때.&lt;/li&gt;
&lt;li&gt;paused: 쿼리가 데이터를 요청했지만 일시 중지된 경우.&lt;/li&gt;
&lt;li&gt;idle: 쿼리가 데이터를 요청하지 않을 때.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;failureCount: number&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리 실패 횟수입니다. 성공 시 0으로 초기화됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) =&amp;gt; Promise&amp;lt;UseQueryResult&amp;gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리를 수동으로 다시 요청하는 함수입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;&lt;b&gt;promise: Promise&amp;lt;TData&amp;gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿼리 데이터를 포함한 안정적인 Promise 객체입니다. experimental_prefetchInRender 플래그를 활성화해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>REACT/React-개념정리</category>
      <author>찰리-누나</author>
      <guid isPermaLink="true">https://make-somthing.tistory.com/108</guid>
      <comments>https://make-somthing.tistory.com/108#entry108comment</comments>
      <pubDate>Wed, 4 Dec 2024 02:09:50 +0900</pubDate>
    </item>
    <item>
      <title>[React] Zustand의 persist</title>
      <link>https://make-somthing.tistory.com/107</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트 페이지에서 새로고침이 되어도 값이 변경되지 않아야 하는 전역 정보를 다룰 때에는 어떻게 하면 좋을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 로그인의 경우 accessToken의 수명이 다할 때까지 어딘가에 반드시 저장이 되어 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해, Zustand의 persist를 사용해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; Zustand 의 Persist란?&lt;/b&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;persist 미들웨어&lt;/b&gt;는 Zustand를 사용하여 스토어의 상태를 &lt;b&gt;로컬 스토리지(localStorage)&lt;/b&gt; 또는 다른 저장소에 유지(persist)하기 위해 사용된다. 이 미들웨어는 Zustand 상태를 지정된 저장소에 저장하고, 애플리케이션 초기화 시 저장된 상태를 복원한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;persist의 사용 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;persist는 두 가지 주요 매개변수를 받는다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;config&lt;/b&gt;: Zustand 상태와 상태 변경 함수들을 정의한 &lt;b&gt;StateCreator&lt;/b&gt;.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: setAuthData, setAuthToken, clearAuth와 같은 상태 변경 함수들이 포함된 콜백 함수.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;options&lt;/b&gt;: 상태를 저장하고 복원하기 위한 &lt;b&gt;설정 옵션&lt;/b&gt;.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저장소 이름, 스토리지 메커니즘(localStorage 등), 직렬화/역직렬화 방법 등을 지정.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;persist를 사용한 코드 예시&lt;/b&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1732656628583&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { StateCreator } from &quot;zustand&quot;;
import { PersistOptions, createJSONStorage, persist } from &quot;zustand/middleware&quot;;
import { createWithEqualityFn } from &quot;zustand/traditional&quot;;
import { useShallow } from &quot;../hooks/useShallow&quot;;

// AuthStoreType 정의
type AuthStoreType = {  
  loginId?: string; // 로그인 ID
  accessToken?: string; // 액세스 토큰
  refreshToken?: string; // 리프레시 토큰
  remember?: boolean; // 로그인 기억 여부

  // 상태 변경 함수들
  setAuthData: (data: Partial&amp;lt;AuthStoreType&amp;gt;) =&amp;gt; void; // JSON 데이터로 상태 설정
  setAuthToken: (accessToken: string, refreshToken: string) =&amp;gt; void;
  clearAuth: () =&amp;gt; void; // 상태 초기화
  ...
};

// persist 미들웨어 타입 정의
type PersistAuthStoreType = (
  config: StateCreator&amp;lt;AuthStoreType&amp;gt;,
  options: PersistOptions&amp;lt;AuthStoreType&amp;gt;
) =&amp;gt; StateCreator&amp;lt;AuthStoreType&amp;gt;;

// Zustand 스토어 생성
export const store = createWithEqualityFn&amp;lt;AuthStoreType&amp;gt;(
  (persist as PersistAuthStoreType)(
    (set) =&amp;gt; ({
      // 상태 변경 함수들 구현
      setAuthData: (data) =&amp;gt; set((state) =&amp;gt; ({ ...state, ...data })),
      setAuthToken: (accessToken, refreshToken) =&amp;gt;
        set({ accessToken, refreshToken }),      
      clearAuth:() =&amp;gt; set({ accessToken: undefined })       
    }),
    {
      name: &quot;admin-auth&quot;, // 로컬 스토리지 키 이름
      storage: createJSONStorage(() =&amp;gt; localStorage), // 로컬 스토리지에 상태 저장
    }
  )
);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;config와 options 값&lt;/b&gt;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. config&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;config는 &lt;b&gt;StateCreator 타입&lt;/b&gt;이며, 위의 코드에서 Zustand 상태 정의와 상태 변경 함수들을 포함한다.&lt;/li&gt;
&lt;li&gt;위 코드에서 config는&lt;b&gt; (set) =&amp;gt; ({ ... })로 작성된 콜백 함수 &lt;/b&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1732656786412&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; (set) =&amp;gt; ({
      // 상태 변경 함수들 구현
      setAuthData: (data) =&amp;gt; set((state) =&amp;gt; ({ ...state, ...data })),
      setAuthToken: (accessToken, refreshToken) =&amp;gt;
        set({ accessToken, refreshToken }),      
      clearAuth:() =&amp;gt; set({ accessToken: undefined })       
    })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. options&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;options는 PersistOptions&amp;lt;AuthStoreType&amp;gt; 타입이며, 상태를 저장/복원하기 위한 설정이다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;코드에서 options는 다음과 같이 정의되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1732656843630&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  name: &quot;admin-auth&quot;, // 로컬 스토리지에 저장될 키 이름
  storage: createJSONStorage(() =&amp;gt; localStorage), // 저장소를 localStorage로 설정
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;options 세부 설명&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;name&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬 스토리지에 저장되는 키 이름을 지정한다.&lt;/li&gt;
&lt;li&gt;여기서는 &quot;admin-auth&quot;로 설정되어, 로컬 스토리지에 이 키로 상태가 저장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;storage&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태를 저장할 스토리지 메커니즘을 정의한다.&lt;/li&gt;
&lt;li&gt;createJSONStorage(() =&amp;gt; localStorage)를 사용하여 **localStorage**를 스토리지로 사용하였다.&lt;/li&gt;
&lt;li&gt;localStorage 외에도 sessionStorage, IndexedDB, 커스텀 스토리지를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;serialize와 deserialize&lt;/b&gt; (옵션에는 없지만, 기본값으로 처리됨)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;persist 미들웨어는 상태를 JSON 형태로 저장하므로, 기본적으로 JSON의 stringify와 parse를 사용해 직렬화/역직렬화를 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동작 방식&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;애플리케이션 시작 시:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;admin-auth라는 키로 로컬 스토리지에서 저장된 값을 가져온다.&lt;/li&gt;
&lt;li&gt;만약 저장된 값이 있으면, Zustand 스토어 상태를 복원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;상태 변경 시:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;set 함수가 호출되면, 상태가 변경되고 해당 상태는 로컬 스토리지에 직렬화되어 저장된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실제 동작 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. setAuthToken을 호출하여 상태를 업데이트한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732656921941&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;store.getState().setAuthToken(&quot;newAccessToken&quot;, &quot;newRefreshToken&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 상태가 변경된 후, 로컬 스토리지에 다음 데이터들이 저장된다.&lt;/p&gt;
&lt;pre id=&quot;code_1732656942973&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;accessToken&quot;: &quot;newAccessToken&quot;,
  &quot;refreshToken&quot;: &quot;newRefreshToken&quot;,
  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;애플리케이션 재시작 시 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; admin-auth키를 읽고, 저장된 상태를 스토어에 복원한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;config&lt;/b&gt;: 상태와 상태 변경 함수들을 정의하는 &lt;b&gt;콜백 함수&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;options&lt;/b&gt;: 저장소 이름(name)과 스토리지(storage) 설정을 포함한 &lt;b&gt;옵션 객체&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;persist의 역할&lt;/b&gt;: 상태를 로컬 스토리지 또는 지정된 스토리지에 저장하고, 초기화 시 복원하여 데이터 유실을 방지.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>프로젝트/React 프론트 프로젝트</category>
      <author>찰리-누나</author>
      <guid isPermaLink="true">https://make-somthing.tistory.com/107</guid>
      <comments>https://make-somthing.tistory.com/107#entry107comment</comments>
      <pubDate>Wed, 27 Nov 2024 06:37:05 +0900</pubDate>
    </item>
    <item>
      <title>[React] 값이 없는 상태를 어떻게 관리해야 할까? undefined와 null</title>
      <link>https://make-somthing.tistory.com/106</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: center;&quot; data-ke-size=&quot;size20&quot;&gt;개발을 하다 보면, 필연적으로 &lt;b&gt;'값이 없는 상태'&lt;/b&gt; 를 관리해야 하는 경우가 생긴다. &lt;/h4&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이 때, &lt;b&gt;undefined&lt;/b&gt;를 쓰는 게 좋을까, &lt;b&gt;null&lt;/b&gt;을 쓰는 게 좋을까?&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, &lt;b&gt;undefined와 &lt;b&gt;null에 대해 알아보자.&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;undefined와 null의 차이&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;undefined&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수가 선언되었지만 초기화되지 않은 상태.&lt;/li&gt;
&lt;li&gt;값이 아예 없음을 나타냄.&lt;/li&gt;
&lt;li&gt;타입스크립트에서 Optional 속성(?)의 기본 상태.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;null&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의도적으로 &quot;값이 없음&quot;을 명시.&lt;/li&gt;
&lt;li&gt;값이 존재하지 않음을 프로그래머가 명시적으로 설정한 상태.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;아하, 그러니까 &lt;b&gt;undefined는 '변수가 선언되기만 한 상태'&lt;/b&gt; 이고, &lt;b&gt;null은 '값이 없다는 것을 넣어준 상태'&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;즉 undefined는 '데이터의 상태를 초기화하지 않은 상태(어떠한 값도 넣지 않은 채)로 남겨두는 경우이며,&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;null은 '이 변수에 아직 적절한 값을 할당하지 않았습니다' 를 &quot;명시적&quot; 으로 넣어둔 상태라고 할 수 있겠다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;이제 어떤 상태에서 undefined를 사용할지 , 또 어떤 상태에서 null을 사용할지만 결정하면 된다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;로그인 api를 불러와서, 로그인 한 유저의 데이터를 변수에 넣어준다고 가정해보자.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;다음과 같은 변수와 데이터들이 존재할 것이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;accessToken&lt;/li&gt;
&lt;li&gt;refreshToken&lt;/li&gt;
&lt;li&gt;userName&lt;/li&gt;
&lt;li&gt;phoneNumber&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중, 'phoneNumber'은 '본인인증을 한 유저에게만 존재하는 값' 이라고 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼, 'phoneNumber' 은 '로그인을 한 유저에게 존재하는 변수이되, 값이 없을 수도, 있을 수도 있는 변수' 가 된다. 그러니까, null이거나 string이 들어있는 둘 중 하나의 상태인 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-4o&quot; data-message-id=&quot;c0185b11-b4ba-4241-a19e-26453c962b00&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도와 컨텍스트에 따라 undefined과 null 중 사용 여부를 나누어 보자면, 다음과 같이 정리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;컨텍스트에 따른 선택&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. &lt;b&gt;undefined를 사용할 때&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터의 상태를 초기화하지 않은 상태로 남겨두는 경우.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;타입스크립트의 Partial 또는 선택적 속성(?)과 자연스럽게 호환된다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;대부분의 상황에서 &quot;아직 값을 모르는 상태&quot;를 나타낼 때 적합.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1732655433103&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type User = {
  accessToken?: string; // 값이 없을 때 undefined
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. &lt;b&gt;null을 사용할 때&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값이 없다는 것을 명시적으로 나타내야 하는 경우.&lt;/li&gt;
&lt;li&gt;데이터베이스나 API와 통신할 때, null이 사용될 가능성이 있는 경우.&lt;/li&gt;
&lt;li&gt;&quot;값이 없음&quot;을 구체적으로 구분하고 싶을 때 적합.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1732655448678&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type User = {
  phoneNumber: string | null; // 값이 없음을 명시적으로 표현
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이해하기 편하게 정리하자면 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;undefined로 초기화할 속성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ID, 이름, 토큰&lt;/b&gt; 등 상태 초기화 시 값을 모르는 것이 자연스러운 속성.&lt;/li&gt;
&lt;li&gt;대부분의 속성(uuid, userNo 등)에 대해 값이 초기화되지 않았음을 나타낼 때.&lt;/li&gt;
&lt;li&gt;명시적으로 &quot;알 수 없는 상태&quot;를 표현하고 기본값으로 유지.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;null로 초기화할 속성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;연락처&lt;/b&gt;처럼 데이터베이스나 API에서 값이 없음을 명시적으로 나타내야 하는 속성.&lt;/li&gt;
&lt;li&gt;즉, &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;특정 속성(attlistMbtlNo, mbtlNo)이 API 설계나 도메인 모델에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;값이 없음을 명시적으로 표현&lt;/b&gt;할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, '아직 로그인이 되지 않아 값이 없는 것들' 은 undefined로 두되, phoneNumber처럼 '로그인 한 유저의 데이터 베이스에 null이 들어있을 수도 있는 값' 은 NULL로 설정하면 코드를 읽는 것만으로 값의 null 여부를 알 수 있어 편리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로젝트/React 프론트 프로젝트</category>
      <author>찰리-누나</author>
      <guid isPermaLink="true">https://make-somthing.tistory.com/106</guid>
      <comments>https://make-somthing.tistory.com/106#entry106comment</comments>
      <pubDate>Wed, 27 Nov 2024 06:14:11 +0900</pubDate>
    </item>
    <item>
      <title>[React] Zustand를 이용하여 useModal 훅 만들기</title>
      <link>https://make-somthing.tistory.com/105</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외주 프로젝트 중 여러 알림을 쌓으면서 관리해야 하는 Alert, 한 번에 하나만 띄워져야 하는 Modal을 useModal이라는 하나의 훅을 이용해 관리하기 위해 작성해 보았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;코드의 흐름 설명&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;모달 상태 관리(useModalStore)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useModalStore는 Zustand를 사용해 모달의 상태를 관리하며, stack 옵션에 따라 모달의 관리 방식을 조정합니다.&lt;/li&gt;
&lt;li&gt;modals: 현재 열려 있는 모달의 배열입니다.&lt;/li&gt;
&lt;li&gt;open: 특정 컴포넌트를 모달로 열고, 관련 데이터를 상태에 추가합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stack: true: modals 배열에 새로운 모달을 추가합니다.&lt;/li&gt;
&lt;li&gt;stack: false: 기존 모달이 없을 때만 추가. 기존 모달이 있다면 **&quot;모달을 추가할 수 없습니다&quot;**라는 메시지를 console에 출력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;close: 특정 key를 가진 모달을 닫습니다.&lt;/li&gt;
&lt;li&gt;clear: 모든 모달을 닫습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;useModal 훅&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useModal은 openModal 와 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;closeModal&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 메서드를 제공하여 모달을 열고 닫는 기능을 제공합니다.&lt;/li&gt;
&lt;li&gt;openModal은 새로운 모달을 열기 위해 사용됩니다. 모달에 전달할 컴포넌트(Component)와 프로퍼티(props)를 받습니다. stack: false로 설정된 경우, 이미 모달이 존재하면 새 모달을 추가하지 않습니다.&lt;/li&gt;
&lt;li&gt;closeModal은 특정 key를 사용해 모달을 닫습니다. 상태에서 key를 가진 모달을 제거합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ModalContainer 컴포넌트&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ModalContainer는 Zustand의 modals 상태를 기반으로 실제 모달을 화면에 렌더링합니다.&lt;/li&gt;
&lt;li&gt;Zustand의 useModalStore에서 modals 배열과 close 메서드를 가져옵니다.&lt;/li&gt;
&lt;li&gt;useEffect로 모달을 렌더링할 DOM 요소(portal)를 초기화합니다.&lt;/li&gt;
&lt;li&gt;modals 배열을 순회하며, 각 모달의 Component를 createPortal을 이용해 portal에 렌더링합니다.&lt;/li&gt;
&lt;li&gt;모달 닫기와 확인 버튼 처리를 위해 각각 handleClose와 handleSubmit을 정의합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;handleClose: onClose 실행 후 모달 닫기.&lt;/li&gt;
&lt;li&gt;handleSubmit: onSubmit 실행 후 모달 닫기.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;u&gt;1. 먼저, modalStore를 작성합니다.&lt;/u&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;modalStore는 모달이 가진 공통 속성들과 현재 화면에 띄워진 모달 전체를 관리하는 데에 사용됩니다. 사용자 요구 사항은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;쌓을 수 있는 모달의 경우, 이미 띄워진 모달이 있으면 그 위에 새로운 모달을 띄운다. 새로운 모달을 닫으면, 기존의 모달이 보여야 한다.&lt;/li&gt;
&lt;li&gt;쌓을 수 없는 모달(ex:alert)의 경우, 이미 띄워진 모달이 있으면 그 모달을 닫을 때까지 새로운 모달을 띄울 수 없다. 또한, 배경이 오퍼시티가 적용된 반투명한 검은색으로 깔려 모달(alert) 이외의 영역을 클릭할 수 없어야 한다. 반드시 한 번에 하나의 모달만 띄울 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 모달 컴포넌트와 모달 스토어를 정리해보면, 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모달 컴포넌트는 &lt;/b&gt;다음과 같은 필수 props를 필요로 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;key : 모달의 고유 키&lt;/li&gt;
&lt;li&gt;Component : 렌더링 될 모달 컴포넌트&lt;/li&gt;
&lt;li&gt;props : 렌더링 될 모달 컴포넌트에 전송될, 기타 props&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모달의 상태를 관리하는 스토어는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;다음과 같은 필수 props를 필요로 합니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;onClose // 모달을 닫는 함수&lt;/li&gt;
&lt;li&gt;onSubmit // 모달 내에 확인 등 close 이외의 버튼이 존재할 경우를 대비한 콜백 함수&lt;/li&gt;
&lt;li&gt;modalId // 모달 고유 식별자&lt;/li&gt;
&lt;li&gt;[property:string] : any // ...rest를 받아오기 위한 타입&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모달 스토어의 역할&lt;/b&gt;은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자의 요구사항에 맞추어&lt;b&gt; '모달을 여러개 쌓을 수 있느냐, 하나만 쌓을 수 있느냐'&lt;/b&gt; 를 판별하기 위해 &lt;b&gt;stack&lt;/b&gt;이라는 변수를 사용합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;stack&lt;/b&gt;이 &lt;b&gt;true&lt;/b&gt;일 경우, 모달을 리스트 형태로 저장하여 여러 모달을 쌓을 수 있게 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;stack&lt;/b&gt;이 &lt;b&gt;false&lt;/b&gt;일 경우, 리스트에 단 하나의 모달만 들어갈 수 있게 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용자가 모달을 열 수 있게 하기 위하여, 리스트에 모달을 저장하는 함수를 제공합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stack이 false일 경우, 리스트 내에 이미 모달이 존재할 시 에러 콘솔을 출력합니다.&lt;/li&gt;
&lt;li&gt;stack이 false이면서 리스트 내에 할당된 모달이 없으면, 리스트에 모달을 하나 넣습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용자가 모달을 닫을 수 있게 하기 위하여, 리스트에서 모달을 제거하는 함수를 제공합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 식별자 modalId와 같은 아이템을 찾아 리스트에서 제거합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모든 모달을 닫기 위하여, 모달 리스트를 공배열로 초기화하는 함수를 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1732646766859&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ComponentType } from &quot;react&quot;; // React에서 제공하는 타입, 컴포넌트 타입 정의에 사용
import { create } from &quot;zustand&quot;; // Zustand 라이브러리에서 `create` 함수 가져오기

// 모달 컴포넌트에 전달할 프로퍼티 타입 정의
export type ModalPropsType = {
  onClose?: () =&amp;gt; void; // 모달 닫기 콜백 함수 (선택적)
  onSubmit?: (event?: any) =&amp;gt; void; // 모달 확인 버튼 클릭 콜백 함수 (선택적)
  modalId?: string; // 모달 고유 식별자 (내부에서 관리)
  [property: string]: any; // 추가적인 속성을 허용
};

// 모달의 데이터 타입 정의
type ModalType = {
  key: string; // 모달의 고유 키 (중복 방지)
  props: ModalPropsType; // 모달에 전달되는 프로퍼티
  Component: ComponentType&amp;lt;ModalPropsType&amp;gt;; // 렌더링될 React 컴포넌트
};

// 모달 스토어의 상태와 메서드 타입 정의
type ModalStoreType = {
  modals: ModalType[]; // 현재 열려 있는 모달들의 배열
  open: (Component: ComponentType&amp;lt;any&amp;gt;, props: ModalPropsType) =&amp;gt; void; // 모달 열기 메서드
  close: (key: string) =&amp;gt; void; // 특정 모달 닫기 메서드
  clear: () =&amp;gt; void; // 모든 모달 닫기 메서드
};

// Zustand를 사용하여 모달 스토어 생성
export const useModalStore = create&amp;lt;ModalStoreType&amp;gt;((set, get) =&amp;gt; ({
  modals: [], // 초기 상태: 모달 리스트는 빈 배열
  open: (Component: ComponentType&amp;lt;any&amp;gt;, props: ModalPropsType) =&amp;gt; {
    const { modals } = get(); // 현재 열려 있는 모달 리스트 가져오기
    const modal = props.key ? modals.find((m) =&amp;gt; m.key === props.key) : null;
    // key가 제공되었을 경우, 동일한 key를 가진 모달이 이미 열려 있는지 확인
    const key = props.key || Date.now().toString(); // key가 없으면 현재 시간을 문자열로 사용
    props.modalId = key; // 모달 ID를 props에 저장

    // stack: false인 경우
    if (props.stack === false) {
      if (modals.length &amp;gt; 0) {
        console.error(
          &quot;새로운 모달을 열 수 없습니다. 기존 모달이 닫혀야 합니다.&quot;
        );
        return;
      }
      set({ modals: [{ Component, props, key }] }); // 기존 모달을 덮어씀
      return;
    }
    // stack: true 경우
    if (!modal) {
      set({ modals: [...modals, { Component, props, key }] });
    }
  },
  close: (key: string) =&amp;gt; {
    const { modals } = get(); // 현재 열려 있는 모달 리스트 가져오기
    set({ modals: modals.filter((m) =&amp;gt; m.key !== key) });
    // 주어진 key와 일치하지 않는 모달들만 남김 (특정 모달 닫기)
  },
  clear: () =&amp;gt; {
    set({ modals: [] }); // 모든 모달을 닫음 (리스트 초기화)
  },
}));

// 모달 열기 함수만 선택적으로 사용하는 훅
export const useModalStoreOpen = () =&amp;gt; useModalStore((state) =&amp;gt; state.open);

// 모달 닫기 함수만 선택적으로 사용하는 훅
export const useModalStoreClose = () =&amp;gt; useModalStore((state) =&amp;gt; state.close);

// 모든 모달 초기화 함수만 선택적으로 사용하는 훅
export const useModalStoreClear = () =&amp;gt; useModalStore((state) =&amp;gt; state.clear);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;2. 모달 스토어를 활용한 useModal hook을 작성합니다. &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 주 활동은&lt;b&gt; '모달 열기, 모달 닫기'&lt;/b&gt; 로 유추되므로 open과 close 함수를 제공해 주는것이 핵심입니다. 이 때, 'stack' 이라는 값이 주어지지 않으면 true로 할당되도록 하여 undefined 에러가 나는 것을 방지합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732647370194&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ComponentType } from &quot;react&quot;;
import {
  ModalPropsType,
  useModalStoreClose,
  useModalStoreOpen,
} from &quot;../stores/modalStore&quot;;

export default function useModal() {
  const open = useModalStoreOpen();
  const close = useModalStoreClose();

  const openModal = &amp;lt;P extends ModalPropsType&amp;gt;(
    Component: ComponentType&amp;lt;P&amp;gt;,
    props?: P &amp;amp; { stack?: boolean }
  ) =&amp;gt; {
    open(Component, { ...props, stack: props?.stack ?? true });
  };

  const closeModal = (key: string) =&amp;gt; {
    close(key);
  };

  return { openModal, closeModal };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;3. 실제로 모달을 화면에 렌더링하는&amp;nbsp;ModalContainer를 작성하고, index.html에 모달이 렌더링 될 자리를 만듭니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 modal은 ModalContainer를 통해 렌더링됩니다. createPortal를 통해 div의 id가 modal인 곳을 타겟으로 모달을 렌더링 할 것이므로, index.html에 &amp;lt;div id=&quot;modal&quot;&amp;gt;&amp;lt;/div&amp;gt;를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732647799263&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
    &amp;lt;link rel=&quot;icon&quot; type=&quot;image/svg+xml&quot; href=&quot;/vite.svg&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
    &amp;lt;title&amp;gt;crmSoft&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;div id=&quot;root&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div id=&quot;modal&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;script type=&quot;module&quot; src=&quot;/src/main.tsx&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ModalContainer를 작성합니다. createPortal를 통해 컴포넌트와 컴포넌트가 생성될 위치를 설정하면, 해당 위치에 지정한 컴포넌트를 마운트 할 수 있습니다. 이 때, ModalContainer이 마운트 되고 난 뒤 모달이 생성될 portal DOM을 할당하기 위하여 useEffect를 사용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 현재 모달의 stack여부를 판단하여 사용자가 요구한 아래 사항을 충족시키기 위해 isStackFalse 변수를 만들었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;* 쌓을 수 없는 모달(ex:alert)의 경우, &lt;u&gt;이미 띄워진 모달이 있으면 그 모달을 닫을 때까지 새로운 모달을 띄울 수 없다.&lt;/u&gt; 또한, &lt;u&gt;배경이 오퍼시티가 적용된 반투명한 검은색으로 깔려 모달(alert) 이외의 영역을 클릭할 수 없어야 한다.&lt;/u&gt; 반드시 &lt;u&gt;한 번에 하나의 모달&lt;/u&gt;만 띄울 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;modals.length === 1: 현재 modals 배열에 모달이 정확히 1개 존재하는지 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;modals[0].props.stack === false: 그 첫 번째 모달(modals[0])의 props에서 stack 값이 false인지를 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 조건이 모두 참일 경우, isStackFalse는 true가 되고, 그렇지 않으면 false가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 isStackFalse는 &quot;현재 열려 있는 모달이 하나이고, 그 모달이 stack: false로 설정된 경우&quot; 를 확인하는 조건입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조건을 통해 stack: false로 설정된 모달이 하나만 열려 있을 때, 새로운 모달을 열지 않도록 제어하는 용도로 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; isStackFalse이 참일 때, '모달의 배경에 반투명한 검은색의 클릭할 수 없는 영역' 을 설정하기 위하여 tailwind를 사용한 div를 사용해 주었습니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732647662282&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ModalPropsType, useModalStore } from &quot;../../stores/modalStore&quot;;
import { Nullable } from &quot;../../types&quot;;
import { useEffect, useState } from &quot;react&quot;;
import { createPortal } from &quot;react-dom&quot;;

export default function ModalContainer() {
  // 현재 모달 리스트 및 모달 닫기 메서드
  const { modals, close } = useModalStore();

  // 모달을 렌더링할 DOM 요소
  const [portal, setPortal] = useState&amp;lt;Nullable&amp;lt;Element&amp;gt;&amp;gt;(null);

  // 컴포넌트가 마운트되었을 때 모달을 렌더링할 DOM 요소를 설정
  useEffect(() =&amp;gt; {
    setPortal(document.getElementById(&quot;modal&quot;));
  }, []);

  const isStackFalse = modals.length === 1 &amp;amp;&amp;amp; modals[0].props.stack === false;

  return (
    portal &amp;amp;&amp;amp;
    createPortal(
      &amp;lt;&amp;gt;
        {isStackFalse &amp;amp;&amp;amp; (
          &amp;lt;div className=&quot;fixed inset-0 bg-black bg-opacity-50 z-50 pointer-events-auto&quot; /&amp;gt;
        )}
        {/* 외부 클릭 차단 */}
        {modals.map((m) =&amp;gt; {
          const { Component, props, key } = m;
          const {
            onSubmit = () =&amp;gt; {},
            onClose = () =&amp;gt; {},
            ...restProps
          } = props;

          const handleClose = async () =&amp;gt; {
            await onClose?.();
            close(key);
          };

          const handleSubmit = async (_props?: ModalPropsType) =&amp;gt; {
            await onSubmit?.(_props);
            close(key);
          };

          return (
            &amp;lt;Component
              key={key}
              modalId={props.modalId}
              onSubmit={handleSubmit}
              onClose={handleClose}
              {...restProps}
            /&amp;gt;
          );
        })}
      &amp;lt;/&amp;gt;,
      portal
    )
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;4. App에 작성한 ModalContainer 컴포넌트를 넣습니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732648321175&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function App() {
  return (
    &amp;lt;ThemeProvider&amp;gt;
      &amp;lt;RouterProvider router={router} /&amp;gt;
      &amp;lt;ModalContainer /&amp;gt;
    &amp;lt;/ThemeProvider&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; useModal 사용 예시 &lt;/b&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack을 true로 주면 여러개 쌓을 수 있는 모달을, false로 주면 단 하나의 모달만 띄우도록 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해, 쌓이는 모달과 하나만 가능한 커스텀 Alert를 이용할 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732648761446&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// BasicAlert은 직접 만든 컴포넌트

const AlertComponent = ({
  onClose,
  onSubmit,
  modalId,
}: {
  onClose: () =&amp;gt; void;
  onSubmit: () =&amp;gt; void;
  modalId: string;
}) =&amp;gt; {
  return (
    &amp;lt;BasicAlert&amp;gt;
      &amp;lt;BasicAlert.Title&amp;gt;{modalId}&amp;lt;/BasicAlert.Title&amp;gt;
      &amp;lt;BasicAlert.ButtonZone&amp;gt;
        &amp;lt;BasicButton onClick={onClose} variant=&quot;outlined&quot;&amp;gt;
          취소
        &amp;lt;/BasicButton&amp;gt;
        &amp;lt;BasicButton onClick={onSubmit} variant=&quot;contained&quot;&amp;gt;
          확인
        &amp;lt;/BasicButton&amp;gt;
      &amp;lt;/BasicAlert.ButtonZone&amp;gt;
    &amp;lt;/BasicAlert&amp;gt;
  );
};


export default function App() {

  const { openModal, closeModal } = useModal();

  const handleOpenStackedModal = () =&amp;gt; {
    openModal(AlertComponent, {
      modalId: &quot;alert1&quot;,
      stack: false, // 단일 모달 모드, 쌓기 식으로 하려면 true
      onClose: () =&amp;gt; console.log(&quot;모달 닫힘&quot;),
      onSubmit: () =&amp;gt; console.log(&quot;확인&quot;),
    });
  };
  
  return(
  	&amp;lt;div&amp;gt;
    	&amp;lt;button onClick={handleOpenStackedModal}
    &amp;lt;/div&amp;gt;
  );
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러개 쌓을 수 있는 경우 (stack:true)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3Ot9k/btsKWHht4Ry/wGsFtAbKXe4n6d5ckR9SKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3Ot9k/btsKWHht4Ry/wGsFtAbKXe4n6d5ckR9SKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3Ot9k/btsKWHht4Ry/wGsFtAbKXe4n6d5ckR9SKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3Ot9k%2FbtsKWHht4Ry%2FwGsFtAbKXe4n6d5ckR9SKK%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;1021&quot; height=&quot;278&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나만 사용 가능한 경우 (stack:false)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJjJuE/btsKWP7wNjI/IEc7YLmi0A2jlkKIMDecq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJjJuE/btsKWP7wNjI/IEc7YLmi0A2jlkKIMDecq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJjJuE/btsKWP7wNjI/IEc7YLmi0A2jlkKIMDecq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJjJuE%2FbtsKWP7wNjI%2FIEc7YLmi0A2jlkKIMDecq0%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;962&quot; height=&quot;216&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>프로젝트/React 프론트 프로젝트</category>
      <author>찰리-누나</author>
      <guid isPermaLink="true">https://make-somthing.tistory.com/105</guid>
      <comments>https://make-somthing.tistory.com/105#entry105comment</comments>
      <pubDate>Wed, 27 Nov 2024 04:24:45 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] 삼성 SW 1945. 간단한 소인수분해</title>
      <link>https://make-somthing.tistory.com/104</link>
      <description>&lt;pre id=&quot;code_1715923424028&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(bf.readLine(), &quot;\n&quot;);
		int N = Integer.parseInt(st.nextToken());
		List&amp;lt;Integer&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
		
		// 소인수분해
		
		for (int i=2; i&amp;lt;=N; i++) {
			while(N%i==0) {
				list.add(i);
				N = N/i;
			}
		}
		
		
		// list에서 특정 글자의 개수를 세려면?
		long a = list.stream().filter(n-&amp;gt;n==2).count();
		long b = list.stream().filter(n-&amp;gt;n==3).count();
		long c = list.stream().filter(n-&amp;gt;n==5).count();
		long d = list.stream().filter(n-&amp;gt;n==7).count();
		long e = list.stream().filter(n-&amp;gt;n==11).count();
		

		System.out.print(a + &quot; &quot; + b + &quot; &quot; + c + &quot; &quot; + d + &quot; &quot; + e);
		
	}&lt;/code&gt;&lt;/pre&gt;</description>
      <author>찰리-누나</author>
      <guid isPermaLink="true">https://make-somthing.tistory.com/104</guid>
      <comments>https://make-somthing.tistory.com/104#entry104comment</comments>
      <pubDate>Fri, 17 May 2024 14:24:05 +0900</pubDate>
    </item>
  </channel>
</rss>