<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>하이고니의 개발 스토리</title>
    <link>https://hajongon.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 5 Apr 2026 14:12:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>하이고니</managingEditor>
    <image>
      <title>하이고니의 개발 스토리</title>
      <url>https://tistory1.daumcdn.net/tistory/5796980/attach/dae3d1768c544b65b8aa61f177722ad5</url>
      <link>https://hajongon.tistory.com</link>
    </image>
    <item>
      <title>webpack으로 리액트 프로젝트 배포하기</title>
      <link>https://hajongon.tistory.com/228</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;create-react-app 을 사용하지 않고 하나하나 설치해서 배포까지 해보겠습니다. 화이팅!&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-filename=&quot;Screen Shot 2023-03-22 at 5.50.56 PM.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cE83lj/btr5oNBOB35/zQJLaqlyKEY6XZtSd1QJM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cE83lj/btr5oNBOB35/zQJLaqlyKEY6XZtSd1QJM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cE83lj/btr5oNBOB35/zQJLaqlyKEY6XZtSd1QJM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcE83lj%2Fbtr5oNBOB35%2FzQJLaqlyKEY6XZtSd1QJM1%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;400&quot; height=&quot;404&quot; data-filename=&quot;Screen Shot 2023-03-22 at 5.50.56 PM.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;848&quot;/&gt;&lt;/span&gt;&lt;/figure&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. npm init&lt;/p&gt;
&lt;pre id=&quot;code_1679475096058&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm init -y&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;2. 웹팩과 웹팩 cli 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679475120784&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i -D webpack webpack-cli&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;3. 바벨 설치 (JSX 읽어야 하니까)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679475166695&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react&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;4. 최상위 경로에 webpack.config.js 파일 만들기&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-filename=&quot;Screen Shot 2023-03-22 at 5.53.28 PM.png&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFkkSr/btr5oNPlNYt/4i8lbcJ47lH5hSJR30emkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFkkSr/btr5oNPlNYt/4i8lbcJ47lH5hSJR30emkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFkkSr/btr5oNPlNYt/4i8lbcJ47lH5hSJR30emkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFkkSr%2Fbtr5oNPlNYt%2F4i8lbcJ47lH5hSJR30emkK%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;400&quot; height=&quot;178&quot; data-filename=&quot;Screen Shot 2023-03-22 at 5.53.28 PM.png&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;370&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;5. webpack.config.js 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679475260051&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const path = require(&quot;path&quot;);

module.exports = {
  entry: &quot;./src/index.js&quot;,
  output: {
    path: path.resolve(__dirname, &quot;dist&quot;),
    filename: &quot;app.bundle.js&quot;,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: &quot;babel-loader&quot;,
          options: {
            presets: [
              [&quot;@babel/preset-env&quot;],
              [&quot;@babel/preset-react&quot;, { runtime: &quot;automatic&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;6. 리액트 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679475280996&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i react react-dom&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;7. css-loader, style-loader 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679475339123&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i -D css-loader style-loader&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;8. styled-components 설치 (optional)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679475379342&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i styled-components&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;9. axios 설치 (optional)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679475405818&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i axios&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;10. html webpack plugin 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679475430884&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i -D html-webpack-plugin&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;이쯤에서 webpack.config.js 파일 설정 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679476550577&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const path = require(&quot;path&quot;);
// 플러그인은 빌드할 때 추가적인 기능을 제공하는 것이기 때문에
// 웹팩이 인식할 수 있게 위에서 require로 불러와야 함

const HtmlWebpackPlugin = require(&quot;html-webpack-plugin&quot;);
// 로더는 빌드할 때 웹팩이 알아서 인식하고 적용함

module.exports = {
  entry: &quot;./src/index.js&quot;,
  output: {
    path: path.resolve(__dirname, &quot;dist&quot;),
    filename: &quot;app.bundle.js&quot;,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: &quot;babel-loader&quot;,
          options: {
            presets: [
              [&quot;@babel/preset-env&quot;],
              [&quot;@babel/preset-react&quot;, { runtime: &quot;automatic&quot; }],
            ],
          },
        },
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: [&quot;style-loader&quot;, &quot;css-loader&quot;]
      },
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: [
          {
            loader: 'file-loader',
          }
        ]
      }
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, &quot;public&quot;, &quot;index.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;11. file-loader 설치 (png, jpeg 등 이미지 파일까지 빌드해야 하는 경우)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679475490350&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i -D file-loader&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;11-1.&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;public 폴더에 이미지 파일이 있을 때 그냥 &amp;lt;img src=&quot;경로&quot; /&amp;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;pre id=&quot;code_1679475584385&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import myImg from '../public/myimage.png'&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;lt;img src={myImg} /&amp;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;여기까지 하면 client는 완료.&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-filename=&quot;Screen Shot 2023-03-22 at 6.00.52 PM.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;1202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/leSAM/btr5pbWJ93c/IuhxFonXzMsg3KezA3WNgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/leSAM/btr5pbWJ93c/IuhxFonXzMsg3KezA3WNgK/img.png&quot; data-alt=&quot;잘 나온다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/leSAM/btr5pbWJ93c/IuhxFonXzMsg3KezA3WNgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FleSAM%2Fbtr5pbWJ93c%2FIuhxFonXzMsg3KezA3WNgK%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;400&quot; height=&quot;559&quot; data-filename=&quot;Screen Shot 2023-03-22 at 6.00.52 PM.png&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;1202&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;이제 서버.. 오 서버가 생각보다 별 게 없네요&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;1. npm init&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679475917585&quot; class=&quot;coffeescript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm init -y&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;2. nodemon, cors, dompurify, express, jsdom, morgan, uuid 설치 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(사용하지 않는 것은 제외)&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;pre id=&quot;code_1679475988474&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i '설치할 패키지 이름'&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;/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;0. 깃헙은 docs 폴더 안에 들어있는 파일들만 읽기 때문에 경로를 수정해줘야 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679476598335&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const path = require(&quot;path&quot;);
const HtmlWebpackPlugin = require(&quot;html-webpack-plugin&quot;);

module.exports = {
  entry: &quot;./src/index.js&quot;,
  output: {
  // 이부분
    path: path.resolve(__dirname, &quot;docs&quot;),
    filename: &quot;app.bundle.js&quot;,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: &quot;babel-loader&quot;,
          options: {
            presets: [
              [&quot;@babel/preset-env&quot;],
              [&quot;@babel/preset-react&quot;, { runtime: &quot;automatic&quot; }],
            ],
          },
        },
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: [&quot;style-loader&quot;, &quot;css-loader&quot;]
      },
      {
        test: /\.(png|jpe?g|gif)$/i,
        use: [
          {
            loader: 'file-loader',
          }
        ]
      }
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, &quot;public&quot;, &quot;index.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;1. 깃 레포지토리 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2023-03-22 at 6.08.59 PM.png&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;1221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y3KOQ/btr5nGJJxxx/yxWkxAnX8wUIvJsWcfeeK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y3KOQ/btr5nGJJxxx/yxWkxAnX8wUIvJsWcfeeK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y3KOQ/btr5nGJJxxx/yxWkxAnX8wUIvJsWcfeeK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy3KOQ%2Fbtr5nGJJxxx%2FyxWkxAnX8wUIvJsWcfeeK1%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;600&quot; height=&quot;718&quot; data-filename=&quot;Screen Shot 2023-03-22 at 6.08.59 PM.png&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;1221&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;2. 최상위 경로에 .gitignore 파일 생성&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. 이 페이지에서 내가 사용할 것 (react, Node 등) 입력하고 생성된 코드를 .gitignore 파일에 입력&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://www.toptal.com/developers/gitignore&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.toptal.com/developers/gitignore&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679476886628&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;gitignore.io&quot; data-og-description=&quot;Create useful .gitignore files for your project&quot; data-og-host=&quot;www.toptal.com&quot; data-og-source-url=&quot;https://www.toptal.com/developers/gitignore&quot; data-og-url=&quot;https://www.toptal.com/developers/gitignore&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/968S9/hyR15NLNFk/8PyXLL3eB2Oz1tT0feSRik/img.png?width=2400&amp;amp;height=1254&amp;amp;face=0_0_2400_1254&quot;&gt;&lt;a href=&quot;https://www.toptal.com/developers/gitignore&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.toptal.com/developers/gitignore&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/968S9/hyR15NLNFk/8PyXLL3eB2Oz1tT0feSRik/img.png?width=2400&amp;amp;height=1254&amp;amp;face=0_0_2400_1254');&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;gitignore.io&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Create useful .gitignore files for your project&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.toptal.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;예시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679476987447&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

### Node Patch ###
# Serverless Webpack directories
.webpack/

# Optional stylelint cache

# SvelteKit build / generate output
.svelte-kit

### react ###
.DS_*
**/*.backup.*
**/*.back.*

node_modules

*.sublime*

psd
thumb
sketch

# End of https://www.toptal.com/developers/gitignore/api/react,node&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;4. 프로젝트 git init&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679478899008&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git init&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;5. 원격 레포지토리 연결 (이건 깃헙에서 레포지토리 생성하면 안내 코드에 나와 있음. 그거 복붙하면 됨.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679478951975&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote add origin [레포지토리 URL]&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;7. git status로 node_modules처럼 커밋하고 싶지 않은 애들 빠져있는지 확인&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;8. add&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679479004158&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git add .&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;9. commit&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679479018786&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git commit -m &quot;커밋 메시지&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;10. push&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679479030099&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git push -u origin main&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;이렇게 하면 push까지 끝난다.&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;11. 레포지토리 settings - pages에서&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-filename=&quot;Screen Shot 2023-03-22 at 6.58.15 PM.png&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qPx7b/btr5pGbij8f/4z0DXlMXEQZlbLsVI7VOK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qPx7b/btr5pGbij8f/4z0DXlMXEQZlbLsVI7VOK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qPx7b/btr5pGbij8f/4z0DXlMXEQZlbLsVI7VOK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqPx7b%2Fbtr5pGbij8f%2F4z0DXlMXEQZlbLsVI7VOK1%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;842&quot; height=&quot;274&quot; data-filename=&quot;Screen Shot 2023-03-22 at 6.58.15 PM.png&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;274&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;위와 같이 세팅 후 save&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;12. 배포 완료&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-filename=&quot;Screen Shot 2023-03-22 at 6.58.51 PM.png&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTMa2g/btr5pz4gdrf/b1qZK3zFfxb3KbzzXFJmP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTMa2g/btr5pz4gdrf/b1qZK3zFfxb3KbzzXFJmP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTMa2g/btr5pz4gdrf/b1qZK3zFfxb3KbzzXFJmP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTMa2g%2Fbtr5pz4gdrf%2Fb1qZK3zFfxb3KbzzXFJmP1%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;649&quot; height=&quot;684&quot; data-filename=&quot;Screen Shot 2023-03-22 at 6.58.51 PM.png&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;684&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;+ node_modules 같이 push 해버렸다...!! 이런 문제 상황 발생 시&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;pre id=&quot;code_1679478698186&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ rm -rf .git&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;입력해서 git 파일 지우고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679478750609&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote remove [원격 저장소 이름. 보통 origin]&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679478767157&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git init&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679478781304&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git status&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2023-03-22 at 6.53.34 PM.png&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wA5RJ/btr5ozKtTp8/kRcCAbhh7ukQFhipIrm2F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wA5RJ/btr5ozKtTp8/kRcCAbhh7ukQFhipIrm2F1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wA5RJ/btr5ozKtTp8/kRcCAbhh7ukQFhipIrm2F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwA5RJ%2Fbtr5ozKtTp8%2FkRcCAbhh7ukQFhipIrm2F1%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;600&quot; height=&quot;240&quot; data-filename=&quot;Screen Shot 2023-03-22 at 6.53.34 PM.png&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;220&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;/p&gt;</description>
      <category>부트캠프/TIL</category>
      <author>하이고니</author>
      <guid isPermaLink="true">https://hajongon.tistory.com/228</guid>
      <comments>https://hajongon.tistory.com/228#entry228comment</comments>
      <pubDate>Wed, 22 Mar 2023 20:16:25 +0900</pubDate>
    </item>
    <item>
      <title>객체 지향 프로그래밍 - Drag &amp;amp; Drop 프로젝트 02.</title>
      <link>https://hajongon.tistory.com/226</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;본 게시글은 udemy 강좌(Typescript :기초부터 실전형 프로젝트까지 with React + NodeJS)를 수강한 내용을 바탕으로 합니다.&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;프로젝트 관리 앱.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2023-03-22 at 1.13.17 AM.png&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x23eG/btr5cC2egoa/UIwdjwKLWrZid5JBOvg2W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x23eG/btr5cC2egoa/UIwdjwKLWrZid5JBOvg2W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x23eG/btr5cC2egoa/UIwdjwKLWrZid5JBOvg2W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx23eG%2Fbtr5cC2egoa%2FUIwdjwKLWrZid5JBOvg2W0%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;400&quot; height=&quot;361&quot; data-filename=&quot;Screen Shot 2023-03-22 at 1.13.17 AM.png&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&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;&lt;br /&gt;이&amp;nbsp;앱에서는&amp;nbsp;사용자가&amp;nbsp;프로젝트를&amp;nbsp;생성할&amp;nbsp;수&amp;nbsp;있고,&amp;nbsp;상태(active&amp;nbsp;/&amp;nbsp;finished)&amp;nbsp;별&amp;nbsp;프로젝트를&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;Project는&amp;nbsp;프로젝트&amp;nbsp;개체를&amp;nbsp;나타내며&amp;nbsp;id,&amp;nbsp;제목,&amp;nbsp;설명,&amp;nbsp;인원&amp;nbsp;수&amp;nbsp;및&amp;nbsp;상태(active&amp;nbsp;/&amp;nbsp;finished)를&amp;nbsp;가진다.&amp;nbsp;&lt;br /&gt;-&amp;nbsp;ProjectItem은&amp;nbsp;프로젝트&amp;nbsp;목록의&amp;nbsp;단일&amp;nbsp;항목을&amp;nbsp;나타내며&amp;nbsp;프로젝트&amp;nbsp;제목,&amp;nbsp;인원&amp;nbsp;수&amp;nbsp;및&amp;nbsp;설명을&amp;nbsp;표시한다.&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;State&amp;nbsp;클래스는&amp;nbsp;리스너가&amp;nbsp;상태&amp;nbsp;업데이트를&amp;nbsp;구독할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;한다.&amp;nbsp;&lt;br /&gt;-&amp;nbsp;ProjectState는&amp;nbsp;State의&amp;nbsp;하위&amp;nbsp;클래스로서&amp;nbsp;프로젝트를&amp;nbsp;위한&amp;nbsp;애플리케이션&amp;nbsp;상태를&amp;nbsp;관리한다.&amp;nbsp;이는&amp;nbsp;싱글톤&amp;nbsp;인스턴스를&amp;nbsp;제공하고,&amp;nbsp;프로젝트를&amp;nbsp;추가하는&amp;nbsp;메서드를&amp;nbsp;제공한다.&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;Validatable&amp;nbsp;인터페이스와&amp;nbsp;validate&amp;nbsp;함수를&amp;nbsp;통해&amp;nbsp;사용자&amp;nbsp;입력의&amp;nbsp;유효성을&amp;nbsp;검사한다.&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;autobind&amp;nbsp;데코레이터를&amp;nbsp;정의해서&amp;nbsp;클래스&amp;nbsp;메서드를&amp;nbsp;클래스&amp;nbsp;인스턴스에&amp;nbsp;바인딩한다.&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;UI&amp;nbsp;구성&amp;nbsp;요소를&amp;nbsp;만들기&amp;nbsp;위한&amp;nbsp;기본&amp;nbsp;클래스&amp;nbsp;인&amp;nbsp;Component.&amp;nbsp;이는&amp;nbsp;templateId,&amp;nbsp;hostElementId,&amp;nbsp;insertAtStart&amp;nbsp;및&amp;nbsp;newElementId를&amp;nbsp;취하는&amp;nbsp;생성자를&amp;nbsp;정의하며&amp;nbsp;템플릿을&amp;nbsp;가져와&amp;nbsp;새&amp;nbsp;요소를&amp;nbsp;생성하고&amp;nbsp;host&amp;nbsp;element에&amp;nbsp;삽입한다.&amp;nbsp;또한&amp;nbsp;추상&amp;nbsp;메서드&amp;nbsp;renderContent를&amp;nbsp;정의하며&amp;nbsp;하위&amp;nbsp;클래스에서&amp;nbsp;이를&amp;nbsp;구현해야&amp;nbsp;한다.&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;ProjectList&amp;nbsp;클래스는&amp;nbsp;Component를&amp;nbsp;확장한&amp;nbsp;것으로,&amp;nbsp;프로젝트&amp;nbsp;목록을&amp;nbsp;표시한다.&amp;nbsp;프로젝트의&amp;nbsp;상태(active&amp;nbsp;/&amp;nbsp;finished)를&amp;nbsp;지정하는&amp;nbsp;type&amp;nbsp;인자를&amp;nbsp;취한다.&amp;nbsp;이는&amp;nbsp;프로젝트&amp;nbsp;상태가&amp;nbsp;변경되거나&amp;nbsp;프로젝트가&amp;nbsp;추가될&amp;nbsp;때&amp;nbsp;할당된&amp;nbsp;프로젝트&amp;nbsp;목록을&amp;nbsp;업데이트하기&amp;nbsp;위해&amp;nbsp;프로젝트&amp;nbsp;상태&amp;nbsp;변경&amp;nbsp;사항을&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2023-03-22 at 1.13.58 AM.png&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;721&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tYylS/btr45l005MH/yRki9CPwVFTeBxHcQ4H0tK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tYylS/btr45l005MH/yRki9CPwVFTeBxHcQ4H0tK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tYylS/btr45l005MH/yRki9CPwVFTeBxHcQ4H0tK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtYylS%2Fbtr45l005MH%2FyRki9CPwVFTeBxHcQ4H0tK%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;400&quot; height=&quot;482&quot; data-filename=&quot;Screen Shot 2023-03-22 at 1.13.58 AM.png&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;721&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;ACTIVE PROJECTS에 있는 항목을 FINISHED PROJECTS로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FINISHED PROJECTS에 있는 항목을 ACTIVE PROJECTS로&lt;br /&gt;&lt;br /&gt;드래그해서 옮길 수 있는 기능을 구현하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679415456150&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Drag &amp;amp; Drop Interfaces
interface Draggable {
  dragStartHandler(event: DragEvent): void;
  dragEndHandler(event: DragEvent): void;
}

interface DragTarget {
	// 드래그 앤 드롭 실행중. 드래그가 타겟이 유효한지 알려주기 위함.
  dragOverHandler(event: DragEvent): void;
  	// 드롭 시 벌어지는 일들.
  dropHandler(event: DragEvent): void;
  	// 드롭이 일어나지 않고 취소되거나 하는 경우?
  dragLeaveHandler(event: DragEvent): void;
}&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;code&gt;Draggable&lt;/code&gt;: 드래그 할 요소. ProjectItem 클래스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;DragTarget&lt;/code&gt;: 드래그가 도착하는 곳. ProjectList 클래스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679415735715&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ProjectItem Class
class ProjectItem extends Component&amp;lt;HTMLUListElement, HTMLLIElement&amp;gt;
  implements Draggable {
  private project: Project;

	...

  constructor(hostId: string, project: Project) {
	...
  }

  @autobind
  dragStartHandler(event: DragEvent) {
    console.log(event);
  }

  dragEndHandler(_: DragEvent) {
    console.log('DragEnd');
  }

  configure() {
    this.element.addEventListener('dragstart', this.dragStartHandler);
    this.element.addEventListener('dragend', this.dragEndHandler);
  }

	...
}&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;Draggable은 dragStartHandler와 dragEndHandler를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addEventListener의 'dragstart', 'dragend' 이벤트 발생 시 해당 메서드가 실행된다.&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-filename=&quot;Screen Shot 2023-03-22 at 1.24.54 AM.png&quot; data-origin-width=&quot;1767&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6q4Sl/btr5aFdEYY2/j9GGkUtyG4yyGsxX0NR0Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6q4Sl/btr5aFdEYY2/j9GGkUtyG4yyGsxX0NR0Jk/img.png&quot; data-alt=&quot;드래그 스타트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6q4Sl/btr5aFdEYY2/j9GGkUtyG4yyGsxX0NR0Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6q4Sl%2Fbtr5aFdEYY2%2Fj9GGkUtyG4yyGsxX0NR0Jk%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;1767&quot; height=&quot;792&quot; data-filename=&quot;Screen Shot 2023-03-22 at 1.24.54 AM.png&quot; data-origin-width=&quot;1767&quot; data-origin-height=&quot;792&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2023-03-22 at 1.24.21 AM.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;1500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjvDhe/btr5fk659DS/p0Wl9W1inuf7z2MahtxD3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjvDhe/btr5fk659DS/p0Wl9W1inuf7z2MahtxD3k/img.png&quot; data-alt=&quot;드래그 엔드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjvDhe/btr5fk659DS/p0Wl9W1inuf7z2MahtxD3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjvDhe%2Fbtr5fk659DS%2Fp0Wl9W1inuf7z2MahtxD3k%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;3024&quot; height=&quot;1500&quot; data-filename=&quot;Screen Shot 2023-03-22 at 1.24.21 AM.png&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;1500&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;pre id=&quot;code_1679416241085&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ProjectList Class
class ProjectList extends Component&amp;lt;HTMLDivElement, HTMLElement&amp;gt;
  implements DragTarget {
  assignedProjects: Project[];

  constructor(private type: 'active' | 'finished') {
	...
  }

  @autobind
  dragOverHandler(_: DragEvent) {
    const listEl = this.element.querySelector('ul')!;
    // drop 할 수 있는 곳인지 css 상으로 표현하기 위해 class 추가
    listEl.classList.add('droppable');
  }

  dropHandler(_: DragEvent) {}

  @autobind
  dragLeaveHandler(_: DragEvent) {
    const listEl = this.element.querySelector('ul')!;
    // 마우스 벗어나면 class 제거
    listEl.classList.remove('droppable');
  }

  configure() {
    this.element.addEventListener('dragover', this.dragOverHandler);
    this.element.addEventListener('dragleave', this.dragLeaveHandler);
    this.element.addEventListener('drop', this.dropHandler);

    projectState.addListener((projects: Project[]) =&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;pre id=&quot;code_1679416504121&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  // ProjectItem
  
  @autobind
  dragStartHandler(event: DragEvent) {
  // 모든 드래그가 데이터를 전송할 수 있는 건 아님. 그래서 null 일 수도 있다.
  //					데이터 포맷, 전송할 데이터
    event.dataTransfer!.setData('text/plain', this.project.id);
    event.dataTransfer!.effectAllowed = 'move';
  }&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;옮기고 싶은 데이터 전체를 전송하는 것이 아니다. 나중에 id를 통해서 나머지 정보를 다 불러올 수 있기 때문에 최소한의 데이터만 전송한다.&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;pre id=&quot;code_1679416723616&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ProjectList

  @autobind
  dragOverHandler(event: DragEvent) {
    if (event.dataTransfer &amp;amp;&amp;amp; event.dataTransfer.types[0] === 'text/plain') {
      event.preventDefault();
      const listEl = this.element.querySelector('ul')!;
      listEl.classList.add('droppable');
    }
  }

  @autobind
  dropHandler(event: DragEvent) {
  	// 데이터 추출
    const prjId = event.dataTransfer!.getData('text/plain');
    projectState.moveProject(
      prjId,
      this.type === 'active' ? ProjectStatus.Active : ProjectStatus.Finished
    );
  }

  @autobind
  dragLeaveHandler(_: DragEvent) {
    const listEl = this.element.querySelector('ul')!;
    listEl.classList.remove('droppable');
  }&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;pre id=&quot;code_1679416784294&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; // ProjectState
 
 moveProject(projectId: string, newStatus: ProjectStatus) {
    const project = this.projects.find(prj =&amp;gt; prj.id === projectId);
    // 드롭할 곳의 project의 상태가 이전과 달라야 상태를 변경시켜준다.
    if (project &amp;amp;&amp;amp; project.status !== newStatus) {
      project.status = newStatus;
      this.updateListeners();
    }
  }&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-filename=&quot;Screen Recording 2023-03-22 at 1.42.30 AM.mov.gif&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E7iU5/btr5h9xdD5Z/2PLNEqFOSIMG5M5U17JyYk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E7iU5/btr5h9xdD5Z/2PLNEqFOSIMG5M5U17JyYk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E7iU5/btr5h9xdD5Z/2PLNEqFOSIMG5M5U17JyYk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/E7iU5/btr5h9xdD5Z/2PLNEqFOSIMG5M5U17JyYk/img.gif&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;400&quot; height=&quot;637&quot; data-filename=&quot;Screen Recording 2023-03-22 at 1.42.30 AM.mov.gif&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;500&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;/p&gt;</description>
      <category>프론트엔드 개발/Typescript</category>
      <author>하이고니</author>
      <guid isPermaLink="true">https://hajongon.tistory.com/226</guid>
      <comments>https://hajongon.tistory.com/226#entry226comment</comments>
      <pubDate>Wed, 22 Mar 2023 01:45:42 +0900</pubDate>
    </item>
    <item>
      <title>객체 지향 프로그래밍 - Drag &amp;amp; Drop 프로젝트 01.</title>
      <link>https://hajongon.tistory.com/225</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;Typescript: &lt;span&gt;기초부터&lt;/span&gt; &lt;span&gt;실전형&lt;/span&gt; &lt;span&gt;프로젝트까지&lt;/span&gt; with React + NodeJS &amp;lt;Section 9 121 ~ 128&amp;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;오버뷰라도 살짝 한 다음에 코드 작성으로 들어갔으면 이해하기가 훨씬 쉽지 않았을까 싶다. 솔직히 강의의 절반도 이해 못한 것 같다. 모르는 건 다 제쳐두고 이해한 것 위주로 작성할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679373991005&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&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;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
    &amp;lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;ie=edge&quot; /&amp;gt;
    &amp;lt;title&amp;gt;ProjectManager&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;app.css&quot; /&amp;gt;
    &amp;lt;script src=&quot;dist/app.js&quot; defer&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;template id=&quot;project-input&quot;&amp;gt;
      &amp;lt;form&amp;gt;
        &amp;lt;div class=&quot;form-control&quot;&amp;gt;
          &amp;lt;label for=&quot;title&quot;&amp;gt;Title&amp;lt;/label&amp;gt;
          &amp;lt;input type=&quot;text&quot; id=&quot;title&quot; /&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;form-control&quot;&amp;gt;
          &amp;lt;label for=&quot;description&quot;&amp;gt;Description&amp;lt;/label&amp;gt;
          &amp;lt;textarea id=&quot;description&quot; rows=&quot;3&quot;&amp;gt;&amp;lt;/textarea&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;form-control&quot;&amp;gt;
          &amp;lt;label for=&quot;people&quot;&amp;gt;People&amp;lt;/label&amp;gt;
          &amp;lt;input type=&quot;number&quot; id=&quot;people&quot; step=&quot;1&quot; min=&quot;0&quot; max=&quot;10&quot; /&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;ADD PROJECT&amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/template&amp;gt;
    &amp;lt;template id=&quot;single-project&quot;&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;/template&amp;gt;
    &amp;lt;template id=&quot;project-list&quot;&amp;gt;
      &amp;lt;section class=&quot;projects&quot;&amp;gt;
        &amp;lt;header&amp;gt;
          &amp;lt;h2&amp;gt;&amp;lt;/h2&amp;gt;
        &amp;lt;/header&amp;gt;
        &amp;lt;ul&amp;gt;&amp;lt;/ul&amp;gt;
      &amp;lt;/section&amp;gt;
    &amp;lt;/template&amp;gt;
    &amp;lt;div id=&quot;app&quot;&amp;gt;&amp;lt;/div&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 강의 예제 코드를 보고, 왜 HTML이랑 CSS가 다 작성되어 있는데 화면에 아무것도 안 나오지? 내가 npm install을 잘못했나? 이런 생각을 했다.&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;&amp;lt;template&amp;gt; 태그&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;lt;template&amp;gt;&amp;nbsp;태그는&amp;nbsp;추가되거나&amp;nbsp;복사될&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;HTML&amp;nbsp;요소들을&amp;nbsp;정의할&amp;nbsp;때&amp;nbsp;사용한다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;lt;template&amp;gt;&amp;nbsp;요소&amp;nbsp;내의&amp;nbsp;콘텐츠는&amp;nbsp;페이지가&amp;nbsp;로드될&amp;nbsp;때&amp;nbsp;바로&amp;nbsp;렌더링되지는&amp;nbsp;않기&amp;nbsp;때문에&amp;nbsp;사용자에게&amp;nbsp;보이지&amp;nbsp;않는다.&amp;nbsp;&lt;br /&gt;하지만&amp;nbsp;나중에&amp;nbsp;자바스크립트를&amp;nbsp;사용하여,&amp;nbsp;해당&amp;nbsp;콘텐츠를&amp;nbsp;복제한&amp;nbsp;후&amp;nbsp;보이도록&amp;nbsp;렌더링할&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;lt;template&amp;gt;&amp;nbsp;요소는&amp;nbsp;특정&amp;nbsp;HTML&amp;nbsp;요소들을&amp;nbsp;원하지&amp;nbsp;않을&amp;nbsp;때까지&amp;nbsp;계속해서&amp;nbsp;다시&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;해준다.&amp;nbsp;&lt;br /&gt;만약&amp;nbsp;&amp;lt;template&amp;gt;&amp;nbsp;요소를&amp;nbsp;사용하지&amp;nbsp;않고&amp;nbsp;이러한&amp;nbsp;작업을&amp;nbsp;수행하려면,&amp;nbsp;&lt;br /&gt;자바스크립트를&amp;nbsp;사용하여&amp;nbsp;브라우저가&amp;nbsp;해당&amp;nbsp;HTML&amp;nbsp;요소들을&amp;nbsp;렌더링하지&amp;nbsp;않도록&amp;nbsp;HTML&amp;nbsp;코드를&amp;nbsp;작성해야&amp;nbsp;한다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;ProjectInput 클래스&lt;/h2&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;pre id=&quot;code_1679374463896&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ProjectInput {
  templateElement: HTMLTemplateElement;
  hostElement: HTMLDivElement;
  element: HTMLFormElement;

  constructor() {
    this.templateElement = document.getElementById(
      'project-input'
    )! as HTMLTemplateElement;
    this.hostElement = document.getElementById('app')! as HTMLDivElement;

    const importedNode = document.importNode(
      this.templateElement.content,
      true
    );
    this.element = importedNode.firstElementChild as HTMLFormElement;
    this.attach();
  }

  private attach() {
    this.hostElement.insertAdjacentElement('afterbegin', this.element);
  }
}

const prjInput = new ProjectInput();&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;code&gt;&amp;lt;div id=&quot;app&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; 내부다. (&lt;code&gt;hostElement&lt;/code&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿 요소의 content를 importedNode에 할당해서(깊은 복사) 그 첫 번째 자식(form)을 클래스의 element로 지정한다.&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;code&gt;attach()&lt;/code&gt; 메서드가 호출되면 &lt;code&gt;hostElement&lt;/code&gt;의 첫 번째 자식으로 &lt;code&gt;this.element&lt;/code&gt;가 삽입된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 아래 캡처와 같이 input 폼이 렌더된다.&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-filename=&quot;Screen Shot 2023-03-21 at 2.22.12 PM.png&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOOZ44/btr42TCV8Tu/krj2SfusJCxpytYdCUlCmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOOZ44/btr42TCV8Tu/krj2SfusJCxpytYdCUlCmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOOZ44/btr42TCV8Tu/krj2SfusJCxpytYdCUlCmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOOZ44%2Fbtr42TCV8Tu%2Fkrj2SfusJCxpytYdCUlCmK%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;1382&quot; height=&quot;642&quot; data-filename=&quot;Screen Shot 2023-03-21 at 2.22.12 PM.png&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;.importNode()&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;노드 복사 메서드&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;&lt;code&gt;let node = document.importNode('복제 원하는 노드', boolean : 자식 노드 포함 여부);&lt;/code&gt;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;반환 : [True인경우(깊은 복사) : 자식 포함], [False인 경우 : 자식 미포함]&lt;/p&gt;
&lt;p style=&quot;color: #555555; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679375226737&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&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;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;노드의 복사 예제&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
        table {
            border-collapse: collapse;
            margin : 20px;
            text-align :center;
        }
        table td {
            border : 1px solid grey;
            padding : 6px 12px;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;template&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/template&amp;gt;
    &amp;lt;table&amp;gt;
        &amp;lt;thead&amp;gt;
            &amp;lt;tr&amp;gt;
                &amp;lt;td&amp;gt;번호&amp;lt;/td&amp;gt;
                &amp;lt;td&amp;gt;제목&amp;lt;/td&amp;gt;
                &amp;lt;td&amp;gt;내용&amp;lt;/td&amp;gt;
            &amp;lt;/tr&amp;gt;
        &amp;lt;/thead&amp;gt;
        &amp;lt;tbody&amp;gt;&amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;
    &amp;lt;script src=&quot;importNode.js&quot;&amp;gt;&amp;lt;/script&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;pre id=&quot;code_1679375248552&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let temp = document.querySelector('template');
let tbody = document.querySelector('tbody');

//데이터 SET
let db = [
    {'id':'1','title':'title1','content':'content1'},
    {'id':'2','title':'title2','content':'content2'},
    {'id':'3','title':'title3','content':'content3'}
];

//테이블 내용 생성
for (let i = 0; i &amp;lt; db.length; i++) {
    //TR (자식노드 까지 복사)
    let copyAllChild = document.importNode(temp.content,true).firstElementChild;
    console.log(copyAllChild);
    copyAllChild.children[0].innerText = db[i].id;
    copyAllChild.children[1].innerText = db[i].title;
    copyAllChild.children[2].innerText = db[i].content;

    tbody.append(copyAllChild);
}&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;HTML 렌더링된 내용(좌)과 &lt;code&gt;console.log(copyAllChild);&lt;/code&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-filename=&quot;Screen Shot 2023-03-21 at 2.09.57 PM.png&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsjtF9/btr5cS3LnXp/yrq5RYXnYgnwqUzqBwZEy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsjtF9/btr5cS3LnXp/yrq5RYXnYgnwqUzqBwZEy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsjtF9/btr5cS3LnXp/yrq5RYXnYgnwqUzqBwZEy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsjtF9%2Fbtr5cS3LnXp%2Fyrq5RYXnYgnwqUzqBwZEy1%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;800&quot; height=&quot;518&quot; data-filename=&quot;Screen Shot 2023-03-21 at 2.09.57 PM.png&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;540&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;말 그대로 필요한 노드 복사해서 쓰기 위한 메서드. template이랑 함께 쓰면 유용할 듯.&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;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;.insertAdjacentElement()&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;/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;code&gt;element.insertAdjacentElement(position, text)&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 76.8605%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 30.8018%; text-align: center;&quot;&gt;&lt;b&gt;position&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.6082%; text-align: center;&quot;&gt;&lt;b&gt;실제 위치&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 30.8018%; text-align: center;&quot;&gt;beforebegin&lt;/td&gt;
&lt;td style=&quot;width: 44.6082%; text-align: center;&quot;&gt;element 전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 30.8018%; text-align: center;&quot;&gt;afterbegin&lt;/td&gt;
&lt;td style=&quot;width: 44.6082%; text-align: center;&quot;&gt;element 안 첫 번째 child&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 30.8018%; text-align: center;&quot;&gt;beforeend&lt;/td&gt;
&lt;td style=&quot;width: 44.6082%; text-align: center;&quot;&gt;element 안 마지막 child&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 30.8018%; text-align: center;&quot;&gt;afterend&lt;/td&gt;
&lt;td style=&quot;width: 44.6082%; text-align: center;&quot;&gt;element 후&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679375864593&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- beforebegin --&amp;gt;
&amp;lt;p&amp;gt;
  &amp;lt;!-- afterbegin --&amp;gt;
  foo
  &amp;lt;!-- beforeend --&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;!-- afterend --&amp;gt;&lt;/code&gt;&lt;/pre&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;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Input Form 에 입력한 데이터 처리하기&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679376630532&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// autobind decorator
function autobind(_: any, _2: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  const adjDescriptor: PropertyDescriptor = {
    configurable: true,
    get() {
      const boundFn = originalMethod.bind(this);
      return boundFn;
    }
  };
  return adjDescriptor;
}

// ProjectInput Class
class ProjectInput {
  templateElement: HTMLTemplateElement;
  hostElement: HTMLDivElement;
  element: HTMLFormElement;
  titleInputElement: HTMLInputElement;
  descriptionInputElement: HTMLInputElement;
  peopleInputElement: HTMLInputElement;

  constructor() {
    this.templateElement = document.getElementById(
      'project-input'
    )! as HTMLTemplateElement;
    this.hostElement = document.getElementById('app')! as HTMLDivElement;

    const importedNode = document.importNode(
      this.templateElement.content,
      true
    );
    this.element = importedNode.firstElementChild as HTMLFormElement;
    this.element.id = 'user-input';

    this.titleInputElement = this.element.querySelector(
      '#title'
    ) as HTMLInputElement;
    this.descriptionInputElement = this.element.querySelector(
      '#description'
    ) as HTMLInputElement;
    this.peopleInputElement = this.element.querySelector(
      '#people'
    ) as HTMLInputElement;

    this.configure();
    this.attach();
  }

  private gatherUserInput(): [string, string, number] | void {
    const enteredTitle = this.titleInputElement.value;
    const enteredDescription = this.descriptionInputElement.value;
    const enteredPeople = this.peopleInputElement.value;

    // 하나라도 입력 안 됐으면 alert 에러 메시지
    if (
      enteredTitle.trim().length === 0 ||
      enteredDescription.trim().length === 0 ||
      enteredPeople.trim().length === 0
    ) {
      alert('Invalid input, please try again!');
      return;
    } else {
      return [enteredTitle, enteredDescription, +enteredPeople];
    }
  }

  private clearInputs() {
    this.titleInputElement.value = '';
    this.descriptionInputElement.value = '';
    this.peopleInputElement.value = '';
  }

  @autobind
  private submitHandler(event: Event) {
    event.preventDefault();
    const userInput = this.gatherUserInput();
    if (Array.isArray(userInput)) {
      const [title, desc, people] = userInput;
      console.log(title, desc, people);
      this.clearInputs();
    }
  }

	// (1)
  private configure() {
    this.element.addEventListener('submit', this.submitHandler);
  }

  private attach() {
    this.hostElement.insertAdjacentElement('afterbegin', this.element);
  }
}

const prjInput = new ProjectInput();&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. input form 에 정보 입력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. submit&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 그 데이터 받아와서 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. input 칸 비우기&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-filename=&quot;Screen Shot 2023-03-21 at 2.53.19 PM.png&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ESyOb/btr5d4bCTqb/uckYh2ttaKR1ew2AxqwWck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ESyOb/btr5d4bCTqb/uckYh2ttaKR1ew2AxqwWck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ESyOb/btr5d4bCTqb/uckYh2ttaKR1ew2AxqwWck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FESyOb%2Fbtr5d4bCTqb%2FuckYh2ttaKR1ew2AxqwWck%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;984&quot; height=&quot;335&quot; data-filename=&quot;Screen Shot 2023-03-21 at 2.53.19 PM.png&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;335&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;(1) &lt;code&gt;configure()&lt;/code&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;code&gt;configure()&lt;/code&gt;&amp;nbsp;메서드를 따로 만드는 이유는 코드의 가독성과 유지 보수성을 높이기 위함이다.&lt;br /&gt;&lt;br /&gt;만약&amp;nbsp;이벤트&amp;nbsp;리스너를&amp;nbsp;constructor()에서&amp;nbsp;바로&amp;nbsp;설정한다면,&amp;nbsp;코드의&amp;nbsp;복잡도가&amp;nbsp;높아져&amp;nbsp;가독성이&amp;nbsp;떨어지고,&amp;nbsp;추후&amp;nbsp;이벤트&amp;nbsp;리스너의&amp;nbsp;수정&amp;nbsp;및&amp;nbsp;삭제가&amp;nbsp;어려울&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;따라서,&amp;nbsp;configure()&amp;nbsp;메서드를&amp;nbsp;만들어서&amp;nbsp;이벤트&amp;nbsp;리스너를&amp;nbsp;설정하면,&amp;nbsp;코드의&amp;nbsp;역할을&amp;nbsp;명확하게&amp;nbsp;구분할&amp;nbsp;수&amp;nbsp;있고,&amp;nbsp;추후&amp;nbsp;코드&amp;nbsp;수정&amp;nbsp;및&amp;nbsp;유지보수가&amp;nbsp;쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;또한, &lt;code&gt;this.submitHandler&lt;/code&gt;를 configure()에서 등록하는 이유는 이벤트 핸들러 함수 내부에서 this를 사용할 때, this가 해당 클래스의 인스턴스를 가리키도록 하기 위해서다. 만약 이벤트 핸들러 함수를 외부에서 등록하지 않고, 바로 생성자 내에서 등록하게 되면, 이벤트 핸들러 함수 내부에서 this를 사용할 때, this가 이벤트 객체를 가리키게 되어, 해당 클래스의 인스턴스의 프로퍼티나 메서드에 접근할 수 없게 된다. 이 문제를 해결하기 위해서 configure() 메서드 내부에서 이벤트 핸들러 함수를 등록하고, 해당 함수를 데코레이터 @autobind를 사용하여 바인딩하면, this가 해당 클래스의 인스턴스를 가리키게 된다.&lt;br /&gt;&lt;br /&gt;Q:&amp;nbsp;constructor&amp;nbsp;안에서&amp;nbsp;이벤트&amp;nbsp;핸들러&amp;nbsp;함수를&amp;nbsp;등록하면&amp;nbsp;@autobind로&amp;nbsp;바인딩이&amp;nbsp;안&amp;nbsp;되는가?&lt;br /&gt;&lt;br /&gt;@autobind&amp;nbsp;데코레이터를&amp;nbsp;사용하여&amp;nbsp;메서드를&amp;nbsp;자동으로&amp;nbsp;바인딩할&amp;nbsp;때,&amp;nbsp;이&amp;nbsp;데코레이터는&amp;nbsp;해당&amp;nbsp;메서드를&amp;nbsp;클래스의&amp;nbsp;프로토타입에&amp;nbsp;등록된&amp;nbsp;메서드로&amp;nbsp;바인딩한다.&amp;nbsp;이때&amp;nbsp;constructor에서&amp;nbsp;이벤트&amp;nbsp;핸들러&amp;nbsp;함수를&amp;nbsp;등록하면,&amp;nbsp;해당&amp;nbsp;메서드는&amp;nbsp;클래스&amp;nbsp;인스턴스의&amp;nbsp;메서드가&amp;nbsp;아니라,&amp;nbsp;constructor에서&amp;nbsp;선언된&amp;nbsp;지역&amp;nbsp;함수로&amp;nbsp;등록된다.&lt;br /&gt;&lt;br /&gt;따라서,&amp;nbsp;이벤트&amp;nbsp;핸들러&amp;nbsp;함수를&amp;nbsp;등록할&amp;nbsp;때는&amp;nbsp;configure()&amp;nbsp;메서드와&amp;nbsp;같이&amp;nbsp;클래스&amp;nbsp;인스턴스에&amp;nbsp;속하는&amp;nbsp;메서드로&amp;nbsp;등록해야&amp;nbsp;한다.&amp;nbsp;그리고&amp;nbsp;configure()&amp;nbsp;메서드에서&amp;nbsp;등록한&amp;nbsp;메서드는&amp;nbsp;@autobind&amp;nbsp;데코레이터를&amp;nbsp;사용하여&amp;nbsp;자동으로&amp;nbsp;바인딩된다.&amp;nbsp;이렇게&amp;nbsp;하면,&amp;nbsp;해당&amp;nbsp;메서드에서&amp;nbsp;this를&amp;nbsp;사용할&amp;nbsp;때,&amp;nbsp;클래스&amp;nbsp;인스턴스를&amp;nbsp;가리키게&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;h2 data-ke-size=&quot;size26&quot;&gt;Validation&lt;/h2&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;pre id=&quot;code_1679378168402&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Validation

interface Validatable {
  value: string | number;
  required?: boolean;
  minLength?: number;
  maxLength?: number;
  min?: number;
  max?: number;
}

// 객체를 받는다. 검증 조건과 input이 포함되어 있다.
function validate(validatableInput: Validatable) {
  let isValid = true;
  if (validatableInput.required) {
    isValid = isValid &amp;amp;&amp;amp; validatableInput.value.toString().trim().length !== 0;
  }
  if (
    validatableInput.minLength != null &amp;amp;&amp;amp;
    typeof validatableInput.value === 'string'
  ) {
    isValid =
      isValid &amp;amp;&amp;amp; validatableInput.value.length &amp;gt;= validatableInput.minLength;
  }
  if (
    validatableInput.maxLength != null &amp;amp;&amp;amp;
    typeof validatableInput.value === 'string'
  ) {
    isValid =
      isValid &amp;amp;&amp;amp; validatableInput.value.length &amp;lt;= validatableInput.maxLength;
  }
  if (
    validatableInput.min != null &amp;amp;&amp;amp;
    typeof validatableInput.value === 'number'
  ) {
    isValid = isValid &amp;amp;&amp;amp; validatableInput.value &amp;gt;= validatableInput.min;
  }
  if (
    validatableInput.max != null &amp;amp;&amp;amp;
    typeof validatableInput.value === 'number'
  ) {
    isValid = isValid &amp;amp;&amp;amp; validatableInput.value &amp;lt;= validatableInput.max;
  }
  return isValid;
}

// autobind decorator
function autobind(_: any, _2: string, descriptor: PropertyDescriptor) {
	...
}

// ProjectInput Class
class ProjectInput {
	...

  constructor() {
    this.templateElement = document.getElementById(
      'project-input'
    )! as HTMLTemplateElement;
    this.hostElement = document.getElementById('app')! as HTMLDivElement;

    const importedNode = document.importNode(
      this.templateElement.content,
      true
    );
    this.element = importedNode.firstElementChild as HTMLFormElement;
    this.element.id = 'user-input';

    this.titleInputElement = this.element.querySelector(
      '#title'
    ) as HTMLInputElement;
    this.descriptionInputElement = this.element.querySelector(
      '#description'
    ) as HTMLInputElement;
    this.peopleInputElement = this.element.querySelector(
      '#people'
    ) as HTMLInputElement;

    this.configure();
    this.attach();
  }

  private gatherUserInput(): [string, string, number] | void {
    const enteredTitle = this.titleInputElement.value;
    const enteredDescription = this.descriptionInputElement.value;
    const enteredPeople = this.peopleInputElement.value;

    const titleValidatable: Validatable = {
      value: enteredTitle,
      required: true
    };
    const descriptionValidatable: Validatable = {
      value: enteredDescription,
      required: true,
      minLength: 5
    };
    const peopleValidatable: Validatable = {
      value: +enteredPeople,
      required: true,
      min: 1,
      max: 5
    };

    if (
      !validate(titleValidatable) ||
      !validate(descriptionValidatable) ||
      !validate(peopleValidatable)
    ) {
      alert('Invalid input, please try again!');
      return;
    } else {
      return [enteredTitle, enteredDescription, +enteredPeople];
    }
  }

	...
  }

  @autobind
  private submitHandler(event: Event) {
    event.preventDefault();
    const userInput = this.gatherUserInput();
    if (Array.isArray(userInput)) {
      const [title, desc, people] = userInput;
      console.log(title, desc, people);
      this.clearInputs();
    }
  }

  private configure() {
    this.element.addEventListener('submit', this.submitHandler);
  }

  private attach() {
    this.hostElement.insertAdjacentElement('afterbegin', this.element);
  }
}

const prjInput = new ProjectInput();&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;h2 data-ke-size=&quot;size26&quot;&gt;Project List 클래스&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1679378509688&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Validation
interface Validatable {
	...
}

function validate(validatableInput: Validatable) {
	...
}

// autobind decorator
function autobind(_: any, _2: string, descriptor: PropertyDescriptor) {
	...
}

// ProjectList Class
class ProjectList {
  templateElement: HTMLTemplateElement;
  hostElement: HTMLDivElement;
  element: HTMLElement;

	// 리스트의 type을 받아서
  constructor(private type: 'active' | 'finished') {
    this.templateElement = document.getElementById(
      'project-list'
    )! as HTMLTemplateElement;
    this.hostElement = document.getElementById('app')! as HTMLDivElement;

    const importedNode = document.importNode(
      this.templateElement.content,
      true
    );
    this.element = importedNode.firstElementChild as HTMLElement;
    // id로 지정
    this.element.id = `${this.type}-projects`;
    this.attach();
    this.renderContent();
  }

  private renderContent() {
    const listId = `${this.type}-projects-list`;
    this.element.querySelector('ul')!.id = listId;
    // 받은 타입을 대문자로 변환해서 렌더링
    this.element.querySelector('h2')!.textContent =
      this.type.toUpperCase() + ' PROJECTS';
    
  }

  private attach() {
  	// 인풋 폼보다 아래로 가도록 마지막에 추가(beforeend)
    this.hostElement.insertAdjacentElement('beforeend', this.element);
  }
}

// ProjectInput Class
class ProjectInput {
	...
}

const prjInput = new ProjectInput();
// 액티브, 피니쉬드 순으로 추가
const activePrjList = new ProjectList('active');
const finishedPrjList = new ProjectList('finished');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2023-03-21 at 3.00.09 PM.png&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cckprd/btr42TpCc7r/TXOJtET21daTt1NX1EKCXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cckprd/btr42TpCc7r/TXOJtET21daTt1NX1EKCXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cckprd/btr42TpCc7r/TXOJtET21daTt1NX1EKCXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcckprd%2Fbtr42TpCc7r%2FTXOJtET21daTt1NX1EKCXk%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;598&quot; height=&quot;542&quot; data-filename=&quot;Screen Shot 2023-03-21 at 3.00.09 PM.png&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;542&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;h2 data-ke-size=&quot;size26&quot;&gt;Input 데이터 -&amp;gt; 프로젝트 목록 전달 (+ 상태 관리)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679379319069&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Project State Management

class ProjectState {
	// 함수 참조 배열
  private listeners: any[] = [];
  
  private projects: any[] = [];
  private static instance: ProjectState;

  private constructor() {}

  static getInstance() {
    if (this.instance) {
      return this.instance;
    }
    this.instance = new ProjectState();
    return this.instance;
  }

  addListener(listenerFn: Function) {
    this.listeners.push(listenerFn);
  }

	2. 새 프로젝트 생성
  addProject(title: string, description: string, numOfPeople: number) {
    const newProject = {
      id: Math.random().toString(),
      title: title,
      description: description,
      people: numOfPeople
    };
    this.projects.push(newProject);
    for (const listenerFn of this.listeners) {
      listenerFn(this.projects.slice());
    }
  }
}

// submitHandler에서 addProject를 호출하기 위해
// 전역에서 사용 가능한 인스턴스 생성
const projectState = ProjectState.getInstance();

// Validation
...

// autobind decorator
...

// ProjectList Class
class ProjectList {
	...
    
  assignedProjects: any[];

  constructor(private type: 'active' | 'finished') {
	...

    projectState.addListener((projects: any[]) =&amp;gt; {
      this.assignedProjects = projects;
      this.renderProjects();
    });

    this.attach();
    this.renderContent();
  }

  private renderProjects() {
    const listEl = document.getElementById(`${this.type}-projects-list`)! as HTMLUListElement;
    for (const prjItem of this.assignedProjects) {
      const listItem = document.createElement('li');
      listItem.textContent = prjItem.title;
      listEl.appendChild(listItem)
    }
  }

  private renderContent() {
    const listId = `${this.type}-projects-list`;
    this.element.querySelector('ul')!.id = listId;
    this.element.querySelector('h2')!.textContent =
      this.type.toUpperCase() + ' PROJECTS';
  }

  private attach() {
    this.hostElement.insertAdjacentElement('beforeend', this.element);
  }
}

// ProjectInput Class
class ProjectInput {
	...

  constructor() {
	...

    this.configure();
    this.attach();
  }
  
	...
    
  @autobind
  private submitHandler(event: Event) {
    event.preventDefault();
    const userInput = this.gatherUserInput();
    if (Array.isArray(userInput)) {
      const [title, desc, people] = userInput;
      // 1. 여기서 호출
      projectState.addProject(title, desc, people);
      this.clearInputs();
    }
  }

  private configure() {
    this.element.addEventListener('submit', this.submitHandler);
  }

  private attach() {
    this.hostElement.insertAdjacentElement('afterbegin', this.element);
  }
}

const prjInput = new ProjectInput();
const activePrjList = new ProjectList('active');
const finishedPrjList = new ProjectList('finished');&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;listeners ?&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;code&gt;ProjectState&lt;/code&gt; 클래스에서 &lt;code&gt;listeners&lt;/code&gt; 배열은 새로운 프로젝트가 추가되면 해당 배열 내의 모든 함수를 실행하는 데 사용된다. 이러한 함수는 addListener 메소드를 사용하여 listeners 배열에 추가된다. ProjectList 클래스는 생성자에서 해당 배열에 함수를 추가한다. 따라서 ProjectList 클래스는 새로운 프로젝트가 추가될 때마다 해당 함수를 실행한다.&lt;br /&gt;&lt;br /&gt;이렇게 하면 상태 변경 시 자동으로 업데이트되는 UI를 만들 수 있다.&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;code&gt;addProject&lt;/code&gt; 메소드는 새로운 프로젝트를 projects 배열에 추가 한 다음, listeners 배열 내에 등록 된 모든 함수를 호출하여 새로운 상태를 전달한다. ProjectList 클래스는 addListener 메소드를 사용하여 새로운 상태를 수신하고 해당 상태에 대한 UI 업데이트를 수행한다.&lt;br /&gt;&lt;br /&gt;이러한 방식으로 상태 관리를 수행하면, 상태 변경시마다 UI를 업데이트하는 작업을 수동으로 수행하지 않아도 되므로 코드 유지 보수성이 향상된다. 또한 애플리케이션 전반에 걸쳐 일관된 상태를 유지할 수 있다.&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;h2 data-ke-size=&quot;size26&quot;&gt;중간 정리&lt;/h2&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;1. &lt;code&gt;ProjectState&lt;/code&gt; 클래스는 프로젝트 상태를 관리한다. &lt;code&gt;addListener()&lt;/code&gt; 메서드를 사용하여 프로젝트 상태 변경에 대한 리스너 함수를 등록하고, &lt;code&gt;addProject()&lt;/code&gt; 메서드를 사용하여 새 프로젝트를 추가한다.&lt;br /&gt;&lt;br /&gt;2. &lt;code&gt;validate()&lt;/code&gt; 함수는 &lt;code&gt;Validatable&lt;/code&gt; 인터페이스를 구현하는 입력 객체를 받아 유효성 검사를 수행한다. 각 속성에 대한 필수 여부, 최소/최대 길이 또는 값 등을 확인하고, 유효한 경우 true를 반환한다.&lt;br /&gt;&lt;br /&gt;3. &lt;code&gt;autobind&lt;/code&gt; 데코레이터는 메서드를 this로 바인딩한다.&lt;br /&gt;&lt;br /&gt;4. &lt;code&gt;ProjectList&lt;/code&gt; 클래스는 프로젝트 목록을 렌더링한다. 생성자에서는 type 매개변수를 사용하여 목록이 'active'인지 'finished'인지 지정한다. &lt;code&gt;renderContent()&lt;/code&gt; 메서드는 제목을 설정하고, &lt;code&gt;renderProjects()&lt;/code&gt; 메서드는 할당된 프로젝트 목록을 렌더링한다.&lt;br /&gt;&lt;br /&gt;5. &lt;code&gt;ProjectInput&lt;/code&gt; 클래스는 새 프로젝트를 추가하는 입력 양식을 렌더링한다. &lt;code&gt;gatherUserInput()&lt;/code&gt; 메서드에서는 입력 필드에서 사용자 입력을 수집하고, &lt;code&gt;configure()&lt;/code&gt; 메서드에서는 입력 필드에서 이벤트 리스너를 등록한다. &lt;code&gt;attach()&lt;/code&gt; 메서드는 입력 양식을 문서에 추가한다.&lt;br /&gt;&lt;br /&gt;마지막으로 &lt;code&gt;new&amp;nbsp;ProjectInput()&lt;/code&gt;과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;new ProjectList('active')&lt;/code&gt;, &lt;code&gt;new ProjectList('finished')&lt;/code&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;/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;&lt;b&gt;&lt;a href=&quot;https://goodmemory.tistory.com/8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://goodmemory.tistory.com/8&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679380670903&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;[Javascript] 노드복사 importNode&quot; data-og-description=&quot;노드의 복사 var node = document.importNode('복제 원하는 노드', boolean : 자식 노드 포함 여부); 반환 : [True인경우 : 자식 포함], [False인 경우 : 자식 미포함] 샘플 예제 &amp;darr; [HTML] 번호 제목 내용 [Javascript] let&quot; data-og-host=&quot;goodmemory.tistory.com&quot; data-og-source-url=&quot;https://goodmemory.tistory.com/8&quot; data-og-url=&quot;https://goodmemory.tistory.com/8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/HmfUK/hyR0jFGsWE/SzTVlJeG3yJTciodi5g5FK/img.jpg?width=800&amp;amp;height=351&amp;amp;face=0_0_800_351,https://scrap.kakaocdn.net/dn/noN6m/hyR0xqpVHG/DJeoEk3QKIk7cJot0gZ5X1/img.jpg?width=800&amp;amp;height=351&amp;amp;face=0_0_800_351,https://scrap.kakaocdn.net/dn/tzH08/hyRZfZfaHu/kEirKHaLmQkdMjDsMU4NuK/img.png?width=400&amp;amp;height=263&amp;amp;face=0_0_400_263&quot;&gt;&lt;a href=&quot;https://goodmemory.tistory.com/8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://goodmemory.tistory.com/8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/HmfUK/hyR0jFGsWE/SzTVlJeG3yJTciodi5g5FK/img.jpg?width=800&amp;amp;height=351&amp;amp;face=0_0_800_351,https://scrap.kakaocdn.net/dn/noN6m/hyR0xqpVHG/DJeoEk3QKIk7cJot0gZ5X1/img.jpg?width=800&amp;amp;height=351&amp;amp;face=0_0_800_351,https://scrap.kakaocdn.net/dn/tzH08/hyRZfZfaHu/kEirKHaLmQkdMjDsMU4NuK/img.png?width=400&amp;amp;height=263&amp;amp;face=0_0_400_263');&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;[Javascript] 노드복사 importNode&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;노드의 복사 var node = document.importNode('복제 원하는 노드', boolean : 자식 노드 포함 여부); 반환 : [True인경우 : 자식 포함], [False인 경우 : 자식 미포함] 샘플 예제 &amp;darr; [HTML] 번호 제목 내용 [Javascript] let&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;goodmemory.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://powerku.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://powerku.tistory.com/115&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679380680737&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;자바스크립트 | insertAdjacentElement() 엘리먼트에서 특정 위치에 노드 삽입&quot; data-og-description=&quot;insertAdjacentElement(position, text) 특정 위치에 노드를 추가합니다. position에는 4가지의 단어만 들어갈 수 있습니다 beforebegin element 앞에 afterbegin element 안에 가장 첫번째 child beforeend element 안에 가장 마&quot; data-og-host=&quot;powerku.tistory.com&quot; data-og-source-url=&quot;https://powerku.tistory.com/115&quot; data-og-url=&quot;https://powerku.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-insertAdjacentElement-%EC%97%98%EB%A6%AC%EB%A8%BC%ED%8A%B8%EC%97%90%EC%84%9C-%ED%8A%B9%EC%A0%95-%EC%9C%84%EC%B9%98%EC%97%90-%EB%85%B8%EB%93%9C-%EC%82%BD%EC%9E%85&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bnIRgS/hyRZc9gHxG/yDGRY70zuGwMGJ6rLARgOk/img.png?width=246&amp;amp;height=235&amp;amp;face=0_0_246_235,https://scrap.kakaocdn.net/dn/ukfH9/hyR0rKvsYG/9MWhlzYqI9fWHGK9bKNuuK/img.png?width=246&amp;amp;height=235&amp;amp;face=0_0_246_235,https://scrap.kakaocdn.net/dn/br21M9/hyRY6OKqpA/GE1SmLGNpkfiTKWUksIg9K/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300&quot;&gt;&lt;a href=&quot;https://powerku.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://powerku.tistory.com/115&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bnIRgS/hyRZc9gHxG/yDGRY70zuGwMGJ6rLARgOk/img.png?width=246&amp;amp;height=235&amp;amp;face=0_0_246_235,https://scrap.kakaocdn.net/dn/ukfH9/hyR0rKvsYG/9MWhlzYqI9fWHGK9bKNuuK/img.png?width=246&amp;amp;height=235&amp;amp;face=0_0_246_235,https://scrap.kakaocdn.net/dn/br21M9/hyRY6OKqpA/GE1SmLGNpkfiTKWUksIg9K/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300');&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;자바스크립트 | insertAdjacentElement() 엘리먼트에서 특정 위치에 노드 삽입&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;insertAdjacentElement(position, text) 특정 위치에 노드를 추가합니다. position에는 4가지의 단어만 들어갈 수 있습니다 beforebegin element 앞에 afterbegin element 안에 가장 첫번째 child beforeend element 안에 가장 마&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;powerku.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679380687159&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;Element.insertAdjacentElement() - Web APIs | MDN&quot; data-og-description=&quot;The insertAdjacentElement() method of the Element interface inserts a given element node at a given position relative to the element it is invoked upon.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/L9p8G/hyRZfya5c8/s4KcJfUOaC6Noe55oM3gz0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/L9p8G/hyRZfya5c8/s4KcJfUOaC6Noe55oM3gz0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;Element.insertAdjacentElement() - Web APIs | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The insertAdjacentElement() method of the Element interface inserts a given element node at a given position relative to the element it is invoked upon.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@zeroequaltwo/TypeScript-%EA%B0%95%EC%9D%98-%EC%A0%95%EB%A6%AC-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@zeroequaltwo/TypeScript-%EA%B0%95%EC%9D%98-%EC%A0%95%EB%A6%AC-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679380691461&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;TypeScript 강의 정리: 데코레이터&quot; data-og-description=&quot;1. 데코레이터란? 1) 메타 프로그래밍이란? 메타프로그래밍이란 자기 자신 혹은 다른 컴퓨터 프로그램을 데이터로 취급하며 프로그램을 작성&amp;middot;수정하는 것을 말한다.(from 위키백과) 클래스와 데&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@zeroequaltwo/TypeScript-%EA%B0%95%EC%9D%98-%EC%A0%95%EB%A6%AC-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0&quot; data-og-url=&quot;https://velog.io/@zeroequaltwo/TypeScript-강의-정리-데코레이터&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BcYFO/hyRY8lt4PI/DVKT8B5bQ3FhPUvEuvjqn0/img.png?width=463&amp;amp;height=287&amp;amp;face=0_0_463_287,https://scrap.kakaocdn.net/dn/cOS58Q/hyR0wkISQa/WCo4H6H94wGFThkfDDeRVK/img.png?width=463&amp;amp;height=287&amp;amp;face=0_0_463_287,https://scrap.kakaocdn.net/dn/FcnJm/hyR0kxQljz/XlGhmcHu7bz6qfGhEPWnY0/img.jpg?width=1440&amp;amp;height=1440&amp;amp;face=0_0_1440_1440&quot;&gt;&lt;a href=&quot;https://velog.io/@zeroequaltwo/TypeScript-%EA%B0%95%EC%9D%98-%EC%A0%95%EB%A6%AC-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@zeroequaltwo/TypeScript-%EA%B0%95%EC%9D%98-%EC%A0%95%EB%A6%AC-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/BcYFO/hyRY8lt4PI/DVKT8B5bQ3FhPUvEuvjqn0/img.png?width=463&amp;amp;height=287&amp;amp;face=0_0_463_287,https://scrap.kakaocdn.net/dn/cOS58Q/hyR0wkISQa/WCo4H6H94wGFThkfDDeRVK/img.png?width=463&amp;amp;height=287&amp;amp;face=0_0_463_287,https://scrap.kakaocdn.net/dn/FcnJm/hyR0kxQljz/XlGhmcHu7bz6qfGhEPWnY0/img.jpg?width=1440&amp;amp;height=1440&amp;amp;face=0_0_1440_1440');&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;TypeScript 강의 정리: 데코레이터&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. 데코레이터란? 1) 메타 프로그래밍이란? 메타프로그래밍이란 자기 자신 혹은 다른 컴퓨터 프로그램을 데이터로 취급하며 프로그램을 작성&amp;middot;수정하는 것을 말한다.(from 위키백과) 클래스와 데&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&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;</description>
      <category>프론트엔드 개발/Typescript</category>
      <category>addListener</category>
      <category>DoM</category>
      <category>importNode</category>
      <category>insertAdjacentElement</category>
      <category>OOP</category>
      <category>template태그</category>
      <category>객체지향프로그래밍</category>
      <category>데코레이터</category>
      <category>자바스크립트</category>
      <category>타입스크립트</category>
      <author>하이고니</author>
      <guid isPermaLink="true">https://hajongon.tistory.com/225</guid>
      <comments>https://hajongon.tistory.com/225#entry225comment</comments>
      <pubDate>Tue, 21 Mar 2023 15:41:28 +0900</pubDate>
    </item>
    <item>
      <title>사이드 프로젝트 with React 30. AWS Amplify</title>
      <link>https://hajongon.tistory.com/224</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS Amplify&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;소개&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;모바일/웹 어플리케이션 프로젝트 진행 시, 구성+빌드+배포+운영까지 모든 라이프사이클을 한 곳에서 편리하게 관리할 수 있도록 통합해둔 AWS의 풀스택 개발 통합 솔루션 서비스&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;둘러보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;- Admin UI 제공&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yoldY/btr4LE0P9Ce/Rkeo1doxC3MLfqziP4jRCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yoldY/btr4LE0P9Ce/Rkeo1doxC3MLfqziP4jRCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yoldY/btr4LE0P9Ce/Rkeo1doxC3MLfqziP4jRCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyoldY%2Fbtr4LE0P9Ce%2FRkeo1doxC3MLfqziP4jRCK%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;340&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;- AWS 서비스와의 연동이 간편하다.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;인증(Cognito)&amp;nbsp;-&amp;nbsp;로그인,&amp;nbsp;회원가입,&amp;nbsp;추가&amp;nbsp;인증&amp;nbsp;등&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;서비스&amp;nbsp;모니터링/로깅(CloudWatch)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;데이터베이스(DynamoDB,&amp;nbsp;Aurora,&amp;nbsp;GraphQL&amp;nbsp;지원)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;앱&amp;nbsp;푸시&amp;nbsp;알림(PinPoint)&lt;br /&gt;- CLI(Command Line Interface)으로 할 수 있는 것들이 매우 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; (CLI 예시. create-react-app, git push, git add ...)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;프론트엔드&amp;nbsp;초기&amp;nbsp;프로젝트&amp;nbsp;셋업&amp;nbsp;(React,&amp;nbsp;Vue,&amp;nbsp;Angular,&amp;nbsp;Next.js&amp;nbsp;등&amp;nbsp;지원)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;백엔드&amp;nbsp;프로젝트&amp;nbsp;셋업&amp;nbsp;(node.js&amp;nbsp;지원)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;DB&amp;nbsp;스키마&amp;nbsp;설정&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;RESTful&amp;nbsp;API&amp;nbsp;기본&amp;nbsp;코드셋도&amp;nbsp;제공&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;DB와의&amp;nbsp;연동도&amp;nbsp;이미&amp;nbsp;되어&amp;nbsp;있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;배포&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;커맨드&amp;nbsp;하나면&amp;nbsp;배포&amp;nbsp;끝!&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;웹&amp;nbsp;호스팅&amp;nbsp;추가&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;별도로&amp;nbsp;웹&amp;nbsp;서버&amp;nbsp;셋팅을&amp;nbsp;요구하지&amp;nbsp;않는다.&amp;nbsp;커맨드&amp;nbsp;하나면&amp;nbsp;호스팅&amp;nbsp;추가&amp;nbsp;가능.&amp;nbsp;뒷&amp;nbsp;단의&amp;nbsp;웹&amp;nbsp;서버&amp;nbsp;관리는&amp;nbsp;AWS가&amp;nbsp;알아서&amp;nbsp;해준다.&lt;br /&gt;-&amp;nbsp;서비스&amp;nbsp;배포&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- 배포 모니터링, 로깅이 잘 되어있어, 배포가 실패할 경우 왜 실패했는지 디버깅 하기 용이&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Github과&amp;nbsp;연동할&amp;nbsp;수&amp;nbsp;있어,&amp;nbsp;main&amp;nbsp;브랜치에&amp;nbsp;푸시하는&amp;nbsp;것으로&amp;nbsp;배포&amp;nbsp;프로세스&amp;nbsp;시작&amp;nbsp;가능&lt;br /&gt;-&amp;nbsp;프론트엔드와&amp;nbsp;백엔드&amp;nbsp;코드를&amp;nbsp;한&amp;nbsp;레포지터리&amp;nbsp;내에서&amp;nbsp;관리&amp;nbsp;가능&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;그럼에도&amp;nbsp;백엔드와&amp;nbsp;프론트엔드&amp;nbsp;분리&amp;nbsp;배포&amp;nbsp;가능&lt;br /&gt;-&amp;nbsp;디자인&amp;nbsp;툴&amp;nbsp;Figma와&amp;nbsp;연동하여&amp;nbsp;Figma&amp;nbsp;내&amp;nbsp;컴포넌트를&amp;nbsp;리액트&amp;nbsp;컴포넌트화&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;-&amp;nbsp;CloudFormation을&amp;nbsp;이용한&amp;nbsp;인프라&amp;nbsp;관리&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;Infrastructure&amp;nbsp;as&amp;nbsp;code&amp;nbsp;서비스의&amp;nbsp;한&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; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;br /&gt;  Infrastructure as code란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;한 서비스를 운영하기 위해 필요한 인프라 리소스(서버, 데이터베이스 등)을 코드화 해놓은 것을 의미한다. 매 배포시 마다 배포를 중개하는 서비스에서 해당 파일을 읽어들인 뒤 해당 코드대로 인프라를 구성한다. (e.g. 서버는 auto-scaling을 해줘, DB는 테이블을 이렇게 생성해줘!)&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;br /&gt;Git으로 버전 관리를 할 수 있으며, 누가 어떤 것을 수정 했는지 트래킹이 가능하다는 장점이 있다.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;AWS&amp;nbsp;resource를&amp;nbsp;관리하기&amp;nbsp;위한&amp;nbsp;template&amp;nbsp;file&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;nbsp;예시&amp;nbsp;template&amp;nbsp; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679354979520&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    
    
    {
      &quot;Description&quot;: &quot;DynamoDB resource&quot;,
      &quot;AWSTemplateFormatVersion&quot;: &quot;2010-09-09&quot;,
      &quot;Parameters&quot;: {
        &quot;partitionKeyName&quot;: {
          &quot;Type&quot;: &quot;String&quot;
        },
        &quot;partitionKeyType&quot;: {
          &quot;Type&quot;: &quot;String&quot;
        },
        &quot;env&quot;: {
          &quot;Type&quot;: &quot;String&quot;
        },
        &quot;tableName&quot;: {
          &quot;Type&quot;: &quot;String&quot;
        }
      },
      &quot;Resources&quot;: {
        &quot;DynamoDBTable&quot;: {
          &quot;Type&quot;: &quot;AWS::DynamoDB::Table&quot;,
          &quot;Properties&quot;: {
            &quot;KeySchema&quot;: [
              {
                &quot;AttributeName&quot;: &quot;guid&quot;,
                &quot;KeyType&quot;: &quot;HASH&quot;
              }
            ],
            &quot;AttributeDefinitions&quot;: [
              {
                &quot;AttributeName&quot;: &quot;guid&quot;,
                &quot;AttributeType&quot;: &quot;S&quot;
              }
            ],
            &quot;GlobalSecondaryIndexes&quot;: [],
            &quot;ProvisionedThroughput&quot;: {
              &quot;ReadCapacityUnits&quot;: 5,
              &quot;WriteCapacityUnits&quot;: 5
            },
            &quot;StreamSpecification&quot;: {
              &quot;StreamViewType&quot;: &quot;NEW_IMAGE&quot;
            },
            &quot;TableName&quot;: {
              &quot;Fn::If&quot;: [
                &quot;ShouldNotCreateEnvResources&quot;,
                {
                  &quot;Ref&quot;: &quot;tableName&quot;
                },
                {
                  &quot;Fn::Join&quot;: [
                    &quot;&quot;,
                    [
                      {
                        &quot;Ref&quot;: &quot;tableName&quot;
                      },
                      &quot;-&quot;,
                      {
                        &quot;Ref&quot;: &quot;env&quot;
                      }
                    ]
                  ]
                }
              ]
            }
          }
        }
      },
      &quot;Outputs&quot;: {
        &quot;Name&quot;: {
          &quot;Value&quot;: {
            &quot;Ref&quot;: &quot;DynamoDBTable&quot;
          }
        },
        &quot;Arn&quot;: {
          &quot;Value&quot;: {
            &quot;Fn::GetAtt&quot;: [
              &quot;DynamoDBTable&quot;,
              &quot;Arn&quot;
            ]
          }
        },
        &quot;StreamArn&quot;: {
          &quot;Value&quot;: {
            &quot;Fn::GetAtt&quot;: [
              &quot;DynamoDBTable&quot;,
              &quot;StreamArn&quot;
            ]
          }
        },
        &quot;PartitionKeyName&quot;: {
          &quot;Value&quot;: {
            &quot;Ref&quot;: &quot;partitionKeyName&quot;
          }
        },
        &quot;PartitionKeyType&quot;: {
          &quot;Value&quot;: {
            &quot;Ref&quot;: &quot;partitionKeyType&quot;
          }
        },
        &quot;Region&quot;: {
          &quot;Value&quot;: {
            &quot;Ref&quot;: &quot;AWS::Region&quot;
          }
        }
      }
    }&lt;/code&gt;&lt;/pre&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-filename=&quot;IMG_8082.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqYvQf/btr4TJAMqVA/XCXx0B1sPT7v5lpsdwiKl1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqYvQf/btr4TJAMqVA/XCXx0B1sPT7v5lpsdwiKl1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqYvQf/btr4TJAMqVA/XCXx0B1sPT7v5lpsdwiKl1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqYvQf%2Fbtr4TJAMqVA%2FXCXx0B1sPT7v5lpsdwiKl1%2Fimg.jpg&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;600&quot; height=&quot;450&quot; data-filename=&quot;IMG_8082.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8083.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAFjws/btr43X5fS2C/dSAAQZltfrgkNVDtG24fMK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAFjws/btr43X5fS2C/dSAAQZltfrgkNVDtG24fMK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAFjws/btr43X5fS2C/dSAAQZltfrgkNVDtG24fMK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAFjws%2Fbtr43X5fS2C%2FdSAAQZltfrgkNVDtG24fMK%2Fimg.jpg&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;600&quot; height=&quot;450&quot; data-filename=&quot;IMG_8083.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&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;#패스트캠퍼스 #패캠챌린지 #수강료0원챌린지 #환급챌린지 #직장인인강 #직장인자기계발&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#패캠인강후기 #패스트캠퍼스후기 #오공완 #사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact&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;본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.&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;a href=&quot;http://bit.ly/3Y34pE0&quot;&gt;http://bit.ly/3Y34pE0&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679354844153&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;패스트캠퍼스 [직장인 실무교육]&quot; data-og-description=&quot;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;http://bit.ly/3Y34pE0&quot; data-og-url=&quot;https://fastcampus.co.kr/?utm_source=naver&amp;amp;utm_medium=viral&amp;amp;utm_campaign=prm^230201^AOP&amp;amp;utm_content=blog^challenge^AOP&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cRl2UO/hyRY4C8OdC/hLxLPNECFVfsHlWz9ArLf1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;http://bit.ly/3Y34pE0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://bit.ly/3Y34pE0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cRl2UO/hyRY4C8OdC/hLxLPNECFVfsHlWz9ArLf1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;패스트캠퍼스 [직장인 실무교육]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&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;</description>
      <category>부트캠프/따로 공부</category>
      <category>사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact</category>
      <category>수강료0원챌린지</category>
      <category>오공완</category>
      <category>직장인인강</category>
      <category>직장인자기계발</category>
      <category>패스트캠퍼스</category>
      <category>패스트캠퍼스후기</category>
      <category>패캠인강후기</category>
      <category>패캠챌린지</category>
      <category>환급챌린지</category>
      <author>하이고니</author>
      <guid isPermaLink="true">https://hajongon.tistory.com/224</guid>
      <comments>https://hajongon.tistory.com/224#entry224comment</comments>
      <pubDate>Tue, 21 Mar 2023 08:39:57 +0900</pubDate>
    </item>
    <item>
      <title>webpack 빌드 시 이미지 경로(assets, 템플릿 로더)</title>
      <link>https://hajongon.tistory.com/223</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;만들어뒀던 js, css, html 기반 프로젝트를 webpack으로 번들링하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 건 잘 되는데, asset 폴더 경로를 지정해서 png나 jpg 파일을 불러오려고 하는 것에서 문제가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679298541347&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&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;!-- ... --&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    
    
		&amp;lt;!-- ... --&amp;gt;
        
      &amp;lt;img src=&quot;./jerry.png&quot;)&amp;gt; alt=&quot;&quot; id=&quot;jerry&quot; /&amp;gt;
      
		&amp;lt;!-- ... --&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;저렇게 상대 경로로 설정해두고 빌드하면 경로가 바뀌지 않아서 파일은 정상적으로 따라와도 html이 png 파일을 읽을 수가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 webpack.config.js 파일 내용이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679298990092&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const path = require(&quot;path&quot;);
const HtmlWebpackPlugin = require(&quot;html-webpack-plugin&quot;);

module.exports = {
  entry: &quot;./src/index.js&quot;,
  output: {
    path: path.resolve(__dirname, &quot;dist&quot;),
    filename: &quot;app.bundle.js&quot;,
    
    // png 파일이 저장될 경로
    assetModuleFilename: 'assets/[name][ext]',
    
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [&quot;style-loader&quot;, &quot;css-loader&quot;],
        exclude: /node_modules/,
      },
      
      
      // asset 관련
      {
        test: /\.png$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024,
          }
        },
      },
      
      
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, &quot;src&quot;, &quot;index.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;빌드가 되면 dist 폴더가 생성되고, 그 안에 assets 폴더가 만들어지며, 그 안에 png 파일이 들어갈 것이다.&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-filename=&quot;Screen Shot 2023-03-20 at 4.56.10 PM.png&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7tOC1/btr41i9Ca6B/yCHlvHUPhZl0hyNSyjvCM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7tOC1/btr41i9Ca6B/yCHlvHUPhZl0hyNSyjvCM1/img.png&quot; data-alt=&quot;빌드 후 경로&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7tOC1/btr41i9Ca6B/yCHlvHUPhZl0hyNSyjvCM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7tOC1%2Fbtr41i9Ca6B%2FyCHlvHUPhZl0hyNSyjvCM1%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;448&quot; height=&quot;258&quot; data-filename=&quot;Screen Shot 2023-03-20 at 4.56.10 PM.png&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;258&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;HTML도 그에 따라 경로가 바뀌어야 하는데, HTML은 그냥 텍스트일 뿐이라서 빌드함에 따라 변하고 자시고 하지 않는다.&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_1679298747193&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;&amp;lt;%= require('./jerry.png') %&amp;gt;&quot; alt=&quot;&quot; id=&quot;jerry&quot; /&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;저런 요상한 문법을 사용하면 된다. '템플릿 로더'라고 하는 건데 알아서 assets 파일 내를 가리키게 해준다.&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-filename=&quot;Screen Shot 2023-03-20 at 4.54.00 PM.png&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d655Om/btr41vuhM3K/ECrSAP7tVF8PhVD5pVWdU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d655Om/btr41vuhM3K/ECrSAP7tVF8PhVD5pVWdU0/img.png&quot; data-alt=&quot;빌드 후 dist 폴더 내 html 파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d655Om/btr41vuhM3K/ECrSAP7tVF8PhVD5pVWdU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd655Om%2Fbtr41vuhM3K%2FECrSAP7tVF8PhVD5pVWdU0%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;1046&quot; height=&quot;184&quot; data-filename=&quot;Screen Shot 2023-03-20 at 4.54.00 PM.png&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;빌드 후 dist 폴더 내 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;경로가 잘 바뀐 것을 확인할 수 있다.&lt;/p&gt;</description>
      <category>부트캠프/따로 공부</category>
      <category>webpackAsset</category>
      <category>webpackAssets</category>
      <category>webpackJPG</category>
      <category>webpackPNG</category>
      <category>webpack이미지</category>
      <author>하이고니</author>
      <guid isPermaLink="true">https://hajongon.tistory.com/223</guid>
      <comments>https://hajongon.tistory.com/223#entry223comment</comments>
      <pubDate>Mon, 20 Mar 2023 16:58:23 +0900</pubDate>
    </item>
    <item>
      <title>사이드 프로젝트 with React 29. 기술셋 선정하기 (2)</title>
      <link>https://hajongon.tistory.com/222</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;기술셋 선정하기 - DevOps&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시작하기에 앞서, DevOps 란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Development + Operation의 합성어&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;전달(delivery) 과정을 자동화하고 빠르게&lt;/b&gt; 하여 결국에는 비지니스의 가치를 높일 수 있도록 하기 위한 개발 환경이나 문화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 시스템의 요구사항을 잘 충족시키기 위해 필요한 것들 리스팅&lt;/h2&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;/li&gt;
&lt;li&gt;배포가 쉬운가?&lt;/li&gt;
&lt;li&gt;서버를 직접 관리해야 하는가?&lt;/li&gt;
&lt;li&gt;웹 사이트 호스팅(도메인)를 쉽게 할 수 있는가?&lt;/li&gt;
&lt;li&gt;Github과 연동되어 git push시 배포 프로세스를 시작할 수 있는가?&lt;/li&gt;
&lt;li&gt;추후 백엔드 API를 개발할 경우 통합하고 배포하는데에 용이한가?&lt;/li&gt;
&lt;li&gt;배포 모니터링 지원&lt;/li&gt;
&lt;li&gt;개발 커뮤니티 활성화&lt;/li&gt;
&lt;li&gt;무료인가?&lt;/li&gt;
&lt;li&gt;기술의 안정성 - v.0.0.1같이 너무 새로운 기술이 아닌, 안정된 버전이 있고, 유지보수 하는 단체가 안정적인가?&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;2. 후보군 조사&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS Amplify&lt;/li&gt;
&lt;li&gt;Jenkins&lt;/li&gt;
&lt;li&gt;Google Firebase&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 비교 테이블 생성&lt;br /&gt;&lt;br /&gt;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 170px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;AWS Amplify&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;Jenkins&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;Google Firebase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;배포 단계를 수립하는 것이 쉬운가?&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;X -&amp;gt; 서버 관리, 호스팅 어떻게 할지 다 정해야 함&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;배포가 쉬운가?&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;웹사이트 호스팅(도메인)을 쉽게 할 수 있는가?&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;서버를 직접 관리하지 않아도 되는가?&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;Github과 연동 및 배포 프로세스를 시작할 수 있는가?&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O -&amp;gt; Github branch만 연결하면 됨&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O -&amp;gt; 하지만 Github action을 별도로 설정해줘야 한다.&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O -&amp;gt; 하지만 Github action을 별도로 설정해줘야 한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;추후 백엔드 API를 개발할 경우, 통합하고 배포하는 데 용이한가?&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O -&amp;gt; Lambda, API Gateway 등&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;X -&amp;gt; 별도의 백엔드 배포 파이프라인이 필요&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O -&amp;gt; Cloud function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;배포 모니터링 지원&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;개발 커뮤니티 활성화&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;기술의 안정성&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;팀 내 기술 친숙도&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;상대적&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;상대적&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;상대적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;무료인가?&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;12개월 간 프리 티어&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;일정 한도 내 무료&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 결정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS가 클라우드 시장에서 선점을 가하고 있고, AWS의 서비스들(Lambda, S3, DynamoDB 등)을 간편하게 연동하여 사용할 수 있다는 것이 큰 장점. 따라서, Firebase와 Amplify 중 선택해야 한다면, &lt;b&gt;더치페이 프로젝트에서는 AWS Amplify&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;h2 data-ke-size=&quot;size26&quot;&gt;기술셋을 입힌 아키텍처는 이렇게 바뀐다!&lt;/h2&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-filename=&quot;Screen Shot 2023-03-20 at 8.29.24 AM.png&quot; data-origin-width=&quot;2020&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l6o8J/btr4TI7YjNv/FVrKlSQTgqKjlWubT1Lec1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l6o8J/btr4TI7YjNv/FVrKlSQTgqKjlWubT1Lec1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l6o8J/btr4TI7YjNv/FVrKlSQTgqKjlWubT1Lec1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl6o8J%2Fbtr4TI7YjNv%2FFVrKlSQTgqKjlWubT1Lec1%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;2020&quot; height=&quot;700&quot; data-filename=&quot;Screen Shot 2023-03-20 at 8.29.24 AM.png&quot; data-origin-width=&quot;2020&quot; data-origin-height=&quot;700&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8064.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lq5c8/btr4LGisUg4/USdYR0j15XTVvS0j23i2fK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lq5c8/btr4LGisUg4/USdYR0j15XTVvS0j23i2fK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lq5c8/btr4LGisUg4/USdYR0j15XTVvS0j23i2fK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flq5c8%2Fbtr4LGisUg4%2FUSdYR0j15XTVvS0j23i2fK%2Fimg.jpg&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;600&quot; height=&quot;450&quot; data-filename=&quot;IMG_8064.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8065.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLGirf/btr4zcoZRTJ/2vKW6k3XjKZUeLLTqu2II0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLGirf/btr4zcoZRTJ/2vKW6k3XjKZUeLLTqu2II0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLGirf/btr4zcoZRTJ/2vKW6k3XjKZUeLLTqu2II0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLGirf%2Fbtr4zcoZRTJ%2F2vKW6k3XjKZUeLLTqu2II0%2Fimg.jpg&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;600&quot; height=&quot;3024&quot; data-filename=&quot;IMG_8065.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&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;#패스트캠퍼스 #패캠챌린지 #수강료0원챌린지 #환급챌린지 #직장인인강 #직장인자기계발&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#패캠인강후기 #패스트캠퍼스후기 #오공완 #사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact&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;본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.&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;a href=&quot;http://bit.ly/3Y34pE0&quot;&gt;http://bit.ly/3Y34pE0&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679268695697&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;패스트캠퍼스 [직장인 실무교육]&quot; data-og-description=&quot;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;http://bit.ly/3Y34pE0&quot; data-og-url=&quot;https://fastcampus.co.kr/?utm_source=naver&amp;amp;utm_medium=viral&amp;amp;utm_campaign=prm^230201^AOP&amp;amp;utm_content=blog^challenge^AOP&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cb0SmI/hyRY3QDJcv/dc2hvNaDHm1VaGHj8Voxfk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/hw84z/hyRZbA7rdg/UZRtnyflfgfFoH4JjPgIkk/img.png?width=2560&amp;amp;height=900&amp;amp;face=0_0_2560_900&quot;&gt;&lt;a href=&quot;http://bit.ly/3Y34pE0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://bit.ly/3Y34pE0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cb0SmI/hyRY3QDJcv/dc2hvNaDHm1VaGHj8Voxfk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/hw84z/hyRZbA7rdg/UZRtnyflfgfFoH4JjPgIkk/img.png?width=2560&amp;amp;height=900&amp;amp;face=0_0_2560_900');&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;패스트캠퍼스 [직장인 실무교육]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&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;</description>
      <category>부트캠프/따로 공부</category>
      <category>사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact</category>
      <category>수강료0원챌린지</category>
      <category>오공완</category>
      <category>직장인인강</category>
      <category>직장인자기계발</category>
      <category>패스트캠퍼스</category>
      <category>패스트캠퍼스후기</category>
      <category>패캠인강후기</category>
      <category>패캠챌린지</category>
      <category>환급챌린지</category>
      <author>하이고니</author>
      <guid isPermaLink="true">https://hajongon.tistory.com/222</guid>
      <comments>https://hajongon.tistory.com/222#entry222comment</comments>
      <pubDate>Mon, 20 Mar 2023 08:32:31 +0900</pubDate>
    </item>
    <item>
      <title>사이드 프로젝트 with React 28. 기술셋 선정하기 -프론트엔드</title>
      <link>https://hajongon.tistory.com/221</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;3/19 (SUN)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;들은 강의: Part 3. &lt;span&gt;프로젝트&lt;/span&gt; &lt;span&gt;설계하기&lt;/span&gt; 02. &lt;span&gt;기술셋&lt;/span&gt; &lt;span&gt;선정하기&lt;/span&gt; (1) - &lt;span&gt;프론트엔드&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;기술셋 선정하기 - 프론트엔드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 시스템의 요구사항을 잘 충족시키기 위해 필요한 것들 리스팅&lt;/h2&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;/li&gt;
&lt;li&gt;UI 코드 재활용 가능&lt;/li&gt;
&lt;li&gt;상태관리가 가능한가?&lt;/li&gt;
&lt;li&gt;테스트 코드 작성 용이&lt;/li&gt;
&lt;li&gt;개발 커뮤니티 활성화&lt;/li&gt;
&lt;li&gt;기술의 안정성 - 나온지 얼마 안 된 너무 새로운 기술은 X. 안정된 버전이 있고, 유지보수 하는 단체가 안정적인가?&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;2. 후보군 조사&lt;/h2&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;React.js&lt;/li&gt;
&lt;li&gt;Angular&lt;/li&gt;
&lt;li&gt;Vanilla JS&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 비교 테이블 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Angular Vanilla&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 40%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 73.9535%;&quot;&gt;페이지 라우팅&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.84553%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 73.9535%;&quot;&gt;UI 코드 재활용 가능&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.84553%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 73.9535%;&quot;&gt;상태 관리 가능&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.84553%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 73.9535%;&quot;&gt;테스트 코드 작성 용이&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.84553%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 73.9535%;&quot;&gt;개발 커뮤니티 활성화&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.84553%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 73.9535%;&quot;&gt;기술의 안정성&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.84553%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 73.9535%;&quot;&gt;팀 내 기술 친숙도&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.60465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 8.84553%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 비교/분석 하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React / Angular / Vanilla&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 99.8824%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.506%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.2781%;&quot;&gt;React&lt;/td&gt;
&lt;td style=&quot;width: 26.3176%;&quot;&gt;Angular&lt;/td&gt;
&lt;td style=&quot;width: 132.804%;&quot;&gt;Vanilla&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.506%;&quot;&gt;페이지 라우팅&lt;/td&gt;
&lt;td style=&quot;width: 28.2781%;&quot;&gt;O&lt;br /&gt;-&amp;gt; React-router를 이용하여 가능&lt;/td&gt;
&lt;td style=&quot;width: 26.3176%;&quot;&gt;O&lt;br /&gt;-&amp;gt; 내장 기능&lt;/td&gt;
&lt;td style=&quot;width: 132.804%;&quot;&gt;X&lt;br /&gt;-&amp;gt; 직접 구현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.506%;&quot;&gt;UI 코드 재활용 가능&lt;/td&gt;
&lt;td style=&quot;width: 28.2781%;&quot;&gt;O&lt;br /&gt;-&amp;gt; 컴포넌트&lt;/td&gt;
&lt;td style=&quot;width: 26.3176%;&quot;&gt;O&lt;br /&gt;-&amp;gt; 컴포넌트&lt;/td&gt;
&lt;td style=&quot;width: 132.804%;&quot;&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.506%;&quot;&gt;상태 관리 가능&lt;/td&gt;
&lt;td style=&quot;width: 28.2781%;&quot;&gt;O&lt;br /&gt;-&amp;gt; Redux / Recoil 을 통해 가능&lt;/td&gt;
&lt;td style=&quot;width: 26.3176%;&quot;&gt;O&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;
&lt;td style=&quot;width: 132.804%;&quot;&gt;O&lt;br /&gt;-&amp;gt; Redux를 통해 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.506%;&quot;&gt;테스트 코드 작성 용이&lt;/td&gt;
&lt;td style=&quot;width: 28.2781%;&quot;&gt;O&lt;br /&gt;-&amp;gt; 테스트하고자 하는 해당 컴포넌트만 import 하면 됨&lt;/td&gt;
&lt;td style=&quot;width: 26.3176%;&quot;&gt;X&lt;br /&gt;-&amp;gt;&amp;nbsp; 컴포넌트에서 의존하는 모듈 및 자식 컴포넌트들을 일일히 명시해야 함&lt;/td&gt;
&lt;td style=&quot;width: 132.804%;&quot;&gt;X&lt;br /&gt;-&amp;gt; 리액트에 비해서..?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.506%;&quot;&gt;개발 커뮤니티 활성화&lt;/td&gt;
&lt;td style=&quot;width: 28.2781%;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 26.3176%;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 132.804%;&quot;&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.506%;&quot;&gt;기술의 안정성&lt;/td&gt;
&lt;td style=&quot;width: 28.2781%;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 26.3176%;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;width: 132.804%;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.506%;&quot;&gt;팀 내 기술 친숙도&lt;/td&gt;
&lt;td style=&quot;width: 28.2781%;&quot;&gt;상대적&lt;/td&gt;
&lt;td style=&quot;width: 26.3176%;&quot;&gt;상대적&lt;/td&gt;
&lt;td style=&quot;width: 132.804%;&quot;&gt;상대적&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 결정&lt;/h2&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;React + React-router + Recoil&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;&lt;b&gt;(이번 프로젝트에서는) 왜 Redux보다 Recoil인가?&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. Redux는 Recoil에 비해 러닝 커브가 훨씬 높다. &lt;br /&gt;2. Recoil은 React 전용 상태 관리 라이브러리.&lt;br /&gt;3. Recoil은 React 스타일의 hook을 제공함.&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;대규모 상태를 관리해야 하는 프로젝트에는 redux를 사용하는 것이 좀 더 적합하다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이런 대규모 상태를 감시, 디버깅하기 위한 안정적인 'devtool'도 가지고 있기 때문에 안정성 면에서도 redux가 더 낫다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나&lt;span&gt; &lt;/span&gt;지금&lt;span&gt; &lt;/span&gt;진행하려는&lt;span&gt; &lt;/span&gt;프로젝트는&lt;span&gt; &lt;/span&gt;대규모&lt;span&gt; &lt;/span&gt;데이터를&lt;span&gt; &lt;/span&gt;다루는&lt;span&gt; &lt;/span&gt;것도&lt;span&gt; &lt;/span&gt;아니고&lt;span&gt;, &lt;/span&gt;기한이&lt;span&gt; &lt;/span&gt;길지&lt;span&gt; &lt;/span&gt;않은&lt;span&gt; &lt;/span&gt;프로젝트이기&lt;span&gt; &lt;/span&gt;때문에&lt;span&gt; &lt;/span&gt;상대적으로&lt;span&gt; &lt;/span&gt;적은&lt;span&gt; &lt;/span&gt;코드를&lt;span&gt; &lt;/span&gt;작성하는&lt;span&gt; recoil&lt;/span&gt;이&lt;span&gt; redux&lt;/span&gt;에&lt;span&gt; &lt;/span&gt;비해&lt;span&gt; &lt;/span&gt;유리할&lt;span&gt; &lt;/span&gt;것이다&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8049.jpg&quot; data-origin-width=&quot;2911&quot; data-origin-height=&quot;2387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ9i0Z/btr4v9e4315/QKOOG4lhI1znEx0VRWapcK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ9i0Z/btr4v9e4315/QKOOG4lhI1znEx0VRWapcK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ9i0Z/btr4v9e4315/QKOOG4lhI1znEx0VRWapcK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ9i0Z%2Fbtr4v9e4315%2FQKOOG4lhI1znEx0VRWapcK%2Fimg.jpg&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;600&quot; height=&quot;492&quot; data-filename=&quot;IMG_8049.jpg&quot; data-origin-width=&quot;2911&quot; data-origin-height=&quot;2387&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8050.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kVhDI/btr4DsxMIMn/E9L8kns00xGqdCBEOVNdkk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kVhDI/btr4DsxMIMn/E9L8kns00xGqdCBEOVNdkk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kVhDI/btr4DsxMIMn/E9L8kns00xGqdCBEOVNdkk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkVhDI%2Fbtr4DsxMIMn%2FE9L8kns00xGqdCBEOVNdkk%2Fimg.jpg&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;600&quot; height=&quot;450&quot; data-filename=&quot;IMG_8050.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&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;#패스트캠퍼스 #패캠챌린지 #수강료0원챌린지 #환급챌린지 #직장인인강 #직장인자기계발&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#패캠인강후기 #패스트캠퍼스후기 #오공완 #사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact&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;본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.&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;a href=&quot;http://bit.ly/3Y34pE0&quot;&gt;http://bit.ly/3Y34pE0&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679156568822&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;패스트캠퍼스 [직장인 실무교육]&quot; data-og-description=&quot;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;http://bit.ly/3Y34pE0&quot; data-og-url=&quot;https://fastcampus.co.kr/?utm_source=naver&amp;amp;utm_medium=viral&amp;amp;utm_campaign=prm^230201^AOP&amp;amp;utm_content=blog^challenge^AOP&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pY1TK/hyRY8jlSJD/DUZNT5k7Chn3KIUj2osef0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/UjHqT/hyRXAoeF3p/GxhdqJW81TAlJpKh8zozLK/img.png?width=5120&amp;amp;height=1800&amp;amp;face=0_0_5120_1800&quot;&gt;&lt;a href=&quot;http://bit.ly/3Y34pE0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://bit.ly/3Y34pE0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pY1TK/hyRY8jlSJD/DUZNT5k7Chn3KIUj2osef0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/UjHqT/hyRXAoeF3p/GxhdqJW81TAlJpKh8zozLK/img.png?width=5120&amp;amp;height=1800&amp;amp;face=0_0_5120_1800');&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;패스트캠퍼스 [직장인 실무교육]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&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;</description>
      <category>부트캠프/따로 공부</category>
      <category>사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact</category>
      <category>수강료0원챌린지</category>
      <category>오공완</category>
      <category>직장인인강</category>
      <category>직장인자기계발</category>
      <category>패스트캠퍼스</category>
      <category>패스트캠퍼스후기</category>
      <category>패캠인강후기</category>
      <category>패캠챌린지</category>
      <category>환급챌린지</category>
      <author>하이고니</author>
      <guid isPermaLink="true">https://hajongon.tistory.com/221</guid>
      <comments>https://hajongon.tistory.com/221#entry221comment</comments>
      <pubDate>Sun, 19 Mar 2023 01:41:31 +0900</pubDate>
    </item>
    <item>
      <title>사이드 프로젝트 with React 27. 기술셋 선정 기준</title>
      <link>https://hajongon.tistory.com/220</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;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;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 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;/b&gt;&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;br /&gt;무조건 최신 기술이라고, 무조건 많은 라이브러리를 쓴다고 올바른 것이 아니다!&lt;/li&gt;
&lt;li&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;&lt;b&gt;왜 중요한가?&lt;/b&gt;&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;br /&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS S3가 좋다고 들어서 사용했다가는 요금 폭탄을 맞을 수도&lt;/li&gt;
&lt;li&gt;소프트웨어의 한계점을 만들어 낼 수도&lt;/li&gt;
&lt;li&gt;  특정 기술이 기능 면에서 한정적이거나 개발자가 다룰 수 있는 범위가 한정적이라면 프로젝트에서 많은 문제를 발생시킬 수 있음!&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;반대로&lt;span&gt; &lt;/span&gt;코드&lt;span&gt; &lt;/span&gt;구현을&lt;span&gt; &lt;/span&gt;수월하게&lt;span&gt; &lt;/span&gt;해준다&lt;span&gt; &lt;/span&gt;하더라도&lt;span&gt; &lt;/span&gt;배포나&lt;span&gt; &lt;/span&gt;다른&lt;span&gt; &lt;/span&gt;기술과의&lt;span&gt; &lt;/span&gt;통합이&lt;span&gt; &lt;/span&gt;어렵다면&lt;span&gt; &lt;/span&gt;이&lt;span&gt; &lt;/span&gt;또한&lt;span&gt; &lt;/span&gt;한계점이&lt;span&gt; &lt;/span&gt;될&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있음&lt;span&gt;!&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;지금 선택한 기술이 이 소프트웨어의 근간 기술이 되고, 추후 다른 개발자들도 이 기술을 배우고 사용해야 하기 때문&lt;/li&gt;
&lt;li&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;&lt;b&gt;어떻게 선정할까?&lt;/b&gt;&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;span&gt; &lt;/span&gt;요구사항을&lt;span&gt; &lt;/span&gt;잘&lt;span&gt; &lt;/span&gt;충족시키기&lt;span&gt; &lt;/span&gt;위해&lt;span&gt; &lt;/span&gt;필요한&lt;span&gt; &lt;/span&gt;것들&lt;span&gt; &lt;/span&gt;리스팅&lt;span&gt;&lt;br /&gt;&lt;br /&gt;e.g. &lt;/span&gt;멀티&lt;span&gt; &lt;/span&gt;스레딩&lt;span&gt; &lt;/span&gt;지원&lt;span&gt;?, &lt;/span&gt;성능&lt;span&gt;, &lt;/span&gt;해당&lt;span&gt; &lt;/span&gt;기술의&lt;span&gt; learning curve, &lt;/span&gt;팀원들의&lt;span&gt; &lt;/span&gt;기술에&lt;span&gt; &lt;/span&gt;대한&lt;span&gt; &lt;/span&gt;친숙도&lt;span&gt;, &lt;/span&gt;해당&lt;span&gt; &lt;/span&gt;기술의&lt;span&gt; &lt;/span&gt;업데이트가&lt;span&gt; &lt;/span&gt;활발하고&lt;span&gt; &lt;/span&gt;커뮤니티가&lt;span&gt; &lt;/span&gt;활성화&lt;span&gt; &lt;/span&gt;되어&lt;span&gt; &lt;/span&gt;있는가&lt;span&gt;? &lt;/span&gt;등&lt;span&gt;..&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;후보군&lt;span&gt; &lt;/span&gt;조사&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt; &lt;/span&gt;기술셋&lt;span&gt; &lt;/span&gt;후보군&lt;span&gt; &lt;/span&gt;중에&lt;span&gt; &lt;/span&gt;사용해본&lt;span&gt; &lt;/span&gt;적이&lt;span&gt; &lt;/span&gt;없는&lt;span&gt; &lt;/span&gt;기술이&lt;span&gt; &lt;/span&gt;보인다면&lt;span&gt; &lt;/span&gt;해당&lt;span&gt; &lt;/span&gt;기술을&lt;span&gt; &lt;/span&gt;배제하는게&lt;span&gt; &lt;/span&gt;맞을까&lt;span&gt;? &amp;rarr;&lt;span&gt;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;❌&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;리서치를&lt;span&gt; &lt;/span&gt;하는&lt;span&gt; &lt;/span&gt;시간을&lt;span&gt; &lt;/span&gt;충분히&lt;span&gt; &lt;/span&gt;가진&lt;span&gt; &lt;/span&gt;후&lt;span&gt; &lt;/span&gt;비교해야&lt;span&gt; &lt;/span&gt;한다&lt;span&gt;. &lt;/span&gt;개발자는&lt;span&gt; &lt;/span&gt;항상&lt;span&gt; &lt;/span&gt;모르는&lt;span&gt; &lt;/span&gt;것이&lt;span&gt; &lt;/span&gt;있을&lt;span&gt; &lt;/span&gt;수밖에&lt;span&gt; &lt;/span&gt;없다&lt;span&gt;. &lt;/span&gt;모른다고&lt;span&gt; &lt;/span&gt;피하기&lt;span&gt; &lt;/span&gt;보다는&lt;span&gt; &lt;/span&gt;이해하는&lt;span&gt; &lt;/span&gt;시간을&lt;span&gt; &lt;/span&gt;가져야&lt;span&gt; &lt;/span&gt;한다&lt;span&gt;. &lt;/span&gt;공식&lt;span&gt; &lt;/span&gt;문서를&lt;span&gt; &lt;/span&gt;읽고&lt;span&gt;, &lt;/span&gt;간단하게&lt;span&gt; &lt;/span&gt;기술을&lt;span&gt; &lt;/span&gt;가지고&lt;span&gt; &lt;/span&gt;놀아보는&lt;span&gt; &lt;/span&gt;시간을&lt;span&gt; &lt;/span&gt;가져보자&lt;span&gt;.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;비교&lt;span&gt; &lt;/span&gt;테이블&lt;span&gt; &lt;/span&gt;생성&lt;span&gt; - &lt;/span&gt;각&lt;span&gt; &lt;/span&gt;후보군&lt;span&gt; &lt;/span&gt;별로&lt;span&gt; &lt;/span&gt;시스템의&lt;span&gt; &lt;/span&gt;요구사항을&lt;span&gt; &lt;/span&gt;충족&lt;span&gt; &lt;/span&gt;시키는지&lt;span&gt; &lt;/span&gt;비교하기&lt;span&gt; &lt;/span&gt;위한&lt;span&gt; &lt;/span&gt;테이블을&lt;span&gt; &lt;/span&gt;만들면&lt;span&gt; &lt;/span&gt;된다&lt;span&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;팀&lt;/span&gt; &lt;span&gt;내&lt;/span&gt; &lt;span&gt;토론&lt;/span&gt; &lt;span&gt;및&lt;/span&gt; &lt;span&gt;결정&lt;/span&gt;!&lt;/li&gt;
&lt;/ol&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7976.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WbtYe/btr4t55joVy/CvqeHGnPP9psK9IEiWnhxk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WbtYe/btr4t55joVy/CvqeHGnPP9psK9IEiWnhxk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WbtYe/btr4t55joVy/CvqeHGnPP9psK9IEiWnhxk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWbtYe%2Fbtr4t55joVy%2FCvqeHGnPP9psK9IEiWnhxk%2Fimg.jpg&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;600&quot; height=&quot;3024&quot; data-filename=&quot;IMG_7976.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7975.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0Wy1B/btr4vIO6eqQ/kv8kKg1xlowekP5ivF8fyk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0Wy1B/btr4vIO6eqQ/kv8kKg1xlowekP5ivF8fyk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0Wy1B/btr4vIO6eqQ/kv8kKg1xlowekP5ivF8fyk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0Wy1B%2Fbtr4vIO6eqQ%2Fkv8kKg1xlowekP5ivF8fyk%2Fimg.jpg&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;600&quot; height=&quot;450&quot; data-filename=&quot;IMG_7975.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&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;&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;#패스트캠퍼스 #패캠챌린지 #수강료0원챌린지 #환급챌린지 #직장인인강 #직장인자기계발&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#패캠인강후기 #패스트캠퍼스후기 #오공완 #사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact&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;본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.&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;a href=&quot;http://bit.ly/3Y34pE0&quot;&gt;http://bit.ly/3Y34pE0&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679107786238&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;패스트캠퍼스 [직장인 실무교육]&quot; data-og-description=&quot;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;http://bit.ly/3Y34pE0&quot; data-og-url=&quot;https://fastcampus.co.kr/?utm_source=naver&amp;amp;utm_medium=viral&amp;amp;utm_campaign=prm^230201^AOP&amp;amp;utm_content=blog^challenge^AOP&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/AigNM/hyRXswt1Tt/J63KHc5LaEF4qKMB0r7HbK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;http://bit.ly/3Y34pE0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://bit.ly/3Y34pE0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/AigNM/hyRXswt1Tt/J63KHc5LaEF4qKMB0r7HbK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;패스트캠퍼스 [직장인 실무교육]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&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;</description>
      <category>부트캠프/따로 공부</category>
      <category>사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact</category>
      <category>수강료0원챌린지</category>
      <category>오공완</category>
      <category>직장인인강</category>
      <category>직장인자기계발</category>
      <category>패스트캠퍼스</category>
      <category>패스트캠퍼스후기</category>
      <category>패캠인강후기</category>
      <category>패캠챌린지</category>
      <category>환급챌린지</category>
      <author>하이고니</author>
      <guid isPermaLink="true">https://hajongon.tistory.com/220</guid>
      <comments>https://hajongon.tistory.com/220#entry220comment</comments>
      <pubDate>Sat, 18 Mar 2023 11:51:00 +0900</pubDate>
    </item>
    <item>
      <title>사이드 프로젝트 with React 26. 클래스 다이어그램 실습</title>
      <link>https://hajongon.tistory.com/219</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;들은 강의: Part 3. 프로젝트 설계하기 07. [실전] 시스템 설계 (3) - Class diagram for 프론트 엔드&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Plant UML 사용&lt;/li&gt;
&lt;li&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;1. 자바 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.java.com/en/download&quot;&gt;https://www.java.com/en/download&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1679002371265&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;Download Java for Linux&quot; data-og-description=&quot;Linux RPM filesize: 76.84 MB Instructions After installing Java, you will need to enable Java in your browser.&quot; data-og-host=&quot;www.java.com&quot; data-og-source-url=&quot;https://www.java.com/en/download&quot; data-og-url=&quot;https://www.java.com/en/download/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.java.com/en/download&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.java.com/en/download&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;Download Java for Linux&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Linux RPM filesize: 76.84 MB Instructions After installing Java, you will need to enable Java in your browser.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.java.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;2. GraphViz 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://plantuml.com/ko/graphviz-dot&quot;&gt;https://plantuml.com/ko/graphviz-dot&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1679002387413&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;Test your GraphViz installation&quot; data-og-description=&quot;This page gives information about testing your GraphViz installation for PlantUML. It lists some basic checks and some actions to be taken in case of failure.&quot; data-og-host=&quot;plantuml.com&quot; data-og-source-url=&quot;https://plantuml.com/ko/graphviz-dot&quot; data-og-url=&quot;https://plantuml.com/ko/graphviz-dot&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nSRzw/hyRXwZbvuy/2t1WuHvYPrBdwvGxaeLIYK/img.png?width=112&amp;amp;height=119&amp;amp;face=0_0_112_119&quot;&gt;&lt;a href=&quot;https://plantuml.com/ko/graphviz-dot&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://plantuml.com/ko/graphviz-dot&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nSRzw/hyRXwZbvuy/2t1WuHvYPrBdwvGxaeLIYK/img.png?width=112&amp;amp;height=119&amp;amp;face=0_0_112_119');&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;Test your GraphViz installation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This page gives information about testing your GraphViz installation for PlantUML. It lists some basic checks and some actions to be taken in case of failure.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;plantuml.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;3. VS Code marketplace에서 PlantUML 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 프로젝트 폴더 아래에 &lt;code&gt;docs&lt;/code&gt;폴더 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. &lt;code&gt;react_component_diagram.plantuml&lt;/code&gt;&amp;nbsp;파일 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. UI 디자인을 보면서 Component화 할 수 있는 것들을 추려내기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. Component 간 구조 정의 (e.g. 상속, 조합)&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;a href=&quot;https://plantuml.com/ko/class-diagram&quot;&gt;https://plantuml.com/ko/class-diagram&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1679002415271&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;클래스 다이어그램 구문 및 기능&quot; data-og-description=&quot;PlantUML 클래스 다이어그램 구문 : 당신은 정의 할 수 있습니다 인터페이스, 회원, 관계, 패키지, 제네릭, 음 ... 변경 글꼴과 색상도 가능하다.&quot; data-og-host=&quot;plantuml.com&quot; data-og-source-url=&quot;https://plantuml.com/ko/class-diagram&quot; data-og-url=&quot;https://plantuml.com/ko/class-diagram&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uBChp/hyRWtiHU5n/TXXUfPPO09dmzr5pHVMKvk/img.png?width=613&amp;amp;height=358&amp;amp;face=0_0_613_358&quot;&gt;&lt;a href=&quot;https://plantuml.com/ko/class-diagram&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://plantuml.com/ko/class-diagram&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uBChp/hyRWtiHU5n/TXXUfPPO09dmzr5pHVMKvk/img.png?width=613&amp;amp;height=358&amp;amp;face=0_0_613_358');&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;클래스 다이어그램 구문 및 기능&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;PlantUML 클래스 다이어그램 구문 : 당신은 정의 할 수 있습니다 인터페이스, 회원, 관계, 패키지, 제네릭, 음 ... 변경 글꼴과 색상도 가능하다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;plantuml.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;&lt;a href=&quot;https://plantuml.com/ko/component-diagram&quot;&gt;https://plantuml.com/ko/component-diagram&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1679002431244&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;구성 요소 다이어그램 구문 및 기능&quot; data-og-description=&quot;PlantUML 구성 요소 다이어그램 구문 : 당신은 정의 할 수 있습니다 인터페이스, 구성 요소, 관계, 그룹, 메모 ... 변경 글꼴과 색상도 가능하다.&quot; data-og-host=&quot;plantuml.com&quot; data-og-source-url=&quot;https://plantuml.com/ko/component-diagram&quot; data-og-url=&quot;https://plantuml.com/ko/component-diagram&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c8TyWO/hyRXAmXVRk/7RjEXxqhfJwdvJZzuWkjP0/img.png?width=445&amp;amp;height=179&amp;amp;face=0_0_445_179&quot;&gt;&lt;a href=&quot;https://plantuml.com/ko/component-diagram&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://plantuml.com/ko/component-diagram&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c8TyWO/hyRXAmXVRk/7RjEXxqhfJwdvJZzuWkjP0/img.png?width=445&amp;amp;height=179&amp;amp;face=0_0_445_179');&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;구성 요소 다이어그램 구문 및 기능&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;PlantUML 구성 요소 다이어그램 구문 : 당신은 정의 할 수 있습니다 인터페이스, 구성 요소, 관계, 그룹, 메모 ... 변경 글꼴과 색상도 가능하다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;plantuml.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;/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;a href=&quot;https://plantuml.com/ko/object-diagram&quot;&gt;https://plantuml.com/ko/object-diagram&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1679002561044&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;Object Diagram syntax and features&quot; data-og-description=&quot;PlantUML object diagram syntax: You can define objects, fields, relationships, packages, notes... Changing fonts and colors is also possible.&quot; data-og-host=&quot;plantuml.com&quot; data-og-source-url=&quot;https://plantuml.com/ko/object-diagram&quot; data-og-url=&quot;https://plantuml.com/ko/object-diagram&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dwxkuh/hyRWuu7IgI/1mY2KUwyvQHIHr919WL9mK/img.png?width=480&amp;amp;height=161&amp;amp;face=0_0_480_161&quot;&gt;&lt;a href=&quot;https://plantuml.com/ko/object-diagram&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://plantuml.com/ko/object-diagram&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dwxkuh/hyRWuu7IgI/1mY2KUwyvQHIHr919WL9mK/img.png?width=480&amp;amp;height=161&amp;amp;face=0_0_480_161');&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;Object Diagram syntax and features&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;PlantUML object diagram syntax: You can define objects, fields, relationships, packages, notes... Changing fonts and colors is also possible.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;plantuml.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;/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. 객체 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679002743816&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@startuml

object CreateGroup
object AddMembers
object ExpenseMain

@enduml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;145&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVRshP/btr4hy6UHkZ/oMb9ek9RM3aWaHol7xYYa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVRshP/btr4hy6UHkZ/oMb9ek9RM3aWaHol7xYYa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVRshP/btr4hy6UHkZ/oMb9ek9RM3aWaHol7xYYa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVRshP%2Fbtr4hy6UHkZ%2FoMb9ek9RM3aWaHol7xYYa1%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;400&quot; height=&quot;226&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;145&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;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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7948.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/75I9D/btr4hPOdvKn/hPOoANAoqyMproZYNSmUZ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/75I9D/btr4hPOdvKn/hPOoANAoqyMproZYNSmUZ0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/75I9D/btr4hPOdvKn/hPOoANAoqyMproZYNSmUZ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F75I9D%2Fbtr4hPOdvKn%2FhPOoANAoqyMproZYNSmUZ0%2Fimg.jpg&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;4032&quot; height=&quot;3024&quot; data-filename=&quot;IMG_7948.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7949.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MpVkU/btr4fzyT0Vu/Ak9VCgGr3WnPLBGS7E9e8k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MpVkU/btr4fzyT0Vu/Ak9VCgGr3WnPLBGS7E9e8k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MpVkU/btr4fzyT0Vu/Ak9VCgGr3WnPLBGS7E9e8k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMpVkU%2Fbtr4fzyT0Vu%2FAk9VCgGr3WnPLBGS7E9e8k%2Fimg.jpg&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;4032&quot; height=&quot;3024&quot; data-filename=&quot;IMG_7949.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&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;pre id=&quot;code_1679003124440&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CreateGroup *-- CenteredOverlayForm
AddMembers *-- CenteredOverlayForm&lt;/code&gt;&lt;/pre&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7950.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6MLUc/btr4guKSMbI/WEQn4YRvemUiw7zZhWMkKK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6MLUc/btr4guKSMbI/WEQn4YRvemUiw7zZhWMkKK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6MLUc/btr4guKSMbI/WEQn4YRvemUiw7zZhWMkKK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6MLUc%2Fbtr4guKSMbI%2FWEQn4YRvemUiw7zZhWMkKK%2Fimg.jpg&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;4032&quot; height=&quot;3024&quot; data-filename=&quot;IMG_7950.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&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;저 하얀색 박스도 CreateGroup이나 AddMembers의 CenteredOverlayForm을 재사용해서 만들 수 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Form의 형태가 아니므로 분리가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679003704620&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@startuml

object CreateGroup
object AddMembers
object ExpenseMain

object CenteredOverlay
object CenteredOverlayForm

object AddExpenseForm
object ExpenseSummary
object ExpenseList

CreateGroup *-- CenteredOverlayForm
AddMembers *-- CenteredOverlayForm

CenteredOverlayForm *-- CenteredOverlay

ExpenseMain *-- AddExpenseForm
ExpenseMain *-- ExpenseSummary
ExpenseMain *-- ExpenseList
ExpenseList *-- CenteredOverlay 

@enduml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mi8b3/btr4jovUpe9/HLIx5tlYWT6vbssnaxv0r0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mi8b3/btr4jovUpe9/HLIx5tlYWT6vbssnaxv0r0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mi8b3/btr4jovUpe9/HLIx5tlYWT6vbssnaxv0r0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmi8b3%2Fbtr4jovUpe9%2FHLIx5tlYWT6vbssnaxv0r0%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;800&quot; height=&quot;204&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;204&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;강의에서는 이렇게 만들었는데, 내가 보기엔 CenteredOverlay가 CenteredOverlayForm을 포함하는 관계가 더 적절한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CenteredOverlay 안에는 CenteredOverlayForm가 있을 수도 있고 ExpenseList가 있을 수도 있기 때문이다.&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;근데 또 뭐 정확한 위계로 배치했다기보다 CenteredOverlay는 디자인적인 역할만 하는 거라 따로 빼놓은 거면 저게 맞을 수도..&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_1679004922573&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@startuml

object CreateGroup {
  state groupName: string
  ' 입력된 값이 조건을 만족하는지 체크 '
  state formValidated: boolean
}
object AddMembers {
  state members: string[]
  state formValidated: boolean
}
object ExpenseMain {
  ' 상태 관리 라이브러리를 사용해서 store에서 get할 수 있기 때문에 '
  ' props는 아닐 수도 '
  groupName: string
  members: string[]
}

object CenteredOverlay
object CenteredOverlayForm {
  ' state와 props는 구별해준다. '
  props title: string
  props inputType: string
  props handleFormSubmitted: function

  state currentValue: string
}

object AddExpenseForm {
  ' members는 계속해서 변하는 상태값이 아님 '
  members: string[]
  state payer: string
  ' 결제 내역 '
  state description: string
  state amount: number
  state paidAt: Date
  state formValidated: boolean
}
object ExpenseSummary {
  expenses: expense[]
  members: string[]
}
object ExpenseList {
  expenses: expense[]
}

CreateGroup *-- CenteredOverlayForm
AddMembers *-- CenteredOverlayForm

CenteredOverlayForm *-- CenteredOverlay

ExpenseMain *-- AddExpenseForm
ExpenseMain *-- ExpenseSummary
ExpenseMain *-- ExpenseList
ExpenseList *-- CenteredOverlay 

@enduml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;485&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djLYyZ/btr4gTXXLuk/lxyprIVgjCo1IHN05RHIR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djLYyZ/btr4gTXXLuk/lxyprIVgjCo1IHN05RHIR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djLYyZ/btr4gTXXLuk/lxyprIVgjCo1IHN05RHIR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjLYyZ%2Fbtr4gTXXLuk%2FlxyprIVgjCo1IHN05RHIR0%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;600&quot; height=&quot;214&quot; data-origin-width=&quot;485&quot; data-origin-height=&quot;173&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;다른 다이어그램은 익숙하지도 않고 잘 안 와닿았는데, 리액트를 써본 입장에서 객체 다이어그램은 프로그램을 만들기 전 필수 요소라고 느껴지기까지 한다. 다이어그램을 그려보면서 이미 코드가 어떤 식으로 작성돼야 할지 어느 정도 감을 잡을 수 있고, 큰 그림을 그릴 수 있게 됐다. 작은 프로젝트라도 PlantUML로 도식화해보는 훈련을 자꾸 하자. 화이팅&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;#패스트캠퍼스 #패캠챌린지 #수강료0원챌린지 #환급챌린지 #직장인인강 #직장인자기계발&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#패캠인강후기 #패스트캠퍼스후기 #오공완 #사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact&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 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;&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;a href=&quot;http://bit.ly/3Y34pE0&quot;&gt;http://bit.ly/3Y34pE0&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679005170434&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;패스트캠퍼스 [직장인 실무교육]&quot; data-og-description=&quot;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;http://bit.ly/3Y34pE0&quot; data-og-url=&quot;https://fastcampus.co.kr/?utm_source=naver&amp;amp;utm_medium=viral&amp;amp;utm_campaign=prm^230201^AOP&amp;amp;utm_content=blog^challenge^AOP&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bOgECU/hyRWrLMoUe/c9uykCzjZwhCgeNSKwmVN1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;http://bit.ly/3Y34pE0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://bit.ly/3Y34pE0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bOgECU/hyRWrLMoUe/c9uykCzjZwhCgeNSKwmVN1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;패스트캠퍼스 [직장인 실무교육]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>부트캠프/따로 공부</category>
      <category>사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact</category>
      <category>수강료0원챌린지</category>
      <category>오공완</category>
      <category>직장인인강</category>
      <category>직장인자기계발</category>
      <category>패스트캠퍼스</category>
      <category>패스트캠퍼스후기</category>
      <category>패캠인강후기</category>
      <category>패캠챌린지</category>
      <category>환급챌린지</category>
      <author>하이고니</author>
      <guid isPermaLink="true">https://hajongon.tistory.com/219</guid>
      <comments>https://hajongon.tistory.com/219#entry219comment</comments>
      <pubDate>Fri, 17 Mar 2023 07:20:47 +0900</pubDate>
    </item>
    <item>
      <title>사이드 프로젝트 with React 25. 프론트엔드 설계란?</title>
      <link>https://hajongon.tistory.com/217</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;프론트엔드 설계란?&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;/li&gt;
&lt;li&gt;더치페이의 프론트엔드 아키텍처 자체는 심플하기에, 리액트 컴포넌트 간의 의존성을 나타내기 위한 클래스 다이어그램을 그려볼 예정!&lt;/li&gt;
&lt;/ul&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;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;UI 디자인을 살펴보면서 공통된 요소들이 있는지 파악&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공통된 요소들에 대해 컴포넌트화(DRY;Don't Repeat Yourself 원칙)&lt;/li&gt;
&lt;li&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;Container 패턴: UI를 렌더링하는 데에 집중하는 컴포넌트, 데이터 불러오기 등의 비즈니스 로직을 담아 둘 Container 컴포넌트로 나눠 설계&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;1280&quot; data-origin-height=&quot;1068&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cY3Bdn/btr4blFwjfD/EwQMAhk0iW2aKWZKZDgPv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cY3Bdn/btr4blFwjfD/EwQMAhk0iW2aKWZKZDgPv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cY3Bdn/btr4blFwjfD/EwQMAhk0iW2aKWZKZDgPv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcY3Bdn%2Fbtr4blFwjfD%2FEwQMAhk0iW2aKWZKZDgPv1%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;800&quot; height=&quot;668&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1068&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;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;Provider 패턴: 컴포넌트 내 props drilling (프로퍼티 내리꽂기; 컴포넌트 트리의 한 부분에서 다른 부분으로 데이터를 전달하는 과정 중 하나로, props를 오로지 하위 컴포넌트로 전달하는 용도로만 사용)을 방지하고자 생긴 패턴. React Context나 Redux/Recoil과 같이 중앙 상태 관리를 이용하여 데이터를 저장해 두고, 필요한 모든 컴포넌트 (i.e. Consumer)에서 이 데이터에 접근 가능하도록 만든 패턴.&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;1280&quot; data-origin-height=&quot;1034&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cclBPV/btr37t5dja9/y1L7Dk4IBQoqBZ29xPQ3m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cclBPV/btr37t5dja9/y1L7Dk4IBQoqBZ29xPQ3m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cclBPV/btr37t5dja9/y1L7Dk4IBQoqBZ29xPQ3m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcclBPV%2Fbtr37t5dja9%2Fy1L7Dk4IBQoqBZ29xPQ3m1%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;800&quot; height=&quot;646&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1034&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;1280&quot; data-origin-height=&quot;387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh6BpX/btr38NoOBPt/14RPd5JiIg1tpWjJPlkFR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh6BpX/btr38NoOBPt/14RPd5JiIg1tpWjJPlkFR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh6BpX/btr38NoOBPt/14RPd5JiIg1tpWjJPlkFR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh6BpX%2Fbtr38NoOBPt%2F14RPd5JiIg1tpWjJPlkFR1%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;800&quot; height=&quot;242&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;387&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;App -&amp;gt; ( GroupDetailContainer ) -&amp;gt; GroupDetail 로 themeContext를 전달&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;각 컴포넌트에 최소한으로 필요한 props는 무엇일지 정의&lt;/li&gt;
&lt;li&gt;컴포넌트 간 어떤 데이터들을 주고받아야 할 지 생각&lt;/li&gt;
&lt;li&gt;각 컴포넌트 별 state/props를 미리 생각&lt;/li&gt;
&lt;li&gt;한 컴포넌트가 하나의 책임만 가지고 있는지 확인(Single Resposibility Principle)&lt;/li&gt;
&lt;li&gt;더치페이 예시 - 한 컴포넌트 내에서 그룹 생성 뿐 아니라 그룹 삭제까지 한다면 이 컴포넌트는 너무 많은 것을 핸들링하려고 하는 것이다 -&amp;gt; 쪼개자.&lt;/li&gt;
&lt;li&gt;읽어보면 좋을 자료&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1678925401269&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;Thinking in React &amp;ndash; React&quot; data-og-description=&quot;A JavaScript library for building user interfaces&quot; data-og-host=&quot;reactjs.org&quot; data-og-source-url=&quot;https://reactjs.org/docs/thinking-in-react.html&quot; data-og-url=&quot;https://reactjs.org/docs/thinking-in-react.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kyBdr/hyRWqZS0u8/fGIfPkqKIWh3eKZyrS3Ul1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/x6c59/hyRWrxIU4J/uNyWcAkESzKCNGDuHDtBp0/img.png?width=605&amp;amp;height=391&amp;amp;face=0_0_605_391,https://scrap.kakaocdn.net/dn/7YUl8/hyRWuHYkta/5gDx48YybIskTlizRGhDZ0/img.png?width=228&amp;amp;height=277&amp;amp;face=0_0_228_277&quot;&gt;&lt;a href=&quot;https://reactjs.org/docs/thinking-in-react.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://reactjs.org/docs/thinking-in-react.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kyBdr/hyRWqZS0u8/fGIfPkqKIWh3eKZyrS3Ul1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/x6c59/hyRWrxIU4J/uNyWcAkESzKCNGDuHDtBp0/img.png?width=605&amp;amp;height=391&amp;amp;face=0_0_605_391,https://scrap.kakaocdn.net/dn/7YUl8/hyRWuHYkta/5gDx48YybIskTlizRGhDZ0/img.png?width=228&amp;amp;height=277&amp;amp;face=0_0_228_277');&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;Thinking in React &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A JavaScript library for building user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;reactjs.org&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;투두리스트가 진짜 컴포넌트 하나에 다 때려박아놓은 구조인데, 리팩토링하면서 위 설계 구조에 대해 복습해봐야겠다.&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7928.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kRrdT/btr38KyOKcI/bINcC0kyfKs0mn7isAiq3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kRrdT/btr38KyOKcI/bINcC0kyfKs0mn7isAiq3K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kRrdT/btr38KyOKcI/bINcC0kyfKs0mn7isAiq3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkRrdT%2Fbtr38KyOKcI%2FbINcC0kyfKs0mn7isAiq3K%2Fimg.jpg&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;600&quot; height=&quot;450&quot; data-filename=&quot;IMG_7928.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7929.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bps372/btr35dPBcng/xxJnEEX0jFHDJd1f75TEMk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bps372/btr35dPBcng/xxJnEEX0jFHDJd1f75TEMk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bps372/btr35dPBcng/xxJnEEX0jFHDJd1f75TEMk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbps372%2Fbtr35dPBcng%2FxxJnEEX0jFHDJd1f75TEMk%2Fimg.jpg&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;600&quot; height=&quot;450&quot; data-filename=&quot;IMG_7929.JPG&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#패스트캠퍼스 #패캠챌린지 #수강료0원챌린지 #환급챌린지 #직장인인강 #직장인자기계발&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;#패캠인강후기 #패스트캠퍼스후기 #오공완 #사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact&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 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;&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;a href=&quot;http://bit.ly/3Y34pE0&quot;&gt;http://bit.ly/3Y34pE0&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678925519747&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;패스트캠퍼스 [직장인 실무교육]&quot; data-og-description=&quot;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&quot; data-og-host=&quot;fastcampus.co.kr&quot; data-og-source-url=&quot;http://bit.ly/3Y34pE0&quot; data-og-url=&quot;https://fastcampus.co.kr/?utm_source=naver&amp;amp;utm_medium=viral&amp;amp;utm_campaign=prm^230201^AOP&amp;amp;utm_content=blog^challenge^AOP&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bJDcQh/hyRWA2xkmL/L7t7Z3bpO7jkuePeutWgsk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;http://bit.ly/3Y34pE0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://bit.ly/3Y34pE0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bJDcQh/hyRWA2xkmL/L7t7Z3bpO7jkuePeutWgsk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;패스트캠퍼스 [직장인 실무교육]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastcampus.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>사이드프로젝트10개기술스택으로구현하는풀스택서버리스프로젝트withReact</category>
      <category>수강료0원챌린지</category>
      <category>오공완</category>
      <category>직장인인강</category>
      <category>직장인자기계발</category>
      <category>패스트캠퍼스</category>
      <category>패스트캠퍼스후기</category>
      <category>패캠인강후기</category>
      <category>패캠챌린지</category>
      <category>환급챌린지</category>
      <author>하이고니</author>
      <guid isPermaLink="true">https://hajongon.tistory.com/217</guid>
      <comments>https://hajongon.tistory.com/217#entry217comment</comments>
      <pubDate>Thu, 16 Mar 2023 09:13:56 +0900</pubDate>
    </item>
  </channel>
</rss>