<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>TimeMap.exe</title>
    <link>https://time-map-installer.tistory.com/</link>
    <description>ThreeLight Studio의 블로그, TimeMap.exe에 오신 것을 환영합니다.</description>
    <language>ko</language>
    <pubDate>Tue, 19 May 2026 19:37:32 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>ThreeLight</managingEditor>
    <image>
      <title>TimeMap.exe</title>
      <url>https://tistory1.daumcdn.net/tistory/5422937/attach/b9f5bea4f7d541f8b9cc244fd0224b03</url>
      <link>https://time-map-installer.tistory.com</link>
    </image>
    <item>
      <title>Electron, 웹 개발인데요, 앱입니다.</title>
      <link>https://time-map-installer.tistory.com/271</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1598&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bP2WRr/btsHPHLxOuZ/euxFLbKHk4Kmdf9SIIX3N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bP2WRr/btsHPHLxOuZ/euxFLbKHk4Kmdf9SIIX3N1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bP2WRr/btsHPHLxOuZ/euxFLbKHk4Kmdf9SIIX3N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbP2WRr%2FbtsHPHLxOuZ%2FeuxFLbKHk4Kmdf9SIIX3N1%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;1598&quot; height=&quot;840&quot; data-origin-width=&quot;1598&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;옛날 옛적(어제), 한 개발자가 살았어요.&lt;br&gt;이 개발자는 아이디어가 떠오를 때마다 도구를 만들어 제공하려고 하는 특징이 있었지만,&lt;br&gt;사내 비즈니스 로직이 엮여있을까봐 마음편히 배포하지 못하고 어떻게 해야할 지 고민을 하고있었어요.&lt;br&gt;&lt;br&gt;정말 아쉬웠어요.&lt;br&gt;&lt;br&gt;그렇게 계속해서 쌓여만 가는 코드들을 가만히 내버려 둘 수는 없던 저는 방법을 찾아보기로 했습니다.&lt;br&gt;그렇게 찾은 굉장한 프로그램, Electron을 소개합니다!&lt;/p&gt;
&lt;h1&gt;왜 하필 Electron이죠?&lt;/h1&gt;
&lt;hr&gt;
&lt;h2&gt;Electron?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Electron&lt;/code&gt;은 &lt;code&gt;GitHub&lt;/code&gt;에서 개발한 &lt;strong&gt;오픈 소스 프레임워크&lt;/strong&gt;로, &lt;strong&gt;웹 기술(HTML, CSS, JavaScript)&lt;/strong&gt; 을 사용하여 &lt;strong&gt;데스크탑 애플리케이션&lt;/strong&gt;을 만들 수 있게 해줍니다.&lt;br&gt;&lt;code&gt;Node.js&lt;/code&gt;와 &lt;code&gt;Chromium&lt;/code&gt;을 결합하여 크로스 플랫폼 애플리케이션을 개발할 수 있으며, &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;윈도우, &lt;/li&gt;
&lt;li&gt;macOS, &lt;/li&gt;
&lt;li&gt;리눅스 등&lt;br&gt;&lt;br&gt;다양한 운영체제에서 실행 가능하다는 특징을 지니고 있어요.&lt;br&gt;어쩌면 우리가 사용하고 있는 데스크톱 앱 중 이걸 사용하여 제작한 프로그램도 있을거에요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;왜 Electron인가?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Electron의 강점은 참 많은데, 여기 세 가지 정도만 가져와보았어요.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;크로스 플랫폼 지원&lt;/strong&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Electron&lt;/code&gt;은 &lt;strong&gt;한 번의 코드 작성&lt;/strong&gt;으로 여러 운영체제에서 실행할 수 있는 애플리케이션을 만들 수 있습니다. &lt;/li&gt;
&lt;li&gt;동일한 코드베이스를 지니고 있기에 개발 시간을 절약하고 유지보수를 간편하게 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;웹 기술 사용&lt;/strong&gt;&lt;ol&gt;
&lt;li&gt;기존에 웹 개발 경험이 있는 개발자라면 새로운 언어나 프레임워크를 배우지 않고도 &lt;strong&gt;쉽게&lt;/strong&gt; 데스크탑 애플리케이션을 개발할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;풍부한 생태계&lt;/strong&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Electron&lt;/code&gt;은 활발한 커뮤니티와 다양한 플러그인, 모듈을 제공하여 개발에 큰 도움을 줍니다. 이를 통해 필요한 기능을 손쉽게 구현할 수 있습니다.&lt;/li&gt;
&lt;li&gt;그리고 &lt;code&gt;MIT LICENSE&lt;/code&gt;라서 무료라는 특징이 있죠.(감사합니다!)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;다른 프로그램과의 차이는 무엇인가요?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;분명 다른 비슷한 프로그램도 많을 거에요. 함께 확인해봐요.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;비교군으로 &lt;code&gt;NW.js&lt;/code&gt;, &lt;code&gt;JavaFX&lt;/code&gt;, &lt;code&gt;Qt&lt;/code&gt;를 들고왔어요.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;NW.js&lt;/code&gt;&lt;ol&gt;
&lt;li&gt;웹 기술을 사용하여 데스크탑 애플리케이션을 개발할 수 있게 해주는 프레임워크입니다. &lt;/li&gt;
&lt;li&gt;하지만 Electron과는 다른 기술적 특징을 지니고 있으며, &lt;a href=&quot;https://electron.ebookchain.org/ko-KR/development/atom-shell-vs-node-webkit.html&quot;&gt;Electron이 NW.js(node-webkit)와 기술적으로 다른점&lt;/a&gt;에서 자세한 정보를 확인해보세요.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JavaFX&lt;/code&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Java&lt;/code&gt;를 기반으로 한 데스크탑 애플리케이션 개발 프레임워크입니다. &lt;/li&gt;
&lt;li&gt;하지만 &lt;code&gt;JavaFX&lt;/code&gt;는 &lt;code&gt;Java&lt;/code&gt; 언어에 익숙해야 하며, 웹 기술을 사용하는 Electron에 비해 &lt;strong&gt;진입 장벽이 높습니다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Qt&lt;/code&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Qt&lt;/code&gt;는 강력한 데스크탑 애플리케이션 개발 프레임워크이지만, &lt;code&gt;C++&lt;/code&gt;을 사용해야 하고, &lt;strong&gt;상용 라이선스 비용&lt;/strong&gt;이 발생할 수 있습니다. &lt;/li&gt;
&lt;li&gt;반면 Electron은 무료 오픈 소스이며, 웹 기술을 사용하기 때문에 접근성이 높습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;개발 시작하기&lt;/h1&gt;
&lt;hr&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;서론이 길었죠. 그럼 이제 진짜로 개발을 시작할 때가 왔습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;기술, 아이디어 선정&lt;/h2&gt;
&lt;p&gt;우선 기술스택의 경우 다음과 같이 설정할 거에요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frontend Base: &lt;code&gt;React + Vite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Language: &lt;code&gt;TypeScript&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;CSS: &lt;code&gt;Tailwind CSS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Package Manager: &lt;code&gt;npm&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;monoRepo&lt;/code&gt;도 아니고, &lt;code&gt;yarn&lt;/code&gt;은 이미 지원 종료 되었으니까요!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그리고 우리는 다음 아이디어를 어플리케이션으로 만들어보도록 할게요.&lt;br&gt;&lt;br&gt;이름하여 &lt;strong&gt;기념일 관리 애플리케이션&lt;/strong&gt;!&lt;br&gt;&lt;br&gt;데스크톱에서 직접 알람을 받을 수 있도록 하기엔 또 이만한 어플이 없다고 생각했어요.&lt;br&gt;물론 이 글에서는 앱을 만드는 것까지 다루지는 않을거에요. 세팅까지 함께 가보시죠.&lt;/p&gt;
&lt;h2&gt;프로젝트 초기 세팅&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;작성자의 환경에 맞게 &lt;code&gt;MacOS M1&lt;/code&gt; 기준으로 작성되었어요.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;프로젝트 생성&lt;/h3&gt;
&lt;p&gt;먼저, 프로젝트를 생성해봅시다. 프로젝트를 생성하려는 폴더로 이동해주세요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd &amp;lt;directory-to-make-project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/SZPTWIk.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그 후, React 프로젝트를 생성합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm create vite@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/0uSqSM6.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/oIFxG9v.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;우와! 리액트 프로젝트가 생성되었어요!&lt;/p&gt;
&lt;h3&gt;Tailwind CSS 설치, 설정&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;간단하게 설정할 수 있죠. 따라오세요!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;관련 설정들 설치 - 조금 시간이 걸려요. &lt;strong&gt;기다리는 동안 차 한 잔 하는건 어때요?&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;tailwind.config.js&lt;/code&gt; 파일을 수정해줄게요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;/** @type {import(&amp;#39;tailwindcss&amp;#39;).Config} */
export default {
  content: [&amp;quot;./src/**/*.{js,ts,jsx,tsx}&amp;quot;],
  theme: {
    extend: {},
  },
  plugins: [],
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;src/index.css&lt;/code&gt; 파일을 다음과 같이 수정합니다&lt;br&gt;기존에는 초기 전역 스타일 세팅이 되어있을 거에요. 가장 윗부분에 다음 코드를 추가해봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;@tailwind base;
@tailwind components;
@tailwind utilities;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Electron 세팅하기&lt;/h1&gt;
&lt;hr&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;이제, 초기세팅은 마쳤으니 &lt;code&gt;Electron&lt;/code&gt;만 연결하면 되겠네요!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;좋아요. 이제 &lt;code&gt;Electron&lt;/code&gt;을 세팅해보죠.&lt;/p&gt;
&lt;p&gt;우선, 설치부터 해봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install electron --save-dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;깜짝 quiz&lt;/strong&gt;: &lt;code&gt;--save-dev&lt;/code&gt;는 무엇일까요?&lt;/p&gt;
&lt;h2&gt;main.js 설정&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Electron에서 핵심이라고 불리는 파일들 중 하나인 main.js를 설정해봅시다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;위치는 root directory에요. &lt;code&gt;README.md&lt;/code&gt;, &lt;code&gt;package.json&lt;/code&gt; 등이 위치해있는 곳이죠!&lt;br&gt;&lt;br&gt;그렇게 작성된 &lt;code&gt;main.js&lt;/code&gt;는 다음과 같아요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { app, BrowserWindow } from &amp;quot;electron&amp;quot;;
import { fileURLToPath } from &amp;quot;url&amp;quot;;
import { dirname, join } from &amp;quot;path&amp;quot;;
import { spawn } from &amp;quot;child_process&amp;quot;;

// 현재 모듈의 디렉토리를 가져오기 위한 설정
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

async function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: join(__dirname, &amp;quot;preload.cjs&amp;quot;),
      nodeIntegration: true,
    },
  });

  mainWindow.loadURL(&amp;quot;http://localhost:5173&amp;quot;);
  mainWindow.webContents.openDevTools();
}

app.whenReady().then(async () =&amp;gt; {
  const { default: waitOn } = await import(&amp;quot;wait-on&amp;quot;);

  waitOn({ resources: [&amp;quot;http://localhost:5173&amp;quot;] }, (err) =&amp;gt; {
    if (err) {
      console.error(&amp;quot;Failed to wait for Vite server:&amp;quot;, err);
      process.exit(1);
    }

    createWindow();
  });
});

app.on(&amp;quot;window-all-closed&amp;quot;, () =&amp;gt; {
  if (process.platform !== &amp;quot;darwin&amp;quot;) {
    app.quit();
  }
});

app.on(&amp;quot;activate&amp;quot;, () =&amp;gt; {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

// Vite 개발 서버 실행
const viteProcess = spawn(&amp;quot;npm&amp;quot;, [&amp;quot;run&amp;quot;, &amp;quot;start&amp;quot;], { stdio: &amp;quot;inherit&amp;quot; });

viteProcess.on(&amp;quot;close&amp;quot;, (code) =&amp;gt; {
  console.log(`Vite process exited with code ${code}`);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;lint 에러&lt;/strong&gt;를 피하기 위해 추가적인 설정을 하고 ES6를 적용시켰어요.&lt;/p&gt;
&lt;h3&gt;ESLint 설정&lt;/h3&gt;
&lt;p&gt;그리고 &lt;strong&gt;Lint&lt;/strong&gt; 파일도 수정하여 &lt;code&gt;node&lt;/code&gt; 환경을 명시적으로 설정하여 &lt;code&gt;process&lt;/code&gt; 객체를 인식하도록 해주었어요.&lt;br&gt;&lt;br&gt;&lt;code&gt;.eslintrc.cjs&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    &amp;quot;eslint:recommended&amp;quot;,
    &amp;quot;plugin:react/recommended&amp;quot;,
    &amp;quot;plugin:@typescript-eslint/recommended&amp;quot;,
  ],
  parser: &amp;quot;@typescript-eslint/parser&amp;quot;,
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: &amp;quot;latest&amp;quot;,
    sourceType: &amp;quot;module&amp;quot;,
  },
  plugins: [&amp;quot;react&amp;quot;, &amp;quot;@typescript-eslint&amp;quot;],
  rules: {
    &amp;quot;no-unused-vars&amp;quot;: &amp;quot;warn&amp;quot;,
    &amp;quot;no-undef&amp;quot;: &amp;quot;off&amp;quot;,
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;preload.cjs 설정&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;실행시키기 전 사전에 해야하는 작업이 필요하다면 해당 파일에 세팅을 해두어요.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;window.addEventListener(&amp;quot;DOMContentLoaded&amp;quot;, () =&amp;gt; {
  const replaceText = (selector, text) =&amp;gt; {
    const element = document.getElementById(selector);
    if (element) element.innerText = text;
  };

  for (const dependency of [&amp;quot;chrome&amp;quot;, &amp;quot;node&amp;quot;, &amp;quot;electron&amp;quot;]) {
    replaceText(`${dependency}-version`, process.versions[dependency]);
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;스크립트 설정 후 실행하기&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;이제 프로그램을 실행시킬 스크립트가 있어야겠죠?&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;package.json의 script에 추가&lt;/h3&gt;
&lt;p&gt;다음과 같이 스크립트를 추가해봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&amp;quot;main&amp;quot;: &amp;quot;main.js&amp;quot;,
&amp;quot;scripts&amp;quot;: {
    &amp;quot;start&amp;quot;: &amp;quot;vite&amp;quot;,
    &amp;quot;build&amp;quot;: &amp;quot;vite build&amp;quot;,
    &amp;quot;serve&amp;quot;: &amp;quot;vite preview&amp;quot;,
    &amp;quot;electron&amp;quot;: &amp;quot;electron .&amp;quot;
},&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;확인해보기&lt;/h1&gt;
&lt;hr&gt;
&lt;h2&gt;간단한 예제 코드 작성&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;code&gt;App.tsx&lt;/code&gt;에 간단히 작성을 해봅시다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { useState } from &amp;quot;react&amp;quot;;
import &amp;quot;./App.css&amp;quot;;

interface Event {
  date: string;
  name: string;
}

const App: React.FC = () =&amp;gt; {
  const [events, setEvents] = useState&amp;lt;Event[]&amp;gt;([]);
  const [newEvent, setNewEvent] = useState&amp;lt;Event&amp;gt;({ date: &amp;quot;&amp;quot;, name: &amp;quot;&amp;quot; });

  const handleAddEvent = () =&amp;gt; {
    if (newEvent.date &amp;amp;&amp;amp; newEvent.name) {
      setEvents([...events, newEvent]);
      setNewEvent({ date: &amp;quot;&amp;quot;, name: &amp;quot;&amp;quot; });
    }
  };

  return (
    &amp;lt;div className=&amp;#39;min-h-screen bg-gray-100 flex flex-col items-center justify-center py-10&amp;#39;&amp;gt;
      &amp;lt;div className=&amp;#39;bg-white p-8 rounded shadow-md w-full max-w-md&amp;#39;&amp;gt;
        &amp;lt;h1 className=&amp;#39;text-3xl font-bold mb-6 text-center text-gray-700&amp;#39;&amp;gt;
          기념일 관리
        &amp;lt;/h1&amp;gt;
        &amp;lt;div className=&amp;#39;mb-4 flex flex-col gap-4&amp;#39;&amp;gt;
          &amp;lt;input
            type=&amp;#39;date&amp;#39;
            value={newEvent.date}
            onChange={(e) =&amp;gt; setNewEvent({ ...newEvent, date: e.target.value })}
            className=&amp;#39;border border-gray-300 p-2 rounded&amp;#39;
          /&amp;gt;
          &amp;lt;input
            type=&amp;#39;text&amp;#39;
            placeholder=&amp;#39;이벤트 이름&amp;#39;
            value={newEvent.name}
            onChange={(e) =&amp;gt; setNewEvent({ ...newEvent, name: e.target.value })}
            className=&amp;#39;border border-gray-300 p-2 rounded&amp;#39;
          /&amp;gt;
          &amp;lt;button
            onClick={handleAddEvent}
            className=&amp;#39;bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded&amp;#39;&amp;gt;
            추가
          &amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;ul className=&amp;#39;divide-y divide-gray-200&amp;#39;&amp;gt;
          {events.map((event, index) =&amp;gt; (
            &amp;lt;li key={index} className=&amp;#39;py-2 flex justify-between items-center&amp;#39;&amp;gt;
              &amp;lt;span className=&amp;#39;font-medium text-gray-800&amp;#39;&amp;gt;{event.date}&amp;lt;/span&amp;gt;
              &amp;lt;span className=&amp;#39;text-gray-600&amp;#39;&amp;gt;{event.name}&amp;lt;/span&amp;gt;
            &amp;lt;/li&amp;gt;
          ))}
        &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;결과물은?&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm run electron
# 이 명령어를 실행시키면 npm run start가 먼저 실행되고 이후에 앱이 열릴 것이에요.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/BIxlSYv.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;앱 형태로 잘 열리는 것을 볼 수 있어요.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;끝!&lt;/strong&gt;&lt;/p&gt;</description>
      <category>(3D)Dev Deep Dive/Frontend origin</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/271</guid>
      <comments>https://time-map-installer.tistory.com/271#entry271comment</comments>
      <pubDate>Thu, 6 Jun 2024 16:27:14 +0900</pubDate>
    </item>
    <item>
      <title>프론트엔드에 적절한 폴더구조는 없다</title>
      <link>https://time-map-installer.tistory.com/270</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/@tminstaller/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%9[&quot;&gt;https://medium.com/@tminstaller/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%9[&lt;/a&gt;&lt;a style=&quot;background-color: #222529; color: #000000; text-align: left;&quot; href=&quot;https://medium.com/@tminstaller/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90-%EC%A0%81%EC%A0%88%ED%95%9C-%ED%8F%B4%EB%8D%94%EA%B5%AC%EC%A1%B0%EB%8A%94-%EC%97%86%EB%8B%A4-ba3a47e91508&quot; data-sk=&quot;tooltip_parent&quot; data-stringify-link=&quot;https://medium.com/@tminstaller/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90-%EC%A0%81%EC%A0%88%ED%95%9C-%ED%8F%B4%EB%8D%94%EA%B5%AC%EC%A1%B0%EB%8A%94-%EC%97%86%EB%8B%A4-ba3a47e91508&quot;&gt;&amp;hellip;]D%94%EA%B5%AC%EC%A1%B0%EB%8A%94-%EC%97%86%EB%8B%A4-ba3a47e91508&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1708772315899&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;폴더구조엔 정답이 있을까요?&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@tminstaller/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90-%EC%A0%81%EC%A0%88%ED%95%9C-%ED%8F%B4%EB%8D%94%EA%B5%AC%EC%A1%B0%EB%8A%94-%EC%97%86%EB%8B%A4-ba3a47e91508&quot; data-og-url=&quot;https://medium.com/@tminstaller/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90-%EC%A0%81%EC%A0%88%ED%95%9C-%ED%8F%B4%EB%8D%94%EA%B5%AC%EC%A1%B0%EB%8A%94-%EC%97%86%EB%8B%A4-ba3a47e91508&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DBJEL/hyVm40ZYt1/Jm9fedgk5fGmsDA6cR64XK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://medium.com/@tminstaller/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90-%EC%A0%81%EC%A0%88%ED%95%9C-%ED%8F%B4%EB%8D%94%EA%B5%AC%EC%A1%B0%EB%8A%94-%EC%97%86%EB%8B%A4-ba3a47e91508&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@tminstaller/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90-%EC%A0%81%EC%A0%88%ED%95%9C-%ED%8F%B4%EB%8D%94%EA%B5%AC%EC%A1%B0%EB%8A%94-%EC%97%86%EB%8B%A4-ba3a47e91508&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DBJEL/hyVm40ZYt1/Jm9fedgk5fGmsDA6cR64XK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024');&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;폴더구조엔 정답이 있을까요?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>(3D)Dev Deep Dive/Frontend origin</category>
      <category>FE</category>
      <category>folder structure</category>
      <category>FrontendEngineering</category>
      <category>폴더구조</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/270</guid>
      <comments>https://time-map-installer.tistory.com/270#entry270comment</comments>
      <pubDate>Sat, 24 Feb 2024 19:59:49 +0900</pubDate>
    </item>
    <item>
      <title>[ComputerScience] 비전공자의 시선으로 바라본 TCP와 UDP, 이쯤되면 다시 한번 복습해 봐야겠죠?</title>
      <link>https://time-map-installer.tistory.com/269</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw3kTz/btsC8QstKYt/LZk3NT7oRQmPRGBJFCeqk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw3kTz/btsC8QstKYt/LZk3NT7oRQmPRGBJFCeqk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw3kTz/btsC8QstKYt/LZk3NT7oRQmPRGBJFCeqk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw3kTz%2FbtsC8QstKYt%2FLZk3NT7oRQmPRGBJFCeqk0%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;262&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1704720271713&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;[ComputerScience] 비전공자의 시선으로 바라본 TCP와 UDP, 이쯤되면 다시 한번 복습해 봐야겠죠?&quot; data-og-description=&quot;TCP, UDP, 근본적인 개념이라 이미 알고 있는 내용이긴 한데 다시 자세히 말해보라하면 기억이 잘 나지 않으신가요? 그렇다면 다시 한 번 봐야겠죠!&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@tminstaller/computerscience-%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EC%9D%98-%EC%8B%9C%EC%84%A0%EC%9C%BC%EB%A1%9C-%EB%B0%94%EB%9D%BC%EB%B3%B8-tcp%EC%99%80-udp-%EC%9D%B4%EC%AF%A4%EB%90%98%EB%A9%B4-%EB%8B%A4%EC%8B%9C-%ED%95%9C%EB%B2%88-%EB%B3%B5%EC%8A%B5%ED%95%B4-%EB%B4%90%EC%95%BC%EA%B2%A0%EC%A3%A0-1a89d799b6bc&quot; data-og-url=&quot;https://medium.com/@tminstaller/computerscience-%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EC%9D%98-%EC%8B%9C%EC%84%A0%EC%9C%BC%EB%A1%9C-%EB%B0%94%EB%9D%BC%EB%B3%B8-tcp%EC%99%80-udp-%EC%9D%B4%EC%AF%A4%EB%90%98%EB%A9%B4-%EB%8B%A4%EC%8B%9C-%ED%95%9C%EB%B2%88-%EB%B3%B5%EC%8A%B5%ED%95%B4-%EB%B4%90%EC%95%BC%EA%B2%A0%EC%A3%A0-1a89d799b6bc&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/drkdiI/hyU2qomx5r/4dos3EFrxPjk8RtO8kVifk/img.png?width=911&amp;amp;height=341&amp;amp;face=0_0_911_341,https://scrap.kakaocdn.net/dn/cGCP4s/hyU2rANOl4/pemFTm1MUcFs4xfCTZhzc1/img.png?width=1000&amp;amp;height=300&amp;amp;face=0_0_1000_300&quot;&gt;&lt;a href=&quot;https://medium.com/@tminstaller/computerscience-%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EC%9D%98-%EC%8B%9C%EC%84%A0%EC%9C%BC%EB%A1%9C-%EB%B0%94%EB%9D%BC%EB%B3%B8-tcp%EC%99%80-udp-%EC%9D%B4%EC%AF%A4%EB%90%98%EB%A9%B4-%EB%8B%A4%EC%8B%9C-%ED%95%9C%EB%B2%88-%EB%B3%B5%EC%8A%B5%ED%95%B4-%EB%B4%90%EC%95%BC%EA%B2%A0%EC%A3%A0-1a89d799b6bc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@tminstaller/computerscience-%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EC%9D%98-%EC%8B%9C%EC%84%A0%EC%9C%BC%EB%A1%9C-%EB%B0%94%EB%9D%BC%EB%B3%B8-tcp%EC%99%80-udp-%EC%9D%B4%EC%AF%A4%EB%90%98%EB%A9%B4-%EB%8B%A4%EC%8B%9C-%ED%95%9C%EB%B2%88-%EB%B3%B5%EC%8A%B5%ED%95%B4-%EB%B4%90%EC%95%BC%EA%B2%A0%EC%A3%A0-1a89d799b6bc&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/drkdiI/hyU2qomx5r/4dos3EFrxPjk8RtO8kVifk/img.png?width=911&amp;amp;height=341&amp;amp;face=0_0_911_341,https://scrap.kakaocdn.net/dn/cGCP4s/hyU2rANOl4/pemFTm1MUcFs4xfCTZhzc1/img.png?width=1000&amp;amp;height=300&amp;amp;face=0_0_1000_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;[ComputerScience] 비전공자의 시선으로 바라본 TCP와 UDP, 이쯤되면 다시 한번 복습해 봐야겠죠?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;TCP, UDP, 근본적인 개념이라 이미 알고 있는 내용이긴 한데 다시 자세히 말해보라하면 기억이 잘 나지 않으신가요? 그렇다면 다시 한 번 봐야겠죠!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Development Study/CS(Computer Science)</category>
      <category>handshake</category>
      <category>TCP</category>
      <category>UDP</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/269</guid>
      <comments>https://time-map-installer.tistory.com/269#entry269comment</comments>
      <pubDate>Mon, 8 Jan 2024 22:25:03 +0900</pubDate>
    </item>
    <item>
      <title>&amp;lt;TimeMap.dmg&amp;gt; WEBP 이미지를 통해 웹 렌더링 시간을 개선해보자</title>
      <link>https://time-map-installer.tistory.com/268</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 포트폴리오 웹사이트를 만들었다. 처음엔 그저 언젠간 나만의 페이지를 만들어야지 싶었는데, 그러지 못했다.&lt;br /&gt;왜냐하면 처음부터 너무나도 완벽한 것을 바랬으니, 디자인이 마음에 들지 않으면 제작하지 않기를 반복하였다.&lt;br /&gt;그렇게 시간이 흘러 웹 개발을 시작한 지 1년이 다 되어갈 무렵, 클린코드에 대한 컨퍼런스를 다녀오며 프로젝트와 코드를 대하는 마음가짐을 어떻게 설정해야 할 지에 대한 생각의 변화가 생겼다.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/265&quot;&gt;바로 이 글을 통해 생각이 변했다.&lt;/a&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 만들어두고, 계속해서 발전시켜나가야겠다. 언제곤 머릿속에서만 아이디어로 남겨둘 수는 없다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그렇게 생각하는 순간 바로 이 계획을 실행하기로 했다. 간단하게 초안을 만들고, 계속해서 업데이트 해나가는 방식으로&lt;br /&gt;코드를 성장시키기로 했다. 그렇게 만들어진 사이트의 첫 번째 데모영상은 아래에서 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/embed/EE_m0jAGuVw?si=QPYPn-oT5OfFWJzz&quot;&gt;&lt;img src=&quot;https://i.imgur.com/t7xuFP1.png&quot; alt=&quot;Demo&quot; /&gt;&lt;/a&gt;&lt;br /&gt;꽤 만족스러운 사이트긴 했지만, 그럼에도 불편한 부분들이 분명 존재했고, 그들 중 일부 문제를 해결하기 위한 과정을 이 글에서 깊게 다루기로 했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;나는 무엇을 개선해볼 것인가?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;나 자신을 소개하는 whoami 페이지&lt;/li&gt;
&lt;li&gt;18개의 png 형식의 무손실 압축형 사진파일들 -&amp;gt; 개선의 여지가 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/asGzpZz.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 시점의 포트폴리오는 약 5~6개의 페이지로 이루어져 있다. 그리고 여기서 가장 크게 느끼고 있는 불편한 점은 아래와 같았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(첫 방문자 기준) 사진이 많이 들어가는 페이지는 처음 로드될 때 1초를 넘게 기다려야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이후 로드 속도는 NextJS와 Vercel의 캐싱 지원으로 괜찮은 편이지만, 첫 방문자가 많은 이 사이트의 특성상 치명적으로 작용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그리고 사진이 로드되기 전까지 UI 사이즈가 변경되며 약간 정신없어 보이기도 했다.&lt;br /&gt;그렇기에 lighthouse와 네트워크 탭을 통해 지연시간이 어느정도인지 확인해보았다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;lighthouse를 통한 페이지 성능 측정&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Desktop 기준 Navigation 측정&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/zFi70PK.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제작된 지 얼마되지 않은 사이트기에 치명적으로 무언가 크게 잘못 짜둔 코드는 없는 것 같았다.&lt;br /&gt;물론 모든 것이 완벽하다고 나와있긴 하지만 직접 눈에 보이는 문제가 있지 않은가.&lt;br /&gt;바로 초기 로드 속도였다. 그렇게 간단한 페이지가 1000ms 전후로 지연 로드되는 현상이 발생하고 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제상황: 높은 TTFB(Time to First Byte)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시 비활성상태로 측정했을 때 첫 로드시간이 굉장히 오래 걸리는 상황&lt;/li&gt;
&lt;li&gt;아무래도 이미지가 많고, &quot;무손실 압축형 사진파일&quot;인 .png로 구성되어있어서 그런 듯 하다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/jol27ca.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 오류메세지는 초기 로드가 완료되고 다시 측정했을 때 발생하지 않지만, 사용자의 초기 방문 시의 경험이 중요한 포트폴리오 페이지 특성 상 경험에 부정적인 효과를 줄 수 있다고 보았다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/37uwmVb.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 네트워크 탭에서도 초기 로드마다 view부분을 띄우는 데 많은 시간을 소요하는 것을 볼 수 있기에 더욱이 이미지 파일 최적화를 해보아야겠다고 생각했다. 이 부분에서만 0.7초 정도면 전체 로드 시 걸리는 시간은 더욱 많이 소요된다는 의미이기도 하다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/LHp3uHi.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;정확하지는 않겠지만 대략 30%정도의 방문자들이 지연시간으로 인해 이탈하지 않았을까 싶다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/xlCxalN.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 프론트엔드만으로 구성되어있는 프로젝트이긴 하지만 Next.js를 사용하였고, Route Handlers를 통해 이미지들을 불러오고 있기에 서버쪽에서 지연이 걸리고 있는것으로 나타난 듯 하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가설: 이미지 확장자 변경해서 확인해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나에게는 두 가지 선택지가 있었다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Skeleton UI를 적용시켜 UX 개선하기&lt;/li&gt;
&lt;li&gt;사진 파일 크기를 줄여 로드 시간 줄이기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 중 더 빠른 방법으로는 이미지 확장자를 바꿔주는 것이었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 &quot;.png&quot; 파일들을 &quot;.webp&quot;로 변경해서 성능개선을 시도하려고 했다.PNG, WEBP의 차이점은 무엇일까?&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PNG (Portable Network Graphics)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;손실 없는 압축. 원본 이미지의 품질을 그대로 유지하면서 파일 크기를 줄인다.&lt;/li&gt;
&lt;li&gt;투명도 지원, 이미지의 특정 부분을 완전히 투명하게 만들 수 있다.&lt;/li&gt;
&lt;li&gt;주로 로고, 텍스트, 아이콘 같은 고해상도 이미지에 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WEBP&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구글이 개발한 오픈소스 이미지 포맷으로, 손실 압축과 손실 없는 압축 모두 지원한다.&lt;/li&gt;
&lt;li&gt;PNG보다 더 효율적인 압축을 제공하여, 비슷한 품질의 이미지를 더 작은 파일 크기로 만들 수 있 .&lt;/li&gt;
&lt;li&gt;또한 투명도를 지원하며, 애니메이션 이미지 저장도 가능하다.&lt;/li&gt;
&lt;li&gt;웹에서 다양한 유형의 이미지를 효율적으로 사용하고자 할 때 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사이즈 비교해보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 변환 툴은 Figma의 커뮤니티 플러그인인 &quot;WebP Exporter&quot;를 사용했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;PNG&lt;br /&gt;
&lt;div&gt;&lt;img src=&quot;https://i.imgur.com/ODRPDYY.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;WebP&lt;br /&gt;
&lt;div&gt;&lt;img src=&quot;https://i.imgur.com/182ffDf.png&quot; alt=&quot;&quot; /&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;품질 비교(250 X 100)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;현재 웹상에서 띄워지는 크기의 이미지를 기준으로 확인해보았다.&lt;/li&gt;
&lt;li&gt;웹에서는 기재되어있는 크기보다 약간 더 크거나 약간 더 작은 사이즈로 사용된다.&lt;br /&gt;&lt;img src=&quot;https://i.imgur.com/YNA9TjS.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;품질 비교(2500 X 1000)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사이즈를 늘렸을 때의 차이를 비교하기 위해 크기를 10배 더 늘려서 비교해 보았다.&lt;br /&gt;&lt;img src=&quot;https://i.imgur.com/GdqNDtc.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;정리
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;webp 파일의 용량은 png에 비해 &quot;33%&quot; 더 작다.&lt;/li&gt;
&lt;li&gt;webp 파일이 아주 약간 흐리게 보였지만 웹 상에서는 크게 차이나는 정도는 아니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이런 차이점이 있어 WebP를 적용해보고자 하였고, 웹 상에서는 이미지가 어떻게 렌더링되는 것인지도 궁금해서 알아보기로 했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;브라우저의 렌더링을 통해 알아보는 이미지 렌더링&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 브라우저에서 화면을 렌더링하는 과정을 통해 알아보도록 하겠다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/zZ48ESX.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진은 웹상에서 이미지가 렌더링 되는 일련의 과정이다. 좌측부터 추가적인 설명을 붙여가며 알아보겠다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. HTML 파싱&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 먼저, 브라우저는 HTML 문서를 읽고 파싱하여 DOM (Document Object Model) 트리를 구성한다.&lt;/li&gt;
&lt;li&gt;이 과정 중에 이미지 태그(&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;)를 만나면, 이미지의 다운로드를 시작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;여기서 Next.js를 사용하는 내 프로젝트의 경우에는 &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt;컴포넌트를 만나면서 다음과 같은 추가적인 기능들이 적용된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;자동 최적화&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Next.js의 &lt;code&gt;Image&lt;/code&gt; 컴포넌트는 자동으로 이미지를 최적화하여 로드 시간을 단축한다.&lt;/li&gt;
&lt;li&gt;서버 측에서 이미지를 적절한 크기로 리사이즈하고, 지원 가능한 브라우저의 경우 WebP와 같은 포맷으로 자동 변환한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&amp;lt;&amp;lt;&amp;lt;1. 그런데... Next.js의 &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt;태그에서 WebP로 자동 변환해준다고 하지 않았었나?&amp;gt;&amp;gt;&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;&amp;lt;&amp;lt;글의 마지막 부분 &quot;궁금증 해소하기&quot; 부근에서 추가로 다루도록 하겠다.&amp;gt;&amp;gt;&amp;gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지연 로딩 (Lazy Loading)&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;Image&lt;/code&gt; 컴포넌트는 뷰포트에 들어오기 전까지 이미지 로딩을 지연시키는 기능을 내장하고 있다.&lt;/li&gt;
&lt;li&gt;사용자가 스크롤하여 이미지가 화면에 나타날 때까지 로딩을 연기함으로써 초기 페이지 로드 시간을 단축할 수 있다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;그렇기에 &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt;컴포넌트를 사용하면 굳이 레이지 로딩을 적용시킬 필요가 없다. 하지만 알아두면 좋으니 나중에 React에서 연습해볼 예정이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자리 표시자 (Placeholder)&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이미지가 로딩되는 동안 표시될 자리 표시자를 설정할 수 있다.&lt;/li&gt;
&lt;li&gt;이는 사용자에게 이미지가 여전히 로딩 중임을 알리고, 페이지의 레이아웃이 이미지 로딩으로 인해 변경되지 않도록 합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&amp;lt;&amp;lt;&amp;lt;2. 그런데 왜 내 프로젝트에서는 이미지 로드 중에 자리 표시자가 뜨지 않고 레이아웃이 변경되었을까?&amp;gt;&amp;gt;&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;&amp;lt;&amp;lt;글의 마지막 부분 &quot;궁금증 해소하기&quot; 부근에서 추가로 다루도록 하겠다.&amp;gt;&amp;gt;&amp;gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버와의 상호작용&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;Image&lt;/code&gt; 컴포넌트는 Next.js 서버와 상호작용하여 요청된 이미지를 최적화한다.&lt;/li&gt;
&lt;li&gt;이미지 요청이 들어오면, Next.js의 이미지 최적화 API가 해당 이미지를 처리하고 캐시한다.&lt;/li&gt;
&lt;li&gt;이후 동일한 이미지에 대한 요청은 캐시된 버전을 사용하여 빠르게 제공할 수 있다.&lt;/li&gt;
&lt;li&gt;따라서 따로 캐싱 처리를 하지 않아도 된다는 소리이기도 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;많은 최적화 기능들이 Next.js의 &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt;컴포넌트를 통해 이루어진다.&lt;br /&gt;그리고 &quot;4. 이미지 로딩&quot;에서 최적화가 이루어진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. CSS 파싱&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1번과 동시에, CSS 파일 또는 &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; 태그 내부의 스타일 규칙들이 파싱되어 스타일 규칙이 메모리에 저장된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. DOM, 스타일의 결합&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTML과 CSS 파싱이 끝나면, DOM 트리에 스타일 규칙을 적용한다.&lt;/li&gt;
&lt;li&gt;그리고 각 요소에 적용되는 스타일이 계산되어 렌더 트리가 구성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 이미지 로딩&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지 파일은 서버에서 비동기적으로 다운로드되며, 이 과정은 HTML/CSS 파싱과 병렬로 진행될 수 있다.&lt;/li&gt;
&lt;li&gt;이미지가 완전히 로드되면, 이미지의 실제 크기와 메타데이터가 파악된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 렌더 트리 구성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DOM 트리와 스타일 규칙이 결합되어 렌더 트리가 만들어진다.&lt;/li&gt;
&lt;li&gt;이 트리는 실제로 화면에 렌더링될 요소들과 그 스타일을 포함합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 리플로우(Reflow)&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;7. 페인팅(Painting)&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;8. 디스플레이(Display)&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;이 과정을 거치고, 처음 브라우저 렌더링을 살짝 수정해본다면?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 변경된 플로우를 볼 수 있다. 이미지 로딩은 비동기적으로 이루어진다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/04NUgM8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그리고 이 모든것을 하나의 이야기로 만들어서 이해해보자면?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 렌더링을 하나의 큰 연회를 준비하는 것으로 이해할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;나&quot; 자신을 웹브라우저라고 생각해보자. 연회장에서 나에게 연회를 위한 준비를 의뢰했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;HTML 파싱&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;HTML을 파싱하는 것은 연회장에서 보내온 초대장을 열어보는 것과 같다.&lt;/li&gt;
&lt;li&gt;초대장에는 연회의 모든 이벤트와 참가자들이 어떤 순서로 등장하는지에 대해 적혀 있다.&lt;/li&gt;
&lt;li&gt;나(웹 브라우저)는 초대장을 읽으며, 무엇이 필요한지 리스트업 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CSS 파싱&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;연회장을 꾸미기 위한 장식 계획을 세우는 단계이다.&lt;/li&gt;
&lt;li&gt;장식품들이 어디에 어떻게 배치될지, 색상과 테마는 무엇인지를 결정한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DOM과 스타일 규칙의 결합&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;초대장의 정보와 장식 계획이 합쳐져서, 연회장이 어떻게 꾸며질지의 상세한 인테리어(레이아웃)가 나온다.&lt;/li&gt;
&lt;li&gt;각 참가자가 어디에 앉을지, 무대는 어떻게 설치될지 등이 결정 다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이미지 로딩&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;연회장의 큰 그림이 걸릴 예정이다.&lt;/li&gt;
&lt;li&gt;이 그림은 연회장 밖의 스튜디오에서 가져와야 하며, 도착하는 대로 벽에 걸릴 것입니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt; 컴포넌트는 종이에 그려진 그림에 대신 디스플레이를 활용해 벽면을 꾸며주며, 참석자들의 시선이 향해있을 떄만 그림을 띄우도록 하는 등 첨단 기술들을 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;렌더 트리 구성&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;모든 계획이 확정되면, 실제로 연회장을 꾸미기 시작한다.&lt;/li&gt;
&lt;li&gt;장식품들이 정해진 자리에 배치되고, 의자가 줄지어 세워집니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리플로우(Reflow)&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;거대한 그림이 도착하고 벽에 걸리면, 그림의 크기와 위치에 따라 주변의 장식품들을 조금씩 조정해야 할 수도 있다.&lt;/li&gt;
&lt;li&gt;이는 연회장의 전체적인 모습을 가장 좋게 만들기 위해 일어나는 일이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;페인팅(Painting)&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이제 모든 장식이 완료되고, 연회장의 모든 모습이 완성된다.&lt;/li&gt;
&lt;li&gt;마치 화가가 마지막 붓터치를 하는 것처럼, 모든 것이 제 자리를 찾는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디스플레이(Display)&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;마침내, 연회장의 문이 열리고 손님들이 들어오기 시작한다.&lt;/li&gt;
&lt;li&gt;그들은 꾸며진 연회장을 보며 감탄하고, 연회는 화려하게 시작된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증: 이미지를 모두 변환해서 적용해보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 지금부터 이 파일들을 더 작은 크기의 webp 파일로 바꿔서 다시 적용시켜볼 예정이다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ue4TN5M.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;No.&lt;/th&gt;
&lt;th&gt;PNG File&lt;/th&gt;
&lt;th&gt;Size (KB)&lt;/th&gt;
&lt;th&gt;WEBP File&lt;/th&gt;
&lt;th&gt;Size (KB)&lt;/th&gt;
&lt;th&gt;Size Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;D3.png&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;D3.webp&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;-4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Emotion.png&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Emotion.webp&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;-7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Figma.png&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Figma.webp&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Highcharts.png&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Highcharts.webp&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;JavaScript.png&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;JavaScript.webp&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;-4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Jira.png&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Jira.webp&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;NextJS.png&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;NextJS.webp&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Notion.png&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Notion.webp&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Npm.png&lt;/td&gt;
&lt;td&gt;0.98&lt;/td&gt;
&lt;td&gt;Npm.webp&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;+1.02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;React.png&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;React.webp&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;-4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Sass.png&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Sass.webp&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Slack.png&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Slack.webp&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;-4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Svelte.png&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Svelte.webp&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;SvelteKit.png&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;SvelteKit.webp&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;-4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Tailwind.png&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Tailwind.webp&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Three.png&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Three.webp&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;TypeScript.png&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;TypeScript.webp&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;-4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;Yarn.png&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Yarn.webp&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Total Size of PNG Images:&lt;/b&gt; 160.98 KB&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Total Size of WEBP Images:&lt;/b&gt; 112 KB&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Total Size Reduction:&lt;/b&gt; 48.98 KB&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Percentage Total Reduction:&lt;/b&gt; Approximately 30.43%&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;&lt;b&gt;결과에 따라서 초기 렌더링 속도가 30% 이상 단축될 것으로 보인다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;적용&lt;/h2&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&gt;&lt;img src=&quot;https://i.imgur.com/Bb9aF4A.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론: 성능측정(좌측(이전), 우측(이후))&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;723.08ms -&amp;gt; 472.35ms
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;약 34% 단축&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/MjquTGG.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 캐시 미적용 옵션으로 측정하였으며, 약 34%의 딜레이 감소가 이루어져 예상했던 결과보다 더 높은 성능 개선을 이루어냈다.&lt;br /&gt;실제로도 체감될 정도로 빠르게 로드되었기에 사용자 경험적인 측면에서도 더욱 좋은 결과를 내었다.&lt;br /&gt;&lt;br /&gt;이전에는 캐싱이 되어있었음에도 불구하고 로드되는 시간이 체감되었다면, 이제는 즉시 로딩된다는 느낌을 주고 있다.&lt;br /&gt;&lt;br /&gt;이 시도로 성능 개선을 성공적으로 마쳤으니, 다른 파일 또한 webp로 변경하여 전체적인 성능 개선을 이루어낼 예정이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;작성하면서 추가로 남아있던 궁금증 해소하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 내용들을 적으면서 무언가 분명히 궁금했던 내용들이 있었다. 그리고 여기에서 알아보는 내용들을 결론까지 모두 보고 알아보면 더 좋을 것 같은 내용이므로 여기서 마무리하기 전에 정리해서 알아보도록 하겠다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 그런데... Next.js의 &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt;태그에서 WebP로 자동 변환해준다고 하지 않았었나?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 궁금해서 프로젝트에 설치되어있는 Next.js의 코드 중 &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt; 컴포넌트 코드를 뜯어보기로 했다.&lt;br /&gt;들어간 경로는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Gzkx0L1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;nex/image로 이동, 누르면 image.d.ts 파일로 진입&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/EpsYKKv.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;./dist/shared/lib/image-external로 이동, 누를 경우 image-external.d.ts 파일로 진입&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ZyEwZ4v.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;./image-config로 이동, 누를 경우 image-config.d.ts 파일로 이동&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서 &quot;declare&quot;이라는 키워드가 있는데, Typescript로 개발하던 중에 아주 잘 작성된 JS 모듈을 가져와서 쓰고자 한다면, 해당 모듈 내의 함수는 typescript로 정의되어있지 않기 때문에 컴파일 과정에서 오류가 발생한다.&lt;/li&gt;
&lt;li&gt;모듈이 해당 속성을 가지고 있음에도 type이 정해져있지 않으면 해당 모듈을 가지고 있지 않다고 해석하는 현상이 발생한다.&lt;/li&gt;
&lt;li&gt;declare 키워드를 써서 정의해두면 type이 인식되어 typescript compiler가 해석할 수 있게 된다.&lt;/li&gt;
&lt;li&gt;간단히 말해서 컴파일러에게 &quot;이건 이미 존재하고 다른 코드에서 참조할 수 있다.&quot;라고 설명하는 것이다.&lt;br /&gt;&lt;br /&gt;그렇게 3개의 파일을 거쳐가면 webp를 다루고 있는 코드를 볼 수 있다.&lt;br /&gt;&lt;img src=&quot;https://i.imgur.com/a1ahLq4.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;여기서 이미 avif 또는 webp로 제한되고 있는 파일의 형식을 볼 수 있으며, 다른 파일의 경우 해당 형식으로 변환해서 출력하는 듯 하다.&lt;br /&gt;&lt;br /&gt;하지만 변환 과정 자체가 서버 리소스를 사용하는 일이므로 &amp;lt;기존 이미지 다운로드 + 이미지 최적화(변환)&amp;gt; 시간의 합이 적용되어 더 오래 걸린 것으로 보고 있다. 따라서 처음부터 &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt; 컴포넌트에서 지정해둔 format인 webp 파일이 다운로드되고 이미지 변환 과정을 스킵하면서 더욱 빠른 로드 속도를 낼 수 있는 것 같다. 물론 파일 크기가 줄어든 것도 많은 영향을 미친 것으로 보인다.2. 그런데 왜 내 프로젝트에서는 이미지 로드 중에 자리 표시자가 뜨지 않고 레이아웃이 변경되었을까?이미지 로드 중에 자리 표시자가 뜨고 있지는 않는 이유는 아마 프로젝트의 로드 방식으로 인한 것이라고 생각된다.&lt;br /&gt;작성되어있는 코드를 보며 그 이유를 살펴보겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;      &amp;lt;TechStack&amp;gt;
        &amp;lt;div className='flex flex-col gap-2 md:gap-8 md:flex-row'&amp;gt;
          {techStackData.map((column, columnIndex) =&amp;gt; (
            &amp;lt;div key={columnIndex} className='flex flex-col gap-2 md:gap-4'&amp;gt;
              {column.map((stack, stackIndex) =&amp;gt; (
                &amp;lt;StackInfo key={stackIndex} category={stack.category}&amp;gt;
                  {stack.items.map((item, itemIndex) =&amp;gt; (
                    &amp;lt;TechStackItem key={itemIndex} imgSrc={item.imgSrc} /&amp;gt;
                  ))}
                &amp;lt;/StackInfo&amp;gt;
              ))}
            &amp;lt;/div&amp;gt;
          ))}
        &amp;lt;/div&amp;gt;
      &amp;lt;/TechStack&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;lt;TechStack&amp;gt;&lt;/code&gt; 라는 컨테이너 역할을 하는 컴포넌트 안에 자식 컴포넌트를 렌더링 하는 방식으로 데이터가 불러와지고 있다.&lt;br /&gt;문제는 여기서 2중 반복문을 통해 하나씩 호출이 되는데, 내부에서 &lt;code&gt;&amp;lt;TechStackItem&amp;gt;&lt;/code&gt; 컴포넌트에 이미지가 들어가있기에 다음과 같이 렌더링된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;TechStack&amp;gt;&lt;/code&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;StackInfo&amp;gt;&lt;/code&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;TechStackItem&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;TechStackItem&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;TechStackItem&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;StackInfo&amp;gt;&lt;/code&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;TechStackItem&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;TechStackItem&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;TechStackItem&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;StackInfo&amp;gt;&lt;/code&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;TechStackItem&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;TechStackItem&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;TechStackItem&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;...&lt;br /&gt;그렇기에 이미지 하나하나가 로딩이 늦어질 수록 이후의 컴포넌트들 또한 로딩이 늦어지기에 이 부분에 대해서는 개선이 되면 좋을 것 같다고 본다.&lt;br /&gt;어쩌면 HTML 코드의 길이가 마냥 짧다고 좋은 것은 아닌 것 같다고 느꼈다. 나중에 구조를 조정하여 개선해봐야겠다.&lt;br /&gt;&lt;br /&gt;많은 것들을 배우고 얻어갈 수 있는 시간이었다. 글을 마치겠다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;References&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/19185855/how-do-browsers-render-images&quot;&gt;https://stackoverflow.com/questions/19185855/how-do-browsers-render-images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hini7.tistory.com/227&quot;&gt;https://hini7.tistory.com/227&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>(3D)Dev Deep Dive/Frontend origin</category>
      <category>image</category>
      <category>Image Component</category>
      <category>nextjs</category>
      <category>PNG</category>
      <category>TimeMap</category>
      <category>WebP</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/268</guid>
      <comments>https://time-map-installer.tistory.com/268#entry268comment</comments>
      <pubDate>Mon, 18 Dec 2023 20:13:22 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript ES6] function*? yield? 이거 자바스크립트 맞아?? 제너레이터 함수(Generator Functions)를 알아보자</title>
      <link>https://time-map-installer.tistory.com/267</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgmweB/btsBKjC165T/7vEG7CJlAsRqO7eFTDVU1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgmweB/btsBKjC165T/7vEG7CJlAsRqO7eFTDVU1K/img.png&quot; data-origin-width=&quot;225&quot; data-origin-height=&quot;225&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.7123%; margin-right: 10px;&quot; data-widthpercent=&quot;50.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgmweB/btsBKjC165T/7vEG7CJlAsRqO7eFTDVU1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgmweB%2FbtsBKjC165T%2F7vEG7CJlAsRqO7eFTDVU1K%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;225&quot; height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LqF3g/btsBQUvIdPr/p8RvOk8UjSTgPIY835Uxc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LqF3g/btsBQUvIdPr/p8RvOk8UjSTgPIY835Uxc1/img.png&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;1862&quot; data-is-animation=&quot;false&quot; width=&quot;225&quot; height=&quot;228&quot; style=&quot;width: 49.1249%;&quot; data-widthpercent=&quot;49.7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LqF3g/btsBQUvIdPr/p8RvOk8UjSTgPIY835Uxc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLqF3g%2FbtsBQUvIdPr%2Fp8RvOk8UjSTgPIY835Uxc1%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;1840&quot; height=&quot;1862&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이 글은 코딩 테스트를 풀다가 다른 사람들의 풀이를 확인하던 도중 신기한 코드를 발견해서 알아본 내용들을 정리한 글이에요. 정말 신기하게 생겨서 공유하고자 이 글을 작성합니다.&lt;/p&gt;
&lt;h3&gt;제너레이터 함수란 무엇일까요?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;이런 상상을 해봐요. 평화로운 오후, 당신은 서재에서 시간을 보내고 있었어요.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;평화롭게 읽고싶었던 책을 읽고 있었는데, 갑자기 중요한 전화가 온 거에요.&lt;br&gt;책에 책갈피를 꽂아두고 잠시 덮어두고 전화를 받았어요. 업무 관런해서 질문이 들어왔었네요.&lt;br&gt;당신은 대답을 해주고 다시 책을 펼쳐 정확히 읽던 부분부터 읽기 시작했습니다.&lt;br&gt;&lt;br&gt;제너레이터 함수도 이와 비슷하게 돌아가요. 함수의 실행을 &amp;#39;일시 중지&amp;#39;했다가 &amp;#39;다시 시작&amp;#39;할 수 있다는 것이죠!&lt;/p&gt;
&lt;h3&gt;어떻게 작동할까요?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;function 키워드를 사용합니다. 하지만 조금 다르게 쓰이죠. 예시와 함께 알아 볼까요?&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function* storyTeller() {
    yield &amp;#39;오랜 옛날, 멀고 먼 왕국에...&amp;#39;;
    yield &amp;#39;용감한 기사와 똑똑한 공주가 있었어요.&amp;#39;;
    return &amp;#39;그리고 그들은 오래오래 행복하게 살았답니다.&amp;#39;;
}

const story = storyTeller();
console.log(story.next().value); // &amp;#39;오랜 옛날, 멀고 먼 왕국에...&amp;#39;
console.log(story.next().value); // &amp;#39;용감한 기사와 똑똑한 공주가 있었어요.&amp;#39;
console.log(story.next().value); // &amp;#39;그리고 그들은 오래오래 행복하게 살았답니다.&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;제너레이터 함수는 &lt;code&gt;function*&lt;/code&gt; 키워드를 사용해 정의해요.&lt;br&gt;이 함수 내부에서는 &lt;code&gt;yield&lt;/code&gt; 키워드를 사용해 함수의 실행을 중단할 수 있죠.&lt;br&gt;&lt;code&gt;yield&lt;/code&gt;를 만나면, 함수는 그 부분의 결과를 반환하고 실행을 멈춰요.&lt;br&gt;나중에 다시 실행을 한 번 더 하면? &lt;strong&gt;정확히 멈췄던 지점부터 다시 시작해요.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;제너레이터 함수의 용도는 무엇일까요?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;여러 방법으로 사용될 수 있어요. 생각보다 신기할거에요.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;ol&gt;
&lt;li&gt;비동기 프로그래밍&lt;ul&gt;
&lt;li&gt;제너레이터 코드는 비동기 코드를 마치 동기 코드처럼 쉽게 작성할 수 있게 해줘요.&lt;/li&gt;
&lt;li&gt;콜백이나 프로미스가 복잡한 작업을 수행하는 코드를 단순화할 수 있어요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// 제너레이터 함수 정의
function* fetchUserSequence() {
    // 첫 번째 API 호출: 사용자 데이터 불러오기
    const user = yield fetch(&amp;#39;/api/user&amp;#39;);
    // 두 번째 API 호출: 해당 사용자의 프로필 불러오기
    const profile = yield fetch(`/api/profile/${user.id}`);
    // 세 번째 API 호출: 해당 사용자의 게시물 불러오기
    const posts = yield fetch(`/api/posts/${user.id}`);

    // 모든 데이터 반환
    return { user, profile, posts };
}

// 제너레이터 실행 및 제어 함수
const iterator = fetchUserSequence();
function handleYield(yielded) {
    if (!yielded.done) {
        yielded.value.then(result =&amp;gt; {
            // 다음 yield까지 실행
            handleYield(iterator.next(result));
        });
    } else {
        // 모든 작업 완료, 결과 출력
        console.log(&amp;#39;모든 데이터를 불러왔습니다:&amp;#39;, yielded.value);
    }
}
handleYield(iterator.next());&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;데이터 스트림 처리&lt;ul&gt;
&lt;li&gt;대용량 데이터를 효율적으로 처리할 수 있어요.&lt;/li&gt;
&lt;li&gt;필요할 때만 데이터를 생성하기 때문에 메모리 사용을 최적화할 수 있어요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function* infiniteNumbers() {
    let n = 1;
    while (true) {
        yield n++;
    }
}

const numbers = infiniteNumbers(); // 무한 시퀀스 생성
console.log(numbers.next().value); // 1
console.log(numbers.next().value); // 2
// 계속해서 다음 숫자를 생성할 수 있지만, 한 번에 하나씩만 메모리에 적재됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;복잡한 로직 제어&lt;ul&gt;
&lt;li&gt;복잡한 반복이나 재귀적인 방법들 조금 더 관리하기 쉽게 구현할 수 있어요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// 트리 순회를 위한 제너레이터 함수
function* traverseTree(node) {
    // 현재 노드의 값을 반환
    yield node.value;
    // 왼쪽 자식 노드가 있다면 왼쪽 자식 노드를 순회
    if (node.left) {
        yield* traverseTree(node.left);
    }
    // 오른쪽 자식 노드가 있다면 오른쪽 자식 노드를 순회
    if (node.right) {
        yield* traverseTree(node.right);
    }
}

// 트리 구조 예시
const tree = { 
    value: 1, 
    left: { value: 2 },
    right: { value: 3 }
};

// 제너레이터를 사용하여 트리 순회
for (let value of traverseTree(tree)) {
    console.log(value); // 1, 2, 3 순서로 출력
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;중첩된 n차원 배열을 1차원 배열로 바꾸는 경우&lt;ul&gt;
&lt;li&gt;이 문제를 풀다가 발견하게 된 코드로, 신기함을 느끼고 있어요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const inorderTraversal = function* (arr) {
  // 중첩 배열을 순회하는 도우미 제너레이터 함수
  function* traverse(node) {
    // node가 배열인 경우, 각 서브 배열에 대해 재귀적으로 traverse 호출
    if (Array.isArray(node)) {
      for (let subArray of node) {
        yield* traverse(subArray);
      }
    } else {
      // 배열이 아닌 요소는 그대로 yield(반환)
      yield node;
    }
  }

  // 주어진 배열에 대해 중위 순회 시작
  yield* traverse(arr);
};
/**
 * 사용 예:
 * const gen = inorderTraversal([1, [2, 3]]);
 * gen.next().value; // 1
 * gen.next().value; // 2
 * gen.next().value; // 3
 *
 * 이 코드는 [1, [2, 3]]과 같은 중첩 배열을 입력으로 받아,
 * 1, 2, 3 순서로 각 요소를 하나씩 반환하는 제너레이터를 생성합니다.
 * gen.next()를 호출할 때마다 배열의 다음 요소를 반환합니다.
 */&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;기억해 두세요!&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;제너레이터 함수는 책갈피와 같아요!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;제너레이터 함수는 책을 읽는 것과 같아서 yield라는 책갈피를 가지고 있죠.&lt;br&gt;또는 마리오 게임처럼 여러 세이브포인트를 가지고 있는 것과 같다고 볼 수도 있어요.&lt;br&gt;아무튼 중간에 저장할 수 있는 것이 있는 어떤 것이든 제너레이터 함수처럼 작동한다고 생각해보세요!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;End&lt;/strong&gt;&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>function*</category>
      <category>JavaScript</category>
      <category>yield</category>
      <category>제너레이터 함수</category>
      <category>책갈피</category>
      <category>함수</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/267</guid>
      <comments>https://time-map-installer.tistory.com/267#entry267comment</comments>
      <pubDate>Tue, 12 Dec 2023 14:40:15 +0900</pubDate>
    </item>
    <item>
      <title>React Native Expo Navigation 설정하기 (+TypeScript)</title>
      <link>https://time-map-installer.tistory.com/266</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;들어가며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native Expo를 통해 프로젝트를 생성했다면, 이제 각각의 화면마다 이를 넘겨주는 기능이 필요하다. 이에 TypeScript와 함께 사용하는 방법을 보도록 하겠다.&lt;br /&gt;&lt;br /&gt;React에 React Router DOM이 있듯이 React Native에는 react-navigation이 있다.&lt;br /&gt;이 글에서는 프로젝트를 생성한 상태에서 &quot;home&quot;, 그리고 &quot;login&quot;을 만들어 두 페이지끼리 이동하도록 해볼 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; 1. Install Packages&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 네비게이션과 관련되어있는 모듈들을 설치한다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# 이 부분은 이상하게 npx expo를 통해 설치하면 오류가 발생한다.
yarn add @react-navigation/stack
# 나머지 패키지 설치(expo cli 사용)
npx expo install react-native-screens react-native-safe-area-context&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Set Navigation: App.tsx&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 Native 프로젝트를 생성하면 처음에 &quot;App.tsx&quot; 라는 파일을 볼 수 있을 것이다.&lt;br /&gt;이 파일에 네비게이션을 세팅해두겠다.&lt;br /&gt;&lt;br /&gt;먼저, 필요한 모듈들을 &quot;import&quot;해온다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;// App.tsx
import { NavigationContainer } from &quot;@react-navigation/native&quot;;
import { createNativeStackNavigator } from &quot;@react-navigation/native-stack&quot;;
import React from &quot;react&quot;;
import LoginPage from &quot;./screens/login&quot;;
import Home from &quot;./screens/home&quot;;
import { RootStackParamList } from &quot;./types/navigationTypes&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이후 &quot;Stack&quot;을 선언해 네비게이션 사용을 위한 스택을 생성해준다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;// App.tsx
const Stack = createNativeStackNavigator&amp;lt;RootStackParamList&amp;gt;();
// RootStackParamList는 Navigation에 들어가는 스크린에 대한 파라미터로, App.tsx 함수 이후 보여줄 예정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이제 메인 함수에 네비게이션 세팅을 해둔다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;// App.tsx
export default function App() {
  return (
    &amp;lt;NavigationContainer&amp;gt;
      &amp;lt;Stack.Navigator&amp;gt;
        &amp;lt;Stack.Screen name='Login' component={LoginPage} /&amp;gt;
        &amp;lt;Stack.Screen name='Home' component={Home} /&amp;gt;
      &amp;lt;/Stack.Navigator&amp;gt;
    &amp;lt;/NavigationContainer&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;여기서 중요한 점은 &amp;lt;Stack.Navigator&amp;gt; 안에 있는 페이지가 서로 영향을 받는다는 것이다.&lt;br /&gt;만약 앱을 켰을 때 &quot;Home&quot; 페이지를 먼저 띄우게 하고 싶다면 두 순서를 변경하면 된다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;// App.tsx
&amp;lt;Stack.Screen name='Home' component={Home} /&amp;gt;
&amp;lt;Stack.Screen name='Login' component={LoginPage} /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&quot;RootStackParamList&quot;는 위에서 지정해둔 스택들에 대한 타입으로, 한 화면에서 다른 화면으로 넘어가기 위해 미리 설정해두는 타입니다.&lt;br /&gt;타입의 내용들은 아래와 같이 구성되어있다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;// types/navigationTypes.ts
export type RootStackParamList = {
  Home: undefined;
  Login: undefined;
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Set Navigation: Login.tsx &amp;amp; Home.tsx&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제부터 위에 구성된 &quot;Home&quot;, 그리고 &quot;LoginPage&quot;에서 아주 간단하게 버튼을 통해 스크린을 이동시켜볼 것이다.&lt;br /&gt;&lt;br /&gt;그럼 아래와 같이 &quot;Login.tsx&quot;와 &quot;Home.tsx&quot;를 작성해보자.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// /screens/login.tsx
import React from &quot;react&quot;;
import { View, Text, Button } from &quot;react-native&quot;;
import { StackNavigationProp } from &quot;@react-navigation/stack&quot;;
import { RootStackParamList } from &quot;../types/navigationTypes&quot;;

// 네비게이션 prop 타입 정의
type LoginScreenNavigationProp = StackNavigationProp&amp;lt;
  RootStackParamList,
  &quot;Login&quot;
&amp;gt;;

type LoginProps = {
  navigation: LoginScreenNavigationProp;
};

const LoginPage: React.FC&amp;lt;LoginProps&amp;gt; = ({ navigation }) =&amp;gt; {
  return (
    &amp;lt;View style={{ flex: 1, alignItems: &quot;center&quot;, justifyContent: &quot;center&quot; }}&amp;gt;
      &amp;lt;Text&amp;gt;Login Page&amp;lt;/Text&amp;gt;
      &amp;lt;Button title='Go to Home' onPress={() =&amp;gt; navigation.navigate(&quot;Home&quot;)} /&amp;gt;
    &amp;lt;/View&amp;gt;
  );
};

export default LoginPage;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// /screens/home.tsx
import React from &quot;react&quot;;
import { View, Text, Button } from &quot;react-native&quot;;
import { StackNavigationProp } from &quot;@react-navigation/stack&quot;;
import { RootStackParamList } from &quot;../types/navigationTypes&quot;;

type HomeScreenNavigationProp = StackNavigationProp&amp;lt;RootStackParamList, &quot;Home&quot;&amp;gt;;

type HomeProps = {
  navigation: HomeScreenNavigationProp;
};

const Home: React.FC&amp;lt;HomeProps&amp;gt; = ({ navigation }) =&amp;gt; {
  return (
    &amp;lt;View style={{ flex: 1, alignItems: &quot;center&quot;, justifyContent: &quot;center&quot; }}&amp;gt;
      &amp;lt;Text&amp;gt;Home Page&amp;lt;/Text&amp;gt;
      &amp;lt;Button
        title='Go to Login'
        onPress={() =&amp;gt; navigation.navigate(&quot;Login&quot;)}
      /&amp;gt;
    &amp;lt;/View&amp;gt;
  );
};

export default Home;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;모두 멋지게 작성하고 나면 이제 실제로 돌아가는 지 확인해 볼 차례이다.&lt;br /&gt;아래의 커맨드를 통해 실행시키고 확인해보자.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;npx expo start&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Result&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 모두 멋진 네비게이션 기능을 만들어보자!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/pUdO0MZ.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Mobile</category>
      <category>expo</category>
      <category>Navigation</category>
      <category>react native</category>
      <category>TypeScript</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/266</guid>
      <comments>https://time-map-installer.tistory.com/266#entry266comment</comments>
      <pubDate>Thu, 7 Dec 2023 17:55:07 +0900</pubDate>
    </item>
    <item>
      <title>같은 개발자에게서 클린코드 컨퍼런스를 두 번 듣고온 프론트엔드 엔지니어 꿈나무가 생각하는 클린코드란?</title>
      <link>https://time-map-installer.tistory.com/265</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2534&quot; data-origin-height=&quot;2048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AKJaa/btsBbPoXLLd/2ttCrqWV3yMFLsKNFRbA20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AKJaa/btsBbPoXLLd/2ttCrqWV3yMFLsKNFRbA20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AKJaa/btsBbPoXLLd/2ttCrqWV3yMFLsKNFRbA20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAKJaa%2FbtsBbPoXLLd%2F2ttCrqWV3yMFLsKNFRbA20%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;2048&quot; data-origin-width=&quot;2534&quot; data-origin-height=&quot;2048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;'클린 코드'가 코드에 대한 책이 아닌 사실에 대하여&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/IXdgBn1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;책이 아니라고요? 그렇다네요.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;왜 두번이나 다녀왔어요?&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 학교에서의 회의를 마치고 클린코드에 대한 2번째 컨퍼런스에 다녀왔습니다.&lt;br /&gt;첫 번째는 11월 22일에 다녀왔던 GOORM COMMIT에서, 그리고 두 번째는 오늘 다녀온 튜링의 사과에서 진행된 강연이었습니다.&lt;br /&gt;첫 번째 클린 코드 관련 강연에 갔을 때에는 클린코드가 무언인지 궁금해서 갔고, 그리고 두 번째는 그래서 코드를 어떻게 대해야 하는 지 궁금했기에 다녀왔습니다.&lt;br /&gt;물론 두 곳 모두 왕복 약 80정거장이라는 정말 긴 거리를 다녀오긴 했지만, 그래도 처음과 두 번째가 모두 얻어가는 것이 있었습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;결론부터 말씀드리자면 클린코드에 대한 생각 뿐만 아니라 코딩을 대하는 근본적인 생각까지도 변화했다고 볼 수 있습니다.&lt;br /&gt;이 글은 과연 클린코드가 무엇인지, 그리고 이걸 어떻게 대해야 하는지에 대해 2번의 같은 주제의 컨퍼런스를 듣고 작성한 글임을 알립니다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;그리고 판교는 예뻤습니다&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/S4pdbSt.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이것이.. 판교? 너무 도시가 예뻤어요. 확실한건 여기 살면 매일 자전거 타러 나올 수 있습니다.&lt;br /&gt;물론 날씨이슈가 있다면 조금 힘들겠지만요&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;튜링의 사과(in 뚝섬)도 예뻤습니다&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/wXIWYT3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;강연을 진행하신 박성철 개발자님과 함께 찍은 사진입니다! 두 번 뵙고 찍어서 그런지 기분도 두 배로 좋네요&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;튜링의 사과는 서점 + 작업공간이 공존하는 그런 느낌이었는데 분위기가 너무 좋았습니다.&lt;br /&gt;보증금 환급 혹은 작업공간 종일 이용권을 선택할 수 있었는데, 2시간정도 걸리는 거리로 인해 환급을 받아 조금 아쉬웠네요&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;환급금으로는 맛있는 붕어빵을 사먹어야겠어요.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;요즘 클린코드 핫합니다. 그렇지 않나요?&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;요즘 클린코드는 굉장히 많이 퍼져있는 개념입니다. 개발을 시작한지 1년도 되지 않은 저에게도 친숙한 것을 보면 정말 그렇죠.&lt;br /&gt;클린코드가 어떻게 해서 유명해졌을까 궁금했었는데, 아래와 같은 과정을 통해서 유명해지지 않았을까 싶네요.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;코딩의 미래: AI가 발전하면서 코딩은 필요없어지지 않을까?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩은 &quot;요구조건을 상세히 표현하는 것&quot;이다. 그리고 코드는 &quot;그 조건을 표현하는 수단이다.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 말은 무슨 소리일까요? 코딩은 요구조건을 상세히 표현하는 수단입니다. 그렇기에 아무리 AI가 발전한다고 해도 사라지지는 않을 겁니다.&lt;br /&gt;이전까지만 해도 저는 GPT와 같은 AI가 계속해서 발전하다보면 어느 순간 코딩이 필요가 없는 세상이 오지 않을까 싶었습니다.&lt;br /&gt;하지만 컨퍼런스 이후로는 이 생각이 변화하였는데, 이는 다음과 같았습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;코드는 결국 받아온 요구조건을 상세하게 표현하기 위한 수단으로 요구조건을 아무리 AI라 하더라도 도움은 될 뿐 결국 이를 다루는 사람이 반드시 필요하기 마련입니다. 그래서 개발자는 사라지지 않을 것이며, 이러한 요구조건을 상세하게 구현해내며 일어나는 모든 문제를 해결하는 &quot;해결사&quot;의 역할로 남아있지 않을까 싶습니다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;그래서 &quot;코더&quot;는 예전과는 다른 의미를 띄게 됩니다.&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 생각하는 코더, 그저 코드만 입력하는 사람이었죠.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;기존에 깔려있는 인식을 살펴봅시다. 코더란 무엇일까요? 아마 많은 분들이 생각하시기에 &quot;부여된 요구조건을 그대로 코드로 옮기는 사람&quot;이라는 그저 주는 대로만 구현해내는 쉬운 일만을 하는 사람이라고 보실 수도 있습니다.&lt;br /&gt;하지만 여기서 말하는 코더는 설계하는 사람으로서의 코더입니다. 우리가 흔히 생각하는 개발자인것이죠.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이미 생성형 AI들이 등장함에 따라 단순히 주어진 태스크들을 수행만 하는 역할들 대부분이 대체되었습니다. 그러면 다른 역할이기도 했던 기존에 프로그램에 대한 설계를 하고 실제로 코드로 옮겨내는 작업들이 개발자들의 주 업무로 변화하기 시작했습니다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;개발자는 예전과 다르게 어떻게 바뀌었을까요?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단합니다. 개발자는 이제 설계하는 것에 더 많은 시간을 쏟게 되었습니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;간단하고 반복적인 코드작업들을 더 이상 하지 않아도 되면서 개발자들을 더욱 좋은 설계, 그리고 더욱 좋은 코드를 위한 방법에 집중하기 시작했습니다. 이전까지는 어떻게든 코드를 옮기기만 했던 시간들이 대부분이었다면, 이제는 코드를 어떻게 더 효과적이고, 성능 좋고, 확장성이 좋도록 작성할 수 있을까에 대한 고민을 하기 시작했습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그렇게 설계에 대한 중요성, 좋은 코드에 대한 중요성이 드러나기 시작하면서 &quot;클린 코드&quot;에 대한 관심이 증가해 왔습니다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;그런데, 클린코드는 결국 리팩터링이 아닌가요?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 르블랑의 법칙과 보이스카우트의 법칙을 알고 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;클린 코드를 알고 있다면 유명한 두 가지 법칙을 떠올리실 수 있습니다. 한 가지는 르블랑의 법칙이고, 다른 하나의 법칙은 보이스카우트의 법칙입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;르블랑의 법칙: 나중은 결코 오지 않는다. 지금 좋은 것을 사용하라&lt;/b&gt;&lt;br /&gt;&lt;b&gt;보이스카우트 규칙: 언제나 처음 왔을 때보다 깨끗하게 해놓고 캠프장을 떠날 것&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이런 법칙들을 통해 보았을 때 아얘 처음부터 코드 작성 시작 단계에서부터 완전히 깨끗하게 하는 것이 가장 이상적인 프로그램이다? 라는 생각으로 인해 &quot;아, 일단 정말 신중하게 엄청 잘 설계해두고 코드를 만들어야겠다&quot;라고 생각할 수도 있을 것입니다. 그리고 그것이 바로 클린코드가 아닐까 하는 생각도 들고요.&lt;br /&gt;하지만 어떤 방법이 좋은 설계일 지 잘 모를 뿐더러 현실 세계에 퍼져있는 수많은 유동성 가득한 문제상황들은 이런 완벽한 설계를 한순간에 완벽하지 않은 설계로 만들어버리기도 합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 생각해보면 어떨까요? &quot;왜 '일단 돌아가는 쓰레기부터 만들어라'라는 말이 등장했을까?&quot;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;일단 돌아가는 쓰레기부터 만들어라&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 돌아가는 쓰레기부터 만들어라. 이 말은 결국 일단 현 상태에서 문제를 이해한 수준으로 먼저 만들라는 말과 같다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;어떤 문제던 사람이 한번에 이해할 수 있는 일정 수준은 정해져있다고 생각합니다. 특히 지금의 저와 같은 개발을 배워나가는 학생의 경우는 더더욱 그러하죠. 그렇기에 처음부터 뭘 따로 설계하고 잘 짜려고 하는 것보다는 그저 코드를 만들어서 잘 돌아가는 것을 확인하고, 그 후 이해되는만큼만 나눠서 더 좋게 만들고, 그것을 반복하는 것이 더 좋은 방법이지 않을까 하죠.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;요즘 말하는 개발자, 프로그래머의 역할이 설계자쪽에 점점 가까워지면서 처음부터 좋은 설계를 해서 잘 만들어야 할 것만 같은 그런 추세를 보이고 있습니다. 거기에는 요즘 많이 팔리고 있는 클린 코드와 관련된 책들도 들어가있죠. 예를 들어서 클린 코드가 아니라면 개발을 하지 않으려는 사람도 있을 것이고, 처음부터 코드를 잘 짜면 리팩터링을 안해도 되는 것 아니냐는 것이죠.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;어느정도 맞는 말입니다. 레거시 코드에 상당히 많은 사람들이 고통을 받고 있기도 하고, 처음부터 완벽한 코드를 작성하면 리팩터링을 할 필요도 없으니까요.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이 말이 정말일까요? 저희는 정말 간단한 요구조건이 아닌 이상 현실과 연관되어있는 그 어떤 문제에 대한 요구조건을 명확하게 이해하는 것을 어려워합니다. 현실은 수많은 변수들로 구성되어있고, 변화하는 변수들을 모두 예상하고 코드를 짜는 것이란 불가능에 가까우니까요.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;그리고 리팩터링 하라&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돌고돌아 나온 다음 방법은 이겁니다. 그리고 리팩터링을 하는 겁니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그렇게 다시 돌고 돌아서 리팩터링을 하는 것이 방법이라는 방향으로 나아가고 있습니다. 코드라는 것은 처음부터 만들어지는 &quot;공산품&quot;이 아닌 계속해서 자신을 포식하고 성장하는 &quot;생물&quot;과 같으니까요. 많은 분들이 헷갈리시는 것들 중 하나가 바로 이 &quot;리팩터링(refactoring)&quot;이라는 것의 어원입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 이 리팩터링은 무엇이길래 어디서부터 그 개념이 의도와는 다르게 전파된 것일까요?&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;리팩터링, Factory가 아닌 Factoring!&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리팩터링에서의 Factoring은 우리가 알고있는 Factory와는 그 의미가 다릅니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;우리가 알고있는 그 리팩터링을 생각해볼까요? 가장 친숙한 단어인 Factory가 떠오르실 겁니다. 그래서 &quot;아, 그래서 Factory니깐 잘 만드는, 공장에서 만들어내는 그런 느낌으로 새로운 코드를 짜는거구나!&quot; 라고 인식하고 있으실 텐데요, 단어부터 다시 살펴보고 가볼까요?&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 Factory는 공장, 그리고 같은 어원이긴 하지만 Factoring은 인수분해를 의미합니다. 엄연히 다른 뜻이 되었죠.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/6Pv1Ceo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;정말 그렇습니다. 인수분해를 의미하기에 이를 코드에 적용해본다면 문제를 분해하고, 다시 결합하는 과정을 나타냅니다.&lt;br /&gt;그말인 즉슨, 같은 의미를 뜻하는 단어인 decomposition이라는 말로도 나타낼 수 있으며 &quot;재설계&quot;라고 번역할 수 있습니다.&lt;br /&gt;처음에 이렇게 번역되어 들어왔다면 덜 헷갈리지 않았을까 싶기도 하네요.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;리팩터링을 하기 위해서는 무언가 만들어진 코드베이스가 필요하다는 것이고 그걸 이해하는 순간 적용하면 된다는 겁니다!&lt;br /&gt;그런데 한가지 의문점이 있습니다. 코드 짜기에도 바쁜데, 리팩터링은 언제 하는 것이 좋을까요?&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;코딩비둘기로 살펴보는 &quot;언제 리팩터링을 해야하는가?&quot;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리팩터링은 눈에 보였을 떄 하는게 가장 좋습니다. 날 잡고 하기보다는 코드 보다가 조금씩 수정해나가는 것이지요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;사실 새로운 기능을 작성하다가도 눈에 보이면 코드를 살짝 수정해두는 것이 리팩터링이고, 코드를 작성하다가 반복적으로 고통을 받으면 그때 리팩터링을 하기도 하고, 정말 다양합니다. 저도 겪어본 일이지만 날 잡고 리팩터링한다고 그러고 있다가 결국 제대로 무언가를 하지도 못하고 끝난 적도 많았기에 정말 추가적으로 코딩을 하다가 발견한 문제가 있으면 그때그떄 수정해두는 것이 좋은 방법이 아닐까 싶습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그 비둘기 짤이 있죠. 이걸로 비유를 해볼게요.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Japvn7k.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;저희는 비둘기를 개발하는 개발자로서 &quot;비둘기를 날도록 만들어라&quot;라는 요구조건을 받아 개발하기 시작했습니다. 나머지 요구조건은 따로 없기에 저희의 일은 &quot;날도록 만들어라&quot;라는 문제를 어떻게든 해결하는 것이죠.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;어떻게든 비둘기를 &quot;날도록&quot; 만들었습니다. 요구조건대로 날아가긴 합니다. 다만, 머리가 돌아가면서 헬리콥터처럼 날아가죠. 이럴꺼면 대나무 헬리콥터를 달아줄껄 싶기도 합니다.&lt;/li&gt;
&lt;li&gt;뭔가 예상대로 날아가지는 않지만 원하는 요구조건을 먼저 만족시킨 것은 맞으므로 이 코드를 바탕으로 게속 무언가를 계속해서 만들어나갑니다.&lt;/li&gt;
&lt;li&gt;그러다가 어느 날, &quot;날개를 보강하여 비행 성능을 높여라&quot; 라는 요구조건을 받았습니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이런! 저희는 날개를 보강해도 머리를 돌려서 날고있는데, 의미가 없는 작업일까요? 성능에는 변화가 없어지거든요!&lt;/li&gt;
&lt;li&gt;이 때, 결국 성능을 위해서 머리를 돌리는 것 대신에 날개를 사용하도록 코드를 수정하기로 했습니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&amp;lt;리팩터링을 이럴 때 일어나는 것이 아닐까 싶어요.&amp;gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;일단 작성된 코드로 잘 돌아간다면 두고 있다가, 추가적인 요구조건에 의해 수정하는 것이 현실세계에서 일을 가장 효율적으로 처리하는 방법이 아닐까 싶습니다.&lt;br /&gt;물론 처음부터 이런 것들을 고려해서 비둘기가 날개를 써서 날도록 만들었다면 더 좋았겠지만, 만약 제가 &quot;비둘기&quot;라는 것을 모르고 &quot;헬리콥터&quot;만 알고 있었다면 저도 위와 같은 흐름으로 구현을 했을 것 같습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그렇게 계속 만들어나가면서 &quot;아, 비둘기는 머리를 돌려서 날아가는 것이 아니라 날개를 위아래로 움직이면서 날아가는 것이구나!&quot; 라고 문제를 더 상세하게 이해하고 다음과 같은 사고흐름을 가질 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아, 비둘기의 머리는 생각을 하면서 비행 방향을 결정하고 날개를 움직여서 날아가는 것이겠구나?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이런 예시와 같이 시스템이 발전해나가는 것이 아닐까요?&lt;br /&gt;현실 세계에서는 여기서 예시를 들은 비둘기 대신 해결하려는 문제상황을 대입해보면 비슷한 상황들이 많이 펼쳐진다는 것을 알 수 있었습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 파트는 조금 길어서 정리한 한 문장을 적어보자면 아래와 같습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;우선 내가 이해한 대로 문제를 해결하고 점점 문제에 대한 이해도가 높아지면서 문제 해결 방법을 변경해도 늦지 않다. 어떻게 사람이 한번에 모든 것에 대한 본질을 꿰뚫고 파악할 수 있겠는가?&quot;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;그래도 리팩터링 하다가 코드가 망가질까봐 무서워요&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 테스트코드라는 것이 있죠! 코드가 늘어나서 생산성이 떨어진다고요? 그렇지 않습니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이런 고민을 하시는 분들 대부분은 테스트코드의 중요성을 인지하지 않고 그저 기능만을 구현하신 분들이 대부분일 것이라 생각합니다.&lt;br /&gt;실제로 테스트 코드를 작성하지 않고 리팩터링을 한다면 어떤 문제가 발생할 지, 겉보기에는 잘 된 것 같아보여도 실제로는 어떤 문제가 발생할 지 알 수 없습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;물론 테스트코드를 처음 작성해야 할 때는 해야할 일이 두 배가 된 것처럼 보이기도 합니다. 하지만 처음에는 그렇게 보여도 나중에 요구조건을 변경하거나 리팩터링을 해야할 때에는 오히려 생산성이 높아지는 결과를 낼 수 있다는 사실을 알고 계셨나요? 어떤 틀을 갖추고 있는 지 테스트코드를 통해 미리 지정하다보니, 많은 양의 변경사항이 있어야 함에도 불구하고 다소 빠르고 정확하게 코드를 바꿔나갈 수 있다는 점이 특징입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;예를들어 요즘같은 추운 날 국민간식이라고 불리는 붕어빵에 비유를 해볼까요?&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;붕어빵으로 알아본 테스트코드의 중요성&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;붕어빵은 역시 팥붕이죠. 반박시 슈붕&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/gUP0XHT.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;우리가 붕어빵을 만들기 위해 붕어빵 반죽과 단팥, 슈크림 등을 틀을 잘 만들어 두었습니다. 그리고 항상 일관된 모양이 나오기 위해 미리 확인하려고 일관된 모양의 붕어빵 틀을 만들어두었죠.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;약간 다를 수도 있겠지만 붕어빵 틀을 테스트코드에 비유해보면 이해하기 쉽습니다.&lt;/li&gt;
&lt;li&gt;테스트코드를 작성한다면 어떤 모양으로 나올 지를 미리 확인해볼 수 있기에 붕어빵 모양이 올바른지, 팥붕이면 팥이 제대로 들어가있는지, 슈붕이면 슈크림이 제대로 들어가있는 지 확인할 수 있죠.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;만약 요구조건이 바뀐다면??(음, 네모난 붕어빵으로 제품을 수정해야겠는데?)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단합니다. 테스트코드를 변경하고 이에 맞게 코드를 수정하면 됩니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/qgweZDo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;네모난 붕어빵이 요즘 인기가 많죠. 그래서 이런 인기에 편승해 이익을 내기 위해 우리의 개발자에게 &quot;기존의 붕어빵 대신에 네모난 붕어빵을 만들어주세요!&quot; 라고 요청을 했어요.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그러면 개발자는 간단하게 붕어빵틀을 위 모양에 맞게 변경(테스트코드 변경)하고 이 모양대로 나오도록 제조 방법(코드)를 작성하면 되는 것이죠!&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;어때요 정말 간단하지 않나요? 만약 테스트코드(붕어빵틀)이 없었다면 이게 만들어지긴 했는데 네모가 아니라 세모, 원, 육각형 등 어떤 모양이 나올 지 알 수 없는 상태로 만들어졌을지도 모르죠. 중앙이 비어있는 붕어빵이 될 지도 몰라요.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 테스트코드가 중요합니다. 여차하면 TDD라는 개념이 나왔을까요?&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;결국 테스트코드는 리팩터링을 위한 하나의 장치가 아닐까요?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트코드는 결국 고층 건물 작업을 하는 데 필요한 안전장치와 같이 하나의 수단일 뿐이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/j7JhZa6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;강연에서 사용했던 이 이미지처럼 테스트코드가 없었을 때에는 이런 느낌으로 코드작업을 하지 않았을까요?&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Q. 방금전까지는 붕어빵으로 잘 비유를 하다가 갑자기 웬 고층 건물인가요?&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;A. 물론 방금 전의 비유도 그런 느낌이 맞긴 했지만. 실제로 코드 작업을 하는 것이 생각보다 고층 건물에서 안전장치 없이 작업하는 것처럼 위험하다는 것을 알리기 위해 그렇게 적었습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;많은 개발자분들이 느끼기에도 코드 작업하다가 실수 하나 내면 갑자기 그 정교한 프로그램이 비행기 엔진에 나사 하나가 들어간것마냥 뭐가 문제인지는 모르겠는데 갑자기 안돌아가는 그런 일을 겪으신 분들이 많을 것입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;테스트코드 없이 코드를 작성하고 적용한다는 것을 고층 건물에서 어떠한 안전장치도 없이 계속해서 작업해나간다는 소리와 같습니다. 코드를 다 작성했고, 테스트코드가 없이도 잘 돌아가길래 배포까지 마쳤는데 갑자기 예상치 못했던 문제로 인해 서버가 다운된다면? 생각만해도 끔찍하죠..!&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그래서! 테스트코드는 꼭 필요합니다. 개발자의 안전을 위해서요!&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;어쩌다보니 클린코드에서 테스트코드까지 흘러왔군요. 이 이야기의 결론은 무엇이죠?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 클린코드는 단지 코드에 대한 것만을 의미하는 것이 아니라는 점을 주목해야 합니다. 방법론적인 하나의 방법일 뿐 반드시 적용해야하는 좋은 코드는 아닐수도 있다는 것이죠.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이런 말이 있습니다. &quot;단순히 개발자가 편하기 위해서 우리는 하드웨어의 성능을 포기하는 것은 정말 좋지 않은 태도입니다.&quot;&lt;br /&gt;말 그대로 클린코드 기법을 사용해 코드를 리팩터링 한다고 했을 때 성능이 유지되거나 더 좋아지면 좋겠지만, 그렇지 않은 경우도 존재합니다.&lt;br /&gt;그렇다면 과연 개발자의 편의를 위해 그동안의 하드웨어의 발전을 부정하는 코드를 사용하는 것은 올바르다고 볼 수 있을까요?&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;개발자의 편의, 기업이 바라보는 제품(비용 등), 사용자의 만족 이 모두를 만족시킨만한 그 교집합에 있는 이상적인 제품을 위해 약간은 양보를 하고 가야 하는것이 아닐까 싶습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;많은 개발자들이 &quot;클린(clean)&quot; 그리고 &quot;코드(code)&quot;에 집중을 하고 있습니다. 하지만 막상 이에 대해 깊게 들어가보면 우리가 아는 클린코드는 코드에 대한 것이 아닐수도 있습니다.&lt;br /&gt;어쩌면 클린코드는 붙여져있는 그 이름과는 다르게 &quot;클린&quot;, 그리고 &quot;코드&quot; 양쪽 다 상관없는 개념일지도 모릅니다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;따로 정답도 없고 완벽한 결론도 없는 이러한 주제이지만, 그래도 이 글에 대한 결론은 필요하겠죠?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로의 개발시장에서 우리는 많은 시행착오를 거쳐야 합니다. 아직도 컴퓨터에 대한 많은 부분을 정복하지 못하고 사용하고 있죠.&lt;br /&gt;이러한 세상에서 우리는 그때그때 가장 정답에 근접한 코드들을 통해 정답이 없음에도 그렇게 보이도록 만드는 역할을 하는 &quot;문제 해결사 및 정복자&quot;를 담당하고 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그렇게 주어진 문제들에 대한 이해도를 높여가며 점진적으로 코드를 키워나가는 것이 개발자들의 목표이며 이상이라고 생각합니다. 그러기 위해서는 문제를 이해하는 능력이 필요하고, 이해가 된 순간 이를 분해하여 다시 이해하고 결합하고 이러한 능력이 추가적으로 필요하겠죠.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;개발을 배워나가는 저와 같은 학생들에게 클린코드는 어떤 의미인가요? 지금 배우고 써보는 것도 괜찮을까요?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 아직 개발을 배워가는 한 명의 학생입니다. 이렇게 강연들을 듣고나니 질문을 드리고 싶은 것이 하나 있습니다. 저와 같은 개발자를 향해가는 학생들 중에서도 클린코드에 푹 빠져있고, 좋은 설계에 대해 관심있게 보고 있는 경우가 많습니다.&lt;br /&gt;하지만 지금까지의 내용을 클린코드를 지금 시점에 배우는 것보다는 사고의 확장을 먼저 일으키는 것이 중요한 것 같아 보입니다.&lt;br /&gt;개발자님께서는 조언을 주신다면 어떤 말씀을 해주시겠습니까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;실제로 위와 같은 질문을 드렸고, 다음과 같은 답변을 받았습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 지금 클린코드에 대한 것을 적용시키기 전에 내가 코드를 잘 만들 수 있는 사람인지부터 확인을 해보시는 것이 좋습니다. 만약 클린코드 방법론, 여러 정형화된 규격에 맞지 않은 코드를 작성할까 두려워하며 시작조차 하지 못한다면 어느 의미가 있겠나요?&lt;br /&gt;우선 어떻게든 많은 코드들을 작성해보면서 코드를 찍어내기 쉬운 상태로 먼저 올라가야 합니다. 그러면서 계속해서 코드들을 키워내다보면 그 과정에서 원칙을 익히고, 방법들을 습득하는 것입니다.&lt;br /&gt;만약 이런 상태에서 클린코드와 같은 방법론을 배우고 적용해보고 싶다는 생각을 하고 있다면 얼른 그만두세요.&lt;br /&gt;클린코드? 객체지향? 좋은설계? 일단은 지금은 때가 아닙니다.&lt;br /&gt;물론 하지 않는다면 취직이 되지 않으니 취업 전에 잠깐 공부하는 것으로도 충분합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이런 답변과 함께 하나의 방법론을 제시해주시기도 하였습니다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;마무리: 고통 주도 개발자를 해 보아라&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 돌아가게 만들어라. 그리고 어떤 것이 내게 고통이 된다면 그 떄 해결해도 늦지 않다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;고통 주도 개발자?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;일단 돌아가게 코딩해봅시다&lt;/li&gt;
&lt;li&gt;어떤 것이 나에게 고통이 된다면 그 떄 해결합시다. 미리 깨끗하게 만들 필요는 없어요.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;많은 프로그래머들이 이미 원칙 주도 개발을 하고 있습니다. 그 소리는 원칙을 지키지 않는 코드는 작성하지 않으려 한다는 소리이고, 그러한 원칙을 지키려다보니 원칙에서 벗어난 코드를 작성하는 것을 극도로 기피한다는 의미이기도 합니다.&lt;br /&gt;가장 중요한 것은 일단 코드를 찍어낼 수 있어야 합니다. 무엇이든 만들 수 있어야 개발을 할 수 있거든요.&lt;br /&gt;그리고 그런 원칙을 지키도록 만드는 것은 그 이후에 해도 충분합니다. 특히 우리와 같은 개발자 꿈나무들은 말이죠.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;자, 그럼 고민하지 말고 일단 만들러 가봅시다!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/개인</category>
      <category>Clean</category>
      <category>clean code</category>
      <category>CODE</category>
      <category>goorm</category>
      <category>Refactoring</category>
      <category>testcode</category>
      <category>리팩터링</category>
      <category>클린코드</category>
      <category>테스트코드</category>
      <category>튜링의 사과</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/265</guid>
      <comments>https://time-map-installer.tistory.com/265#entry265comment</comments>
      <pubDate>Thu, 30 Nov 2023 18:38:32 +0900</pubDate>
    </item>
    <item>
      <title>크로스 브라우징 이슈가 발생하는 이유는 무엇일까?</title>
      <link>https://time-map-installer.tistory.com/264</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;308&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nI7Sk/btsATloPkRK/1SG1czGKpOfJ0boBQY00lK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nI7Sk/btsATloPkRK/1SG1czGKpOfJ0boBQY00lK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nI7Sk/btsATloPkRK/1SG1czGKpOfJ0boBQY00lK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnI7Sk%2FbtsATloPkRK%2F1SG1czGKpOfJ0boBQY00lK%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;308&quot; height=&quot;164&quot; data-origin-width=&quot;308&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;들어가며&lt;/h3&gt;
&lt;p&gt;크로스 브라우징 이슈는 현대 웹 개발의 중요한 과제입니다. 오늘날, Microsoft Edge, Safari, Chrome, Arc와 같은 다양한 브라우저가 존재하면서, 웹 개발자들은 이들 각각의 특성을 고려하여 웹서비스를 개발해야 합니다.&lt;br&gt;그리고 이러한 상황에서 발생하는 중복과 호환성 문제를 &amp;#39;크로스 브라우징 이슈&amp;#39;라고 부릅니다.&lt;br&gt;그렇다면, 이 이슈는 왜 발생하는 것일까요? 이 문제를 깊이 이해하기 위해, 먼저 크로스 브라우징의 원인을 살펴보고, 그로 인해 발생하는 문제들을 탐구해보겠습니다.&lt;/p&gt;
&lt;h3&gt;크로스 브라우징 이슈란?&lt;/h3&gt;
&lt;p&gt;&amp;quot;크로스 브라우징 이슈&amp;quot;는 여러 웹 브라우저 간의 호환성 문제로 인해 발생합니다. 다음과 같이 몇가지 주요한 원인들이 존재합니다&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;브라우저 간 차이&lt;/li&gt;
&lt;li&gt;웹 표준 준수&lt;/li&gt;
&lt;li&gt;사용자 에이전트 스타일링&lt;/li&gt;
&lt;li&gt;자바스크립트 실행환경 차이&lt;/li&gt;
&lt;li&gt;플러그인, 그리고 확장 프로그램&lt;/li&gt;
&lt;li&gt;디바이스 및 해상도 차이&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;정말 다양한 원인으로 이러한 문제가 일어나고 있는데, 크로스 브라우징 이슈가 일어날 경우 어떤 문제가 발생할까요?&lt;/p&gt;
&lt;h3&gt;크로스 브라우징으로 인해 발생하는 문제들&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;레이아웃 깨짐&lt;ol&gt;
&lt;li&gt;레이아웃이 의도대로 표시되지 않아 요소들이 겹치거나 예상치 못한 위치에 배치될 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;기능 문제&lt;ol&gt;
&lt;li&gt;특정 브라우저에서 JS 의 일부 기능이 제대로 작동하지 않아 인터페이스가 손상될 수 있음&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;스타일링 문제&lt;ol&gt;
&lt;li&gt;CSS가 일관되게 적용되지 않아 예상과 다른 결과물이 보일 수 있음&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;성능 문제&lt;ol&gt;
&lt;li&gt;일부 브라우저에서 웹페이지의 로딩시간이 늘어나 UX를 해칠 수 있음&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;플러그인 및 확장 프로그램 호환성&lt;ol&gt;
&lt;li&gt;Chrome의 확장 프로그램을 다른 브라우저에서 쓸 수 있을까?&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;보안 문제&lt;ol&gt;
&lt;li&gt;Microsoft의 explorer이 어떻게 되었는지를 생각해보자&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;다양하게 문제가 발생합니다. 여기에 근본적으로는 JavaScript가 연관되어있다고 하는데.. 정말일까요?&lt;/p&gt;
&lt;h3&gt;JavaScript의 역사와 크로스 브라우징 이슈가 관련이 되어있다고?&lt;/h3&gt;
&lt;p&gt;믿기 힘드시겠지만 그렇습니다. 그리고 그 중심에는 Microsoft가 위치해있죠&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JavaScript의 탄생과 JScript의 출시&lt;/strong&gt;&lt;ol&gt;
&lt;li&gt;JavaScript는 1995년 Netscape에 의해 개발되었다. &lt;/li&gt;
&lt;li&gt;그 후 곧바로 Microsoft에서 자체적인 버전인 &amp;#39;JScript&amp;#39;를 출시했다. &lt;/li&gt;
&lt;li&gt;JScript는 JavaScript와 유사했지만, 완전히 호환되지는 않았다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;그렇게 시작된 브라우저 전쟁과 비호환성&lt;/strong&gt;&lt;ol&gt;
&lt;li&gt;Microsoft는 자사의 Internet Explorer를 위해 JScript에 독점적인 기능을 추가&lt;/li&gt;
&lt;li&gt;이러한 Microsoft의 전략은 웹 표준을 벗어나게 되면서, 그들의 브라우저에서만 돌아가는 기능들을 만들어내었다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;크로스 브라우징 이슈의 시작&lt;/strong&gt;&lt;ol&gt;
&lt;li&gt;이러한 상황은 웹 개발자들에게 큰 도전과제를 부여했다&lt;/li&gt;
&lt;li&gt;Microsoft의 Internet Explorer 외 다른 브라우저에서는 이러한 기능이 제대로 작동하지 않았기 때문에 결과적으로 동일한 웹 페이지가 브라우저에 따라 다르게 보이거나, 일부 기능이 작동하지 않는 등의 문제가 발생하기 시작했습니다.&lt;/li&gt;
&lt;li&gt;이를 우리는 크로스 브라우징 이슈라고 부른다    &lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;표준화 노력&lt;/strong&gt;&lt;ol&gt;
&lt;li&gt;다양한 브라우저 제작사들과 웹 표준화 기구들은 웹 표준을 만들고 이를 준수하기 위한 노력이 시작됨&lt;/li&gt;
&lt;li&gt;ECMAScript라는 이름으로 JavaScript의 표준 버전이 만들어졌고, 이는 웹 개발의 호환성 문제를 해결하는 중요한 역할을 담당하고 있다&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;현재의 크로스 브라우징 이슈는 위와 같은 과정에 벌어졌던 Browser War를 통해 발생해왔습니다. 오늘날에도 브라우저 간의 호환성 문제는 존재하지만, 웹 표준의 발전과 브라우저 기술의 향상으로 많은 문제들이 해결되었거나 개선되고 있습니다.&lt;/p&gt;
&lt;h3&gt;그래서 등장한 반응형 웹 디자인, 크로스 플랫폼 서비스&lt;/h3&gt;
&lt;p&gt;이제, 크로스 브라우징 이슈를 해결하기 위한 해법으로 등장한 것이 바로 &amp;#39;반응형 웹 디자인&amp;#39;과 &amp;#39;크로스 플랫폼 서비스&amp;#39; 개발입니다. 반응형 웹 디자인은 다양한 스크린 크기와 해상도를 가진 디바이스들에 적절하게 반응하여, 모든 사용자에게 최적의 경험을 제공하는 디자인 방법론입니다. 이는 CSS 미디어 쿼리를 활용하여 다양한 디바이스에 대응하는 유동적인 레이아웃을 구현합니다.&lt;/p&gt;
&lt;p&gt;또한, 크로스 플랫폼 서비스 개발은 웹, 모바일, 데스크톱 등 다양한 플랫폼에서 일관된 사용자 경험을 제공하기 위한 중요한 전략입니다. 이를 위해 React, Angular, Vue.js 같은 현대적인 웹 프레임워크와 Flutter, React Native와 같은 크로스 플랫폼 개발 도구가 널리 사용됩니다. 이러한 기술들은 접근성, 사용자 인터랙션, 성능 최적화를 고려하여 모든 사용자에게 만족스러운 경험을 제공할 수 있도록 합니다.&lt;/p&gt;
&lt;h4&gt;결론&lt;/h4&gt;
&lt;p&gt;결론적으로, 크로스 브라우징 이슈는 웹 개발의 중요한 과제이며, 이를 해결하기 위한 반응형 웹 디자인과 크로스 플랫폼 서비스 개발은 현재 및 미래의 소프트웨어 개발에서의 핵심 전략으로 자리 잡고 있습니다.&lt;br&gt;이러한 접근 방식은 웹 개발의 복잡성을 줄이고, 모든 사용자에게 최상의 경험을 제공하는 데 크게 기여하고 있습니다.&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>browser war</category>
      <category>cross browsing</category>
      <category>Microsoft</category>
      <category>자바스크립트</category>
      <category>크로스 브라우징</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/264</guid>
      <comments>https://time-map-installer.tistory.com/264#entry264comment</comments>
      <pubDate>Mon, 27 Nov 2023 20:11:57 +0900</pubDate>
    </item>
    <item>
      <title>&amp;lt;우아한테크코스 2024 프리코스&amp;gt; 4주차 후기: 눈떠보니우아한형제들의엔지니어로취칙해비즈니스로직을만들고있었던것에대하여</title>
      <link>https://time-map-installer.tistory.com/263</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMYYtx/btsAq1cxNLz/zPyEWrWFwKvz7hukPqNs01/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMYYtx/btsAq1cxNLz/zPyEWrWFwKvz7hukPqNs01/img.webp&quot; data-alt=&quot;우아한 형제들 (generated by DALL.E)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMYYtx/btsAq1cxNLz/zPyEWrWFwKvz7hukPqNs01/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMYYtx%2FbtsAq1cxNLz%2FzPyEWrWFwKvz7hukPqNs01%2Fimg.webp&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;600&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;우아한 형제들 (generated by DALL.E)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;3주차를 돌아보며&lt;/h3&gt;
&lt;p&gt;3주차에서 가장 크게 와닿았던 부분이 소숫점 계산이었다.&lt;br&gt;원래는 round() 메서드를 이용하여 10을 곱하고 10을 나누어서 했었는데&lt;br&gt;toFixed() 라는 아주 좋은 메서드가 있었다. 이제 깊은 깨달음을 얻었으니 앞으로는 이걸 써야겠다.&lt;/p&gt;
&lt;p&gt;plus+ : 이거 알았으면 숫자 제한으로 안걸리도고 10억원어치 로또를 한번에 살 수 있었을지도...&lt;br&gt;무슨 소리냐고?? &lt;a href=&quot;https://time-map-installer.tistory.com/261?category=1126850&quot;&gt;로또를 10억원어치 자동으로 돌려보았던 지난주차 보고오기&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;프리코스 4주차는 시작부터 폭탄이 예고되어 있었다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;???: 여러분들 지금까지 프리코스 잘 즐기셨죠? 이제부터는 익스펜시브코스에요~&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;때는 바야흐로 수업을 듣고 있는 시간대이며, 코수타가 진행되는 시기인 목요일 오후 2시 이후...&lt;br&gt;이번 주에도 수업이 있어서 라이브로 듣지는 못했지만 커뮤니티에 뭔가 이상한 글들이 올라오기 시작했다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/QH6oRz5.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;???&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rsKmajO.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;????????????&lt;br&gt;혼란이 왔지만 어느정도는 인정할 수 있었다. 만약 어려웠더라면 3주차 때 성장하는 대신에 그대로 탭을 치며 빠져나갔을 지도 모르는 일이기 때문이다.&lt;br&gt;그 외에도 보이지 않는 객체를 상상하고 코드로 구현해야하기때문에 개발자는 상상력도 중요하다 와 같은 말들도 들을 수 있었다. 아무렴 난이도가 어려우면 또 어떠한가. 설레는 마음으로 집으로 돌아가 얼른 풀어봐야겠다는 마음가짐으로 마지막 주차 프리코스를 시작했다.&lt;/p&gt;
&lt;p&gt;&lt;del&gt;??? : 디자인 패턴같은 쓰레기는 버려라&lt;/del&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;뭔가 현직자가 된 느낌이네!&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;이메일 형식의 요구조건! 한 페이지 안에 다 담기지 않은 기능명세! 실제 기업에서 준비하는 듯한 느낌의 이벤트까지! 이 모든 것을 한번에, 일주일 안에 구현해보세요!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;라고 기능명세에 적혀있지는 않았지만 기능명세의 느낌이 그런 느낌이었다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/8bZuuFk.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;마음이 벅차오른다..&lt;br&gt;예산 상관없는 이벤트라니..!&lt;br&gt;실제 기업에서 하는 이벤트 담당 엔지니어가 되었다니..!&lt;br&gt;비즈니스팀이라니!&lt;br&gt;개발 요청이라니!&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/ZftB0iZ.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;&lt;del&gt;고라니!&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;아무튼 요구조건이 조금 많긴 했지만 지난주 처럼 차근차근히 하나씩 정리해가면 순조롭게 해결되어나갈 것이라 믿었다.&lt;br&gt;생각보다 요구조건 정리하는 데 오랜 시간이 걸리긴 했다. 정확하게 적지 않는다면 사고가 일어날 것만 같아서 신중에 신중을 가해서 적어두고 개발을 시작했다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;개발 순서는 어떻게 했을까?&lt;/h3&gt;
&lt;p&gt;이번에는 다이어그램을 그리지는 않았다. 단순하게 입력값 받는 과정 2번과 이를 처리하는 과정 1번으로 매우 간단할 것으로 보였기 때문이다.&lt;br&gt;그렇게 리드미를 작성하고, 기존에 fork 해서 작성하던 코드가 아닌 코드 접근을 차단하기 위해 새로 도입된 template 방식으로 프리코스가 진행되었다.&lt;br&gt;아무튼 그래서 개발 순서는 다음과 같았다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;요구조건에 있는 메세지 및 숫자 상수화&lt;/li&gt;
&lt;li&gt;입력받는 로직에 대한 기능구현 및 예외처리용 모델 생성&lt;/li&gt;
&lt;li&gt;예외처리 없이 전체 코드로직 완성 후 모듈화&lt;/li&gt;
&lt;li&gt;모듈화 이후 예외처리 및 테스트코드 작성&lt;/li&gt;
&lt;li&gt;이후 코드 가독성 개선 및 추가 모듈화, 상수화 등 리팩터링&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;생각보다 모듈화는 먼저 하는것이 더 중요했다. 테스트코드를 통해 단위 테스를 진행하기 위한 코드를 작성한다고 해도, 그 코드가 제대로 분리되어있지 않는다면 테스트 짜기도 힘들뿐더러 나중에 다시 작성하는 것도 다 일이기 떄문이라고 생각했다. 테스트코드가 조금 많긴 했지만 이에 대한 내용은 아래에서 다시 다루도록 하겠다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/fRwcr3h.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&amp;quot;조금&amp;quot; 많아요^^&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;객체를 만드는 기준은 무엇일까? 그냥 다 함수로 분리하면 안돼?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;객체를 만들고 모듈화를 야무지게 하다보니 생각났던 한가지 의문이 있었다. 그냥 다 단일 함수로 분리해버리면 안되는 걸까?&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;모듈화를 얼마나 어떻게 해야하는가에 대한 고민을 하다보니 생겼던 질문이었다. 물론 프리코스 기간이었기에 나 스스로 답을 찾아보고자 했고, 그 이유를 다음과 같이 설명해 보았다.&lt;/p&gt;
&lt;p&gt;객체로 만드는 것과 함수로 다 분리하는 것은 조금씩 차이가 있다고 생각을 했고, 어떠한 규칙에 따라 이를 관리하는 방법이 달라진다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;객체를 사용하는 경우: 2번의 함수들을 조합해서 특정한 모델이나 도메인을 위한 기능들의 모음을 만드는 경우&lt;/li&gt;
&lt;li&gt;함수로 잘게 쪼개는 경우: 모든 로직이 시작될만한 근본적인 로직을 작성할 때 이렇게 사용, 주로 utils에서 관리&lt;h3&gt;테스트를 상세하게 짜야 기존 코드 오류도 찾을 수 있지!&lt;/h3&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;테스트를 작성하기 전까지 저는 제 코드가 완벽한줄 알았어요. 알고보니 젠가의 가장 아래 블록에 금이 가있었는데 그것도 몰랐던 것이었죠.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;테스트를 상세하게 짜다보니 생각보다 기존 코드에서 놓치고 있던 처리들이 있다는 것을 발견했다. 특히 모든 코드 로직의 시작점인 근본 코드를 다루는 것이다보니 더욱 상세하게 테스트를 짜는 것이 중요했다. 그래서 테스트가 84개가 된 것일지도 모르겠다.&lt;/p&gt;
&lt;p&gt;그렇게 테스트를 모두 작성하고 나니 예상하지 못했던 오류들을 찾아서 고쳐나갔고, 이래서 테스트가 중요하구나 라고 다시 한 번 깨달을 수 있었다.&lt;/p&gt;
&lt;h3&gt;끝나기 하루 전에 발견했던 치명적인 오류...&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;와 이거 그냥 넘어갔으면 지금쯤 멘탈이 가루가 되어있지 않았을까?&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Xss2ghV.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;마치 쿠X다스와 같죠.(광고 아닙니다)&lt;/p&gt;
&lt;p&gt;이걸 하루 전에 발견해서 얼마나 다행이었을까요. 결과를 출력하는데 정말 핵심적인 데이터를 전달하는 로직에 값이 반환되지 않았다는 것을 다 끝났다고 생각하며 앱을 가지고 놀다가 우연히 알았고, 이는 기존에 테스트에 적혀있지 않았던 내용이다보니 급히 통합 테스트쪽에 추가 케이스를 5개정도 집어넣고 많은 로그를 찍어보며 &amp;quot;어떻게든&amp;quot; 오류를 해결해서 정말 다행이었다.&lt;/p&gt;
&lt;p&gt;어떻게 해결했지?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;기존 코드에서는 배열 내의 값을 증가시키는 로직에 return 없이 배열에 직접 접근해서 배열 내부의 값을 직접적으로 증가시켰다.&lt;ul&gt;
&lt;li&gt;그게 실제로 자바스크립트 자체적인 기능에 있었기에 그렇게 했었지만 &lt;/li&gt;
&lt;li&gt;후에 로직을 변경하다보니 이 부분이 적용되지 않는 상황이 발생하였다.&lt;/li&gt;
&lt;li&gt;그래서 값을 직접 받아서 return시켜서 상위 메서드에서 변경하는 방식으로 고쳤더니 해결되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;static을 사용하기 좋은 상황은 무엇일까?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;static, 언제 써야 좋을까?&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/EDPhkxl.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이전까지만 해도 다른 사람들의 코드를 보았을 때 class 안에 static을 특정 메서드 앞에 붙여서 사용하는 것으로만 보였다.&lt;br&gt;하지만 이번에 4주차 과제를 진행하면서 명확한 근거를 알 수 있었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;static을 붙이면 인스턴스를 초기화하지 않고 바로 불러올 수 있다.&lt;/li&gt;
&lt;li&gt;이 소리는 호출 할 때마다 항상 일관적인 일을 하는 메서드라면 static을 붙여 인스턴스 초기화를 하지 않아도 된다는 소리와 같다.&lt;/li&gt;
&lt;li&gt;그렇게 나는 에러만을 던지는 객체를 생성하고 static을 붙여 인스턴스 초기화 없이 사용할 수 있도록 만들었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;아무래도 역대급 코드파일 수를 돌파한 것 같다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;자랑한번 해보겠습니다. 저 이만큼 성장했어요!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이건 진짜 시각화해서 보여주어야 할 것 같다고 판단했고, Webstorm의 다이어그램 생성 기능을 이용해서 src 내에 존재하는 파일들을 이은 아래와 같은 다이어그램을 출력해보았다...&lt;br&gt;파일이 36개 정도 된다고 해도 이건 실로 어마어마한 규모가 아닐까 싶었다. 내가 이걸 만들었다니! 기분이 아주 웅장해지네요&lt;br&gt;행운의 77커밋과 함께하는 다이어그램을 아래에 올려두었다. 어때요? 저 많이 성장했죠?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/b4ClsmA.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;4주차&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/2duMWYN.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;아주 멋져요!&lt;br&gt;문득 이 다이어그램을 뽑아내는 기능을 알고나니 1~3주차 때의 코드는 어떻게 나올 지 궁금했다.&lt;br&gt;그래서 과거로 서서히 돌아가보기로 했다.&lt;/p&gt;
&lt;p&gt;3주차 - 약 1200라인&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/3HwBRsY.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;2주차 - 약 400라인&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/x88GdjZ.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;1주차 - 약 200라인&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/BnQ1eam.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ&lt;br&gt;많이 발전했죠? 저도 그렇게 생각해요&lt;/p&gt;
&lt;p&gt;실시간으로 발전해간 모습을 보며 매우 뿌듯했다.&lt;br&gt;앞으로 프로젝트를 해도 프리코스 한다는 느낌으로 해야겠다. 굉장히 효과적이라 10점 만점에 10,000점 드립니다&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;자신감이 붙는 마무리&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;그렇게 나는 마무리까지 정말 잘 해내었다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rB7OMsY.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이제 난 무엇을 해도 잘 할 수 있을 것 같다. 프리코스도 포기하지 않고 잘 끝냈는데 무엇이 두려울까? 지금&lt;br&gt;기존에 했던 프로젝트들은 그저 코드만 작성한 느낌이었다면, 이제는 여러 방면으로 테스트하기 용이하게도 짤 수 있을 것 같고, 가장 중요하게는 &amp;quot;모듈화&amp;quot;에 대한 감을 잡은 것 같다.&lt;br&gt;현재 사용하고 있는 React, Next, Svelte같은 경우에는 객체를 사용하기보다는 주로 함수로만 작성을 해왔는데, 객체도 적절히 사용하면 될 것 같기도 하고 그렇다&lt;/p&gt;
&lt;h3&gt;인줄 알았는데 요구조건을!!!!!!! 놓쳤어!!!!!!!&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;그렇게 나는 마무리까지 정말 잘 해내었다. 라고 생각했다. 리뷰하면서 놓친 부분들을 찾아내기 전까지 말이다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;잘못한건 공개해두고 스스럼없이 비판하라고 배웠다. 그래서 스스로를 비판해보겠다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;도대체 왜 나는 코드를 사용하고 주석처리 해둔 후 지우지 않았는가&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/NcE50DO.png&quot; alt=&quot;&quot;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;도대체 왜 나는 혜택 금액을 &amp;quot;0원&amp;quot;이 아니라 &amp;quot;없음&amp;quot;으로 표현했는가&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/kA8lx3r.png&quot; alt=&quot;&quot;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;으아악&lt;/p&gt;
&lt;p&gt;심지어 제작과정중에서는 요구조건이 정확히 일치했었다. 결국 휴먼에러가 발생하여 이런 문제들이 일어났다는 것인데... &lt;/p&gt;
&lt;p&gt;혹시 휴먼 디버깅은 아직 안되나요. 기능 출시된다고 들은지 23년정도 된 것 같은데&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;나에게 주는 피드백&lt;/h3&gt;
&lt;p&gt;아쉬운점이 있다면 기존에 객체를 잘 활용하지 못했다는 점이었다. 생각보다 배열을 활용하는 것이 익숙하고 더 쉽게 느껴졌다보니 대부분을 배열 로직으로 작성했던 것 같다. 이번 프리코스에서도 그랬고, 그 부분이 조금 아쉬웠다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;후에 코드리뷰를 일부 받아보았을 때에는 지금 이것도 잘 짠 것 같다 라고 해서 다행이었긴 하지만 뭔가 뭔가 아쉬움이 남긴 해서 적어보았다.&lt;ul&gt;
&lt;li&gt;그럼에도 약 30~40여개의 파일을 리뷰해주신 리뷰어분께 감사를 표합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;물론 수확도 있었다. 그만큼 객체 활용도가 더 늘었다는 점. 그동안 코딩테스트에서 애를 먹었던 부분 중 하나가 객체를 잘 활용하지 못했던 부분에 대한 것이었는데, 이 부분이 약간씩 프리코스의 순기능인가 싶었다.&lt;br&gt;기한이 주어지고, 이를 실제로 구현할 수 있도록 해보니 객체에 대한 조사도 많이 할 수 있었고, 리팩터링 과정에서 계속 뜯어보다보니 &amp;quot;뭔지 알고있는 것&amp;quot;의 다음 단계로 나아갈 수 있던 것이 좋았다.&lt;br&gt;또한 여기서 말하던 &amp;quot;기능 단위 커밋&amp;quot;을 지키려고 했지만 정확하게 이해하지 못한 것 같기도 하다. 그냥 중간중간 진행될 때마다 저장하듯이 커밋을 했는데, 아직 이 부분에 대한 피드백을 받아보지 못해서 추후에 커밋을 잘 하고 있는건지 확인해보려고 한다. 물론 정답이 없다는 것을 알지만 나름대로의 원칙을 세워두고 가고 싶었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;그리고 마지막 코수타에서&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;???: 그리고 감기걸려서 프리코스하느라 고생하신 분도 계셨고요&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;? 내얘기인가 ?&lt;br&gt;프리코스 2주차부터 주구장창 감기걸린 내용을 빼두지 않고 적긴 했다. 물론 나 말고도 다른 분들도 계셨겠지만 그래도 내 얘기를 하는 것 같아서 기분이 신기했다.&lt;br&gt;아닌가 이거랑 같은 상황인가&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/E1gYWAj.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이런 느낌인가&lt;br&gt;아무튼 그래도 기분은 신기했다&lt;/p&gt;
&lt;p&gt;추가적으로 회고 적었던 것들을 읽으셨는데 뭔가 이 글도 읽으시지 않을까 싶다(보고있으신가요? 반갑습니다)&lt;/p&gt;
&lt;p&gt;&amp;quot;1주차의 코드를 보았더니 굉장히 쓰레기같다&amp;quot; 라고 말해야 프리코스를 제대로 했다고 볼 수 있다고 한다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;아 내 예전 코드 쓰레기같다&lt;/del&gt;&lt;br&gt;&lt;img src=&quot;https://i.imgur.com/NhjH9kU.png&quot; alt=&quot;&quot;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;사실 이것도 맞는 말이며, 이전에 했던 프로젝트의 코드를 보아도 정말 그렇게 느껴져서 코드를 폐기하고 싶기도 했다&lt;br&gt;분명 객체지향으로 학습을 한 것 뿐인데 함수형으로도 어떻게 해야할 지 머릿속에 계속 그려지는 것이 신기했고, 이제는 프로젝트 할 때 더 자신감있게, 더 좋은 코드를 만들 수 있을 것이라고 생각한다.&lt;/p&gt;
&lt;p&gt;전화데이트도 있었기에 도전 했었다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;전화데이트 시도 실패! 다들 매크로 쓰는것이 아닌가 싶을 정도로 굉장히 빨랐다. 대부분 백엔드분들이 기회를 얻었던 것으로 기억한다.&lt;ul&gt;
&lt;li&gt;이 때 누군가가 자바스크립트가 자바보다 느려서 안된다고 했던 것 같은데..(맞는말이긴 함)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;뭔가 전화데이트 성공했으면 어떻게 말했을 지 이미 생각해둔 부분이 있다. 아래에 테스트코드를 돌려볼까?&lt;h3&gt;PhoneDate.test.js&lt;/h3&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;describe(&amp;quot;PhoneDate 제발 테스트&amp;quot;, () =&amp;gt; {
  test(&amp;quot;전화를 받으면 인사를 준비한다&amp;quot;, () =&amp;gt; {
      const sayHello = &amp;quot;안녕하세요, 저는 Json이라고 합니다.&amp;quot;
    expect(checkIsEmpty(&amp;quot;&amp;quot;)).toBe(sayHello);

  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;방금까지는 약간 과몰입이었고, 진짜는 여기에 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;몰입을 할 수 있었고, 몰입을 통해 하고싶은 일을 해내었으며, 몰입을 통해 결과로 가는 길에 필연적으로 나타나는 시련에 대한 고통을 일시적으로 잊으며 나아갈 수 있었습니다. 처음 200줄짜리의 코드만을 작성할 수 있었던 저의 상태에서 점차 코드를 잘 짤 수 있게 해주었던 프리코스만의 특수한 시스템으로 인해 저는 많은 도움을 받았습니다. 이후에 코테인원 결과가 나오기까지 약 3~4주 정도 되는 공백기에서는 프로젝트에 여기서 배운 내용들을 적용해가며 획기적으로 코드를 더 예쁘게 만들어보려고 합니다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그리고 실제로 영어이름도 Jason이긴 해요.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;I am 더닝크루거의 꼭대기에 reach 했다&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/vGJ1A4e.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;여기에 보이는 우매함의 봉우리를, 개발의 길을 걷기 시작한지 11개월만에 모두 오르는데 성공했습니다!&lt;br&gt;앞으로 있을 이 절망의 계곡에서 깨달음의 비탈길까지 가는 길을 위치에너지 보존의 법칙에 따라 에너지 소모 없이 빠르게 지나가면서 바로 지속가능성의 고원에  안착하는걸 목표로 하고있습니다.&lt;/p&gt;
&lt;p&gt;이제 어떤 일들이 기다리고 있을까요? 분명 좋은 일들이 일어날 것이라고 믿고있습니다.&lt;br&gt;노력은 결고 배신하지 않을 것입니다. 읽어주신 여러분들 모두 감사합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;End&lt;/strong&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/개인</category>
      <category>2024</category>
      <category>4주차</category>
      <category>6기</category>
      <category>우아한 테크코스</category>
      <category>크리스마스</category>
      <category>프리코스</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/263</guid>
      <comments>https://time-map-installer.tistory.com/263#entry263comment</comments>
      <pubDate>Thu, 16 Nov 2023 00:01:25 +0900</pubDate>
    </item>
    <item>
      <title>&amp;lt;Jest, Unit Test&amp;gt; 쉽고 빠르게 단위테스트 알아보기</title>
      <link>https://time-map-installer.tistory.com/262</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;319&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p5lMg/btsAaOxsFUZ/OKX4Dkrm257VEJzT43qKV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p5lMg/btsAaOxsFUZ/OKX4Dkrm257VEJzT43qKV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p5lMg/btsAaOxsFUZ/OKX4Dkrm257VEJzT43qKV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp5lMg%2FbtsAaOxsFUZ%2FOKX4Dkrm257VEJzT43qKV0%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;319&quot; height=&quot;158&quot; data-origin-width=&quot;319&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;들어가며&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;굳이 테스트 코드가 필요해?? 그냥 실행해보면서 개선사항들을 찾으면 안되려나?&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;나는 지금까지 프론트엔드 개발을 접해오면서 이런 생각을 해왔다. 그도 그럴것이 백엔드보다는 테스트가 덜 중요한 것이 이 프론트엔드였고, 프론트엔드에서는 렌더링만 잘하고 화면을 멋지게 잘 구성하기만 하면 된다라는 인식이 널리 퍼져있기 때문이었다.&lt;br&gt;하지만 이는 내 오판이었다. 실제로 개발을 하면서 규모가 커지고 복잡해지자 어디서부터 오류가 발생하였는 지 찾는 데 걸리는 시간이 훨씬 더 오래 걸리게 되었고, 테스트코드를 작성하지 않음으로 인해 생겨나는 디버깅 시간이 테스트코드를 작성하는 데 걸리는 시간보다 더 길게 걸린다는 사실을 얼마 뒤 알 수 있었다.&lt;/p&gt;
&lt;p&gt;그렇다면 테스트코드, 왜 필요한 것이고 어떻게 작성해야 할까?&lt;br&gt;우아한 테크코스 프리코스를 해오며 깨달은 지금까지의 인사이트를 정리해보도록 하겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;테스트코드? 그건 뭐야?&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/FkDwjLV.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;JavaScript에서 테스트 코드는 개발 과정의 중요한 부분 중 하나로, 개발자들이 작성한 코드가 예상대로 작동하는지 확인하기 위해 사용되며, 아래와 같은 상황을 위해 주로 사용된다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;버그 감지&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;개발 과정 중에 버그를 더 빠르게 찾아 수정할 수 있도록 도와준다.&lt;/li&gt;
&lt;li&gt;이는 코드의 안정성과 신뢰성을 높이는 데 기여한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;리팩토링 지원&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;코드를 개선하거나 업데이트할 때, 기존의 기능이 여전히 올바르게 작동하는지 확인하는 데 도움이 된다. &lt;/li&gt;
&lt;li&gt;테스트 코드가 있으면 보다 더 과감한 변경을 시도할 수 있다는 장점이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;문서화 기능&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;테스트 코드는 종종 코드의 사용 방법을 보여주는 예제로 사용된다. &lt;/li&gt;
&lt;li&gt;새로운 개발자가 프로젝트에 참여할 때, 테스트 코드를 통해 기존 코드를 더 잘 이해할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;디자인 개선&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;테스트를 작성하면서 개발자는 코드의 구조와 설계에 대해 더 깊이 생각하게 된다. &lt;/li&gt;
&lt;li&gt;괜히 테스트를 위한 코드를 작성하는 것, TDD 등의 개념이 나온 것이 아니며,&lt;/li&gt;
&lt;li&gt;더 깔끔하고 유지보수하기 쉬운 코드를 작성하는 데 도움을 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;JavaScript에서 테스트 코드를 작성하기 위한 인기 있는 도구로는 Jest, Mocha, Jasmine 등이 있다.&lt;br&gt;여러 테스트 방법들이 있으며, 그 중 많이 쓰이는 테스트로는 단위 테스트(unit tests)에서부터 통합 테스트(integration tests) 및 E2E(End-to-End) 테스트가 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;테스트코드를 작성하지 않아서 생겼던 문제들&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;테스트코드를 작성하지 않았다는 소리는 내가 시험을 보고 채점을 하지 않았다는 소리와 같다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;실제로도 그랬다. 정말 채점을 하지 않았던 것처럼 나중에 같은 문제가 나왔을 때 틀렸던 부분에서 다시 틀리는 것처럼 그러한 문제가 발생했던 것이다.&lt;/p&gt;
&lt;p&gt;위에서 알아본 테스트코드를 작성해야하는 이유에서 알 수 있듯이 실제로 돌아가는 데에서도 문제가 발생하는 가장 근본적인 이유에서부터 같은 프로젝트를 다른 사람들에게 합류를 요청하고 제안을 해야하는데 실제로도 테스트코드가 없어서 각 기능들을 문서화 해야했고, 이는 다른 사람들이 코드를 이해하는 데 더욱 많은 시간을 소요하게 만들기도 했다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;그렇다면 테스트코드는 어떻게 작성해야할까?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;프론트엔드는 테스트코드가 비교적 중요도가 떨어진다던데, 굳이 작성 해야해?&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;실제로도 테스트코드가 백엔드에 비해 덜 중요한 것은 사실이다. 물론 어디까지나 &amp;quot;상대적으로&amp;quot;를 의미하기에 결국은 중요하지 않다고 이해하는 것은 옳지 않다.&lt;/p&gt;
&lt;p&gt;이 글에서는 간단하게 단위 테스트를 작성하는 방법 몇 가지를 소개해보겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;1. 기본적인 테스트 구조&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;테스트는 기본적으로 &amp;quot;내가 이 입력값을 넣을테니 이런 결과가 나오는지 확인해줘&amp;quot; 라는 말을 만족시키기 위해 돌아간다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이는 실제 테스트코드 예시를 통해 알 수 있는데, 그 중 한가지 구조를 보도록 하겠다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;describe(&amp;quot;DragonFly 모델 테스트&amp;quot;, () =&amp;gt; {
  test(&amp;quot;입력받은 값이 비어있는 값인지 확인&amp;quot;, async () =&amp;gt; {
    const input = &amp;quot;&amp;quot;;
    expect(() =&amp;gt; {
      new DragonFly(input);
    }).toThrow(ERROR_CONVENTION);
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드에서 볼 수 있듯 &amp;quot;describe&amp;quot;를 통해 어떤 것에 대한 테스트를 진행할 지 &amp;quot;묘사&amp;quot;하고, &amp;quot;test&amp;quot;를 통해 어떤 테스트를 진행해볼 지 상세하게 &amp;quot;설명&amp;quot;하며, &amp;quot;expect to ...&amp;quot;를 통해 기대하는 바가 제대로 출력되는지 확인해본다&lt;br&gt;문장으로 다시 변경해보겠다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Describe &amp;quot;What to test&amp;quot;, Test &amp;quot;How to test&amp;quot;, expect to (Be, Equal, Throw ...) specific result&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;무슨 말인지 한번에 이해하기 힘들다면? 아래에 어떻게 작성할 수 있는 지 확인해보자.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;단위테스트 기본원칙&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;단위테스트는 가장 기본적인 로직에 대해 진행하는 근본적인 테스트이다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;쉽게말해 단위테스트는 시스템 흐름을 살펴보는 것이 아닌 시스템 흐름에 들어가있는 기능 하나하나에 대한 테스트를 진행하는 것이다.&lt;/p&gt;
&lt;p&gt;아래에 여러 설정을 확인해보겠다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;단위 테스트는 최소한의 기능을 가진 함수에 대한 테스트이다.&lt;ul&gt;
&lt;li&gt;여기서 기본적으로 테스트를 하기 위해 최소한의 기능만을 가지고있는 지 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단위 테스트를 하기 위해 보다 상세하게 테스트 목적을 적어야 한다.&lt;ul&gt;
&lt;li&gt;테스트 목적이 상세하면 상세할 수록 코드 유지보수 및 시간이 흘렀을 때 나 자신 혹은 다른 사람들이 기존의 코드를 보고 이해하는 시간이 단축된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단위 테스트를 작성할 때 변수명 또한 가급적 이해하기 쉽게 작성한다.&lt;ul&gt;
&lt;li&gt;&amp;quot;input&amp;quot;, &amp;quot;output&amp;quot;보다는 &amp;quot;numberData&amp;quot;, &amp;quot;expectedStringData&amp;quot; 처럼 보다 읽었을 때 파악하기 쉽도록 작성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3&gt;단위테스트 작성하기: 예상하는 결과가 단일 값일 경우&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;describe(&amp;quot;테스트의 단위를 설명해두는 부분&amp;quot;, () =&amp;gt; {
  test(&amp;quot;단위 내 상세 테스트 정보를 적어두는 부분&amp;quot;, () =&amp;gt; {
      // 이쯤 빈공간에 변수들 선언또는 생성해두기
    expect(expectedValue).toBe(singleValue); // 단일 값의 경우 .toBe()
  });

    test(&amp;quot;...다른 테스트&amp;quot;, () =&amp;gt; {});
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아래와 같은 예시를 볼 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;describe(&amp;quot; Orange Model 테스트&amp;quot;, () =&amp;gt; {
  test(&amp;quot;오렌지 이름에 &amp;#39;싱싱한&amp;#39;이 포함되어 있다면 &amp;#39;fresh&amp;#39; 라는 문자열을 반환한다.&amp;quot;, () =&amp;gt; {
      const orangeName = &amp;quot;싱싱한 제주도 오렌지&amp;quot;;
      const testCase = checkOrangeStatus(orangeName);
      const expectedOrangeStatus = &amp;quot;fresh&amp;quot;;

    expect(testCase).toBe(expectedOrangeStatus);
  });

    test(&amp;quot;...다른 테스트&amp;quot;, () =&amp;gt; {});
});&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;단위테스트 작성하기: 예상하는 결과가 배열 또는 객체일 경우&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;describe(&amp;quot;테스트의 단위를 설명해두는 부분&amp;quot;, () =&amp;gt; {
  test(&amp;quot;단위 내 상세 테스트 정보를 적어두는 부분&amp;quot;, () =&amp;gt; {
      // 이쯤 빈공간에 변수들 선언또는 생성해두기

    expect(expectedValue).toEqual(multpleValue); // 다중 값의 경우 .toEqual()
  });

    test(&amp;quot;...다른 테스트&amp;quot;, () =&amp;gt; {});
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아래와 같은 예시를 볼 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;describe(&amp;quot; Orange Model 테스트&amp;quot;, () =&amp;gt; {
  test(&amp;quot;오렌지 이름에 &amp;#39;싱싱한&amp;#39;이 포함되어 있다면 배열에 &amp;#39;fresh&amp;#39; 라는 문자를 추가한다.&amp;quot;, () =&amp;gt; {
      const orangeName = &amp;quot;싱싱한 제주도 오렌지&amp;quot;;
      const testCase = checkOrangeStatus(orangeName)
      const expectedOrangeStatus = [&amp;quot;orange&amp;quot;, &amp;quot;Jeju-Island&amp;quot;, &amp;quot;fresh&amp;quot;];

    expect(testCase).toEqual(expectedOrangeStatus);
  });

    test(&amp;quot;...다른 테스트&amp;quot;, () =&amp;gt; {});
});&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;단위테스트 작성하기: 예상하는 결과가 에러를 던지는 것일 경우&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;describe(&amp;quot;테스트의 단위를 설명해두는 부분&amp;quot;, () =&amp;gt; {
  test(&amp;quot;단위 내 상세 테스트 정보를 적어두는 부분&amp;quot;, () =&amp;gt; {
      // 이쯤 빈공간에 변수들 선언또는 생성해두기

    expect(() =&amp;gt; {
      new TestClass(&amp;quot;Set Value Here&amp;quot;);
    }).toThrow(&amp;quot;여기의 문자열이 Throw된 메세지에 포함되는지 확인&amp;quot;);
  });

    test(&amp;quot;...다른 테스트&amp;quot;, () =&amp;gt; {});
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아래와 같은 예시를 볼 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;describe(&amp;quot; Orange Model 테스트&amp;quot;, () =&amp;gt; {
  test(&amp;quot;오렌지 이름에 &amp;#39;싱싱한&amp;#39;이 포함되어 있지 않다면 에러를 발생한다.&amp;quot;, () =&amp;gt; {
      const orangeName = &amp;quot;쌩쌩한 제주도 오렌지&amp;quot;;
      const testCase = checkOrangeStatus(orangeName);

    expect(checkOrangeStatus(orangeName)).toThrow(&amp;quot;[ERROR]&amp;quot;);
    // 던져지는 에러에 &amp;quot;[ERROR]&amp;quot;라는 문자열이 포함되어있어야 한다.
  });

    test(&amp;quot;...다른 테스트&amp;quot;, () =&amp;gt; {});
});&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3&gt;단위테스트 작성하기 Plus: 같은 테스트를 여러 값에 대해 진행하고 싶을 때&lt;/h3&gt;
&lt;p&gt;이렇게 하나의 케이스에 하나씩 테스트를 해볼 수 있지만, 생각외로 여러 케이스에 대한 테스트를 진행해보아야 할 때가 많은 실제 요구사항에서는 테스트코드를 여러 개 복사해서 사용할 수도 없는 노릇이다.&lt;br&gt;이럴 때에는 어떻게 해야할까?&lt;/p&gt;
&lt;p&gt;A. 아래와 같이 &amp;quot;test.each()&amp;quot;를 사용하여 메서드를 여러 개 확인해본다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;describe(&amp;quot;calculate util 테스트&amp;quot;, () =&amp;gt; {
  const testCases = [
    [[100, 20], 120],
    [[123, 45], 168],
    [[1, 2, 3, 4, 5], 15],
    [[0, 0, 1, 1000, 2000], 3001],
    [[300, 10, 4000, 32], 4342],
  ];

  test.each(testCases)(
    &amp;quot;배열을 집어넣었을 때 배열 내의 값의 합이 정상적으로 출력되어야 한다.&amp;quot;,
    (arrayItem, expectedResult) =&amp;gt; {
      const testCase = sumArray(arrayItem); // sumArray 또는 해당 계산을 수행하는 함수 사용

      expect(testCase).toBe(expectedResult);
    }
  );

  test(&amp;quot;...다른 테스트&amp;quot;, () =&amp;gt; {});
});&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;배열에 들어있는 각각의 값이 orangeNames에 들어가서 테스트를 한번씩 실행시킨다. 즉, 같은 테스트에 대해 여러 값을 돌려보며 테스트할 수 있다는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이제, 본인이 작성한 코드에 맞게 테스트를 잘 작성하여 구현한 기능이 잘 돌아가는 지 확인해보자!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;End&lt;/strong&gt;&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>JavaScript</category>
      <category>jest</category>
      <category>Unit Test</category>
      <category>단위 테스트</category>
      <category>오렌지</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/262</guid>
      <comments>https://time-map-installer.tistory.com/262#entry262comment</comments>
      <pubDate>Mon, 13 Nov 2023 13:24:48 +0900</pubDate>
    </item>
    <item>
      <title>&amp;lt;우아한테크코스 2024 프리코스&amp;gt; 3주차 후기: 로또 10억원어치 사면 당첨되나요?</title>
      <link>https://time-map-installer.tistory.com/261</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BkE8B/btsz20DN8By/uOpQQYJA0KipnwF2fy8Tr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BkE8B/btsz20DN8By/uOpQQYJA0KipnwF2fy8Tr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BkE8B/btsz20DN8By/uOpQQYJA0KipnwF2fy8Tr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBkE8B%2Fbtsz20DN8By%2FuOpQQYJA0KipnwF2fy8Tr0%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;364&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;450&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;u&gt;&lt;b&gt;깃허브 바로가기&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1699449767303&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - TMInstaller/javascript-lotto-6&quot; data-og-description=&quot;Contribute to TMInstaller/javascript-lotto-6 development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/TMInstaller/javascript-lotto-6/tree/tminstaller&quot; data-og-url=&quot;https://github.com/TMInstaller/javascript-lotto-6&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/3aXnQ/hyUrxo58WO/Ky1Fcf0ippEzkfdlvz62xk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/TMInstaller/javascript-lotto-6/tree/tminstaller&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/TMInstaller/javascript-lotto-6/tree/tminstaller&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/3aXnQ/hyUrxo58WO/Ky1Fcf0ippEzkfdlvz62xk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - TMInstaller/javascript-lotto-6&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to TMInstaller/javascript-lotto-6 development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2주차엔 어땠는지 보고싶다면? &lt;a href=&quot;https://time-map-installer.tistory.com/260&quot;&gt;2주차: 자동차 게임 보러가기&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;lt;2주차 Plus&amp;gt;제출 직전의 스프린트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2주차 자동차 경주 과제에서 나는 제출 직전에 갑작스럽게 몰입이 되는 상황을 겪었다. 왜 였을까? 갑자기 끝나기 직전이 되니 집중이 잘된다니? 사건의 전말은 이러하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 분명 내 코드를 보면서 &quot;음음 이런 피드백이 나오겠지? 이렇게 고치면 되겠다 라는 피드백이 오겠지? 라고 생각하고 있었다. 이왕이면 내가 스스로 피드백 한 부분들을 주석으로 달아두면 더 좋을 것 같다고 생각하여 아래와 같이 주석을 달아두었다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;// self-feedback: 이거 뭔가 이렇게 하면 좋을 듯&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;갑자기 머릿속에서 들려온 한 마디, &quot;한시간 정도 투자하면 지금 내가 한 피드백 바로 적용해볼만 하겠는데?&quot;&lt;br /&gt;이 이후로 즉시 그 어느 때보다 긴박한 순간에서 피드백 적용을 위한 시도가 이루어졌다.&lt;br /&gt;시간이 빠르고도 느리게 흘러가며 온 집중을 코드 수정에 쏟은 결과, 신기하게도 제출 전에 모두 피드백을 반영하고, 모든 테스트에 통과하며, 제출 페이지에 까지 무사히 통과하는 것을 성공시켰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 한 클린코드 관련된 영상에서 반복해서 언급 했던 말 중 &quot;코드 구조를 바꾸는 것에 두려워하지 말아요&quot; 에 영향을 크게 받았다. 이전까지만 해도 코드를 짜다가 구조를 변경하는 것에 두려움이 있어 이를 변경하는 것을 기피하다시피 했는데, 생각해보니 깃으로 미리 커밋을 해두면 될 일이었고, 그렇게 해서 더 깔끔해지면 모두에게도 좋은 결과가 나오기에 두려울 필요가 없었다는 것을 깨달을 수 있었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어우 진짜 감기 너무 오래가네.. 이거 뭔가 잘못된 거 아냐?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 2주차 과제 진행 시작과 동시에 감기에 걸렸던걸로 알고있는데.. 왜 제출하는 이순간까지 감기에 걸려있는거지..? 비염 -&amp;gt; 두통 -&amp;gt; 잦은 기침 순으로 변화해오며 몸이 회복 되는 듯 아닌 듯 했다. 어쩌다보니 이번 과제는 가벼운 감기와 함께했던 과제인 것 같다. 2주차 시작 시점에 감기에 걸렸지만 3주차 제출 시점에 감기가 다 나아가는 뭔가 이상한 상태에 있던 나는 지금부터 배운 것도 많고, 성장도 많이 한 프리코스 3주차 후기를 시작하겠다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2주차의 성공적인 코드리뷰, 무엇을 배웠을까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주에도 정말 많은 것들을 배워갈 수 있었다. 다른 사람들의 코드를 보며 배웠던 내용들, 그리고 코드 리뷰를 받으며 얻은 깨달음 등이 참 많았는데, 간단하게 정리해보자면 아래와 같았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;Object.freeze&lt;/code&gt;를 이용하면 선언한 상수가 외부에서 수정 불가능한 얼어붙은 상태가 된다.&lt;/li&gt;
&lt;li&gt;테스트 코드의 경우 &lt;code&gt;file.test.js&lt;/code&gt;와 같이 &lt;code&gt;.test&lt;/code&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;li&gt;&lt;code&gt;++&lt;/code&gt;, &lt;code&gt;--&lt;/code&gt;와 같은 연산자는 예기치 못한 오류를 발생할 수 있기에 for문에서 이를 사용하고 있다면 변경해주도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;const&lt;/code&gt;와 &lt;code&gt;let&lt;/code&gt;을 구분지어 사용하도록 한다. 거의 대부분의 변수는 &lt;code&gt;const&lt;/code&gt;에 할당된다.&lt;/li&gt;
&lt;li&gt;같은 변수에 다시 값을 재 할당 하는 것은 후에 버그의 원인이 된다. &lt;code&gt;const&lt;/code&gt;로 고정해주고 새로운 변수를 생성하여 할당하도록 하자.&lt;/li&gt;
&lt;li&gt;출력 단위까지 분리해버리는 것이 좋다(&lt;code&gt;Console.print&lt;/code&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;li&gt;추가로 무언가 기능이 요구될 때 변환하기 용이하도록 코드를 작성하라&lt;/li&gt;
&lt;li&gt;&lt;code&gt;js convention&lt;/code&gt;에서는 &lt;code&gt;if문&lt;/code&gt; 이후에 줄바꿈이 된다면 한줄만 작성되어도 중괄호{}를 쓰는 것을 추천한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;static&lt;/code&gt;을 메서드 앞에 붙이면 &lt;code&gt;new Class()&lt;/code&gt;로 인스턴스를 생성하지 않고도 해당 메서드를 사용할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이를 활용해보려 했으나 사용하려는 근거가 명확하지 않았기에 &lt;code&gt;function()&lt;/code&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;잘 적용한 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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;p&gt;&lt;img src=&quot;https://i.imgur.com/kO2GATN.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;정말 생각지도 못하고 있었는데.. 감사함을 표합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ClgjZ0A.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이번엔 커밋 컨벤션도 잘 지킨 것 같다!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코수타(코치들과의 수다 타임)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주 코수타에서는 신사옥을 구경했다. 코치님께서 진행하시는 v-log를 보며 &quot;와 저기서 일하면 없던 힘도 날 것 같다..!&quot; 와 같은 생각들을 하며 구경을 하고, 테코톡 촬영현장을 보고, 아래와 같은 조언을 얻었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;무언가 하나를 계속해서 깊게 파는 것을 높이 평가한다&lt;/li&gt;
&lt;li&gt;겉만 훑고 지나가는 사람들도 좋지는 않지만, 해결안되는데 시간 계속 써서 파는 그런 것은 지양하자. 시간은 한정적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시작할 때 더욱 신중해야해&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주, 2주차 때 했던 실수를 하지 않기 위해, 나는 필수적으로 해야 할 일들을 이번에는 넘기지 말고 꼭 하고 시작하자라는 마음가짐으로 순서에 따라 리드미를 작성했다. 그리고 이번엔 성공적으로 요구사항들을 지켜가며 잘 구현한 것 같았고, 나름 만족하면서 구현을 진행했다.&lt;br /&gt;시작할 때 브랜치 파고, 리드미 먼저 적어두고, 업데이트하면서, 컨벤션 지키면서, 하드코딩 다 대체하고, 동일한 기능 하는 것들 메서드화 시키고, 메서드 길이 준수 최대한 하면서, 1기능 원칙 최대한 지키면서 시간이 더 걸리더라도 신중하게 코드를 작성하고 변경했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 총 39개의 단위 테스트가 진행되었고, 모두 통과시키는 엄청난 결과를 내었다..!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/IfDmfAw.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;와 !&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스스로에게 박수 !&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/0T1JsAW.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3주차까지 오면서 급격하게 늘어난 코드라인 수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 과제를 진행하면서 작성한 코드량이 전주차에 비해 급격하게 많아져서 모듈화를 잘 해두어서 그런가 생각했었다. 그러다 문득 궁금해져서 PR의 코드라인 수를 확인해 보았는데&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1주차: 약 200라인&lt;/li&gt;
&lt;li&gt;2주차: 약 400라인&lt;/li&gt;
&lt;li&gt;3주차: 약 1200라인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리드미가 약 300라인 정도 된다는 것을 감안해도 굉장한 증가폭을 보여준다. 주식도 이렇게는 안 오를텐데 뭔가 내 주가가 엄청 뛰어오르는 듯한 느낌이 들었다. 이제 가즈아를 외치면 되는걸까?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이번 로또를 구현하며 알게된 사실들&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 로또라는 다소 복잡한 요구조건을 지니고 있는 문제를 구현하면서 몇 가지 새로 안 사실들이 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;숫자 배열은 로그를 찍으면 배열형태로 나오지 않아요
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;[1,2,3,4,5]&lt;/code&gt; 의 숫자 배열은 로그를 찍었을 때 &lt;code&gt;1,2,3,4,5&lt;/code&gt;처럼 그냥 숫자가 나열된 형태로만 나온다.&lt;/li&gt;
&lt;li&gt;따라서 아래와 같이 템플릿 리터럴을 사용하여 요구조건을 만족시켰다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;Console.print(`[${newLottoNumbers.join(&quot;, &quot;)}]`);`&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;짧은 회로
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;짧은 회로라고, 말 그대로 짧게 &lt;code&gt;boolean&lt;/code&gt; 값을 이용하여 두개의 값 중 하나를 배정하는 로직이다.&lt;/li&gt;
&lt;li&gt;굉장히 특이했고, 사용할 수 있는 부분이 많을 것 같아서 기억해두려고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export function checkBonusBallIsMatching(hasBonus, rankCounts) {
  // 짧은 회로를 통해 hasBonus가 참일 경우 rankCounts[2], 거짓일 경우 rankCounts[3] 값 증가
  // 배열의 특성 상 return없이 직접 수정되는 효과 보유
  rankCounts[hasBonus ? 2 : 3] += 1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;충격적인 사실
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JavaScript는 아래와 같이 연속으로 두개의 비교연산자를 사용하는 것이 불가능하다&lt;/li&gt;
&lt;li&gt;옆집 파이썬은 이거 되는데에에!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;#isNumberInRange(string) {
    if (!(LOTTO_NUMBERS.minimum &amp;lt;= Number(string) &amp;lt;= LOTTO_NUMBERS.maximum)) {
      // 이러면 코드 안돌아갑니다
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;4&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;모델 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델을 생성할 때 &lt;code&gt;#&lt;/code&gt;을 변수명, 메서드명 앞에 붙이면 외부에서 해당 메서드와 변수를 참조할 수 없게 된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;constructor()&lt;/code&gt;에 &lt;code&gt;this.method(string)&lt;/code&gt;를 넣어둔다면 클래스 호출 시 &lt;code&gt;constructor&lt;/code&gt;에 들어가있는 모든 &lt;code&gt;string&lt;/code&gt;이 들어간 클래스의 메서드들을 실행시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트코드에서도 알아낸 것이 있죠?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론이다. 테스트코드를 작성하는 것에서도 어느 정도 상황에 따른 테스트 작성법을 알 수 있었다.&lt;br /&gt;아래와 같이 여러 상황 별로 작성할 수 있는 방법들을 만들어 보았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트는 &lt;code&gt;describe&lt;/code&gt;를 통해 가장 밖에서 묶어주고, 각각의 케이스를 &lt;code&gt;test&lt;/code&gt;를 통해 진행해주고, 그 테스트 내부에 &lt;code&gt;expect().&amp;lt;methods&amp;gt;&lt;/code&gt; 를 통해 진행된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;expect().toBe()&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주로 숫자, 문자, boolean 등의 단일 값에서 확인하는 케이스에서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;expect().toEqual()&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주로 배열, 객체 등의 Object 단위 값을 확인하는 케이스에서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;expect().toThrow()&lt;/code&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;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;describe(&quot;테스트의 단위를 설명해두는 부분&quot;, () =&amp;gt; {
  test(&quot;단위 내 상세 테스트 정보를 적어두는 부분&quot;, () =&amp;gt; {
    // 이쯤 빈공간에 변수들 선언또는 생성해두기

    // expectedValue가 단일 값인 경우
    expect(expectedValue).toBe(singleValue); // 단일 값의 경우 .toBe()
    expect(expectedValue).toEqual(multpleValue); // 다중 값의 경우 .toEqual()
    // expectedValue라는 return값이 존재하는 함수에 대한 예외처리인경우
    expect(() =&amp;gt; {
      new TestClass(&quot;Set Value Here&quot;);
    }).toThrow(&quot;여기의 문자열이 Throw된 메세지에 포함되는지 확인&quot;);
  });

    test(&quot;...다른 테스트&quot;, () =&amp;gt; {});
});&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그리고 가장 중요하게 깨달은 사실, class는 교실이 아닙니다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/259&quot;&gt;깨알같은 다른 글 홍보&lt;/a&gt;&lt;br /&gt;바로 &lt;code&gt;class 모델&lt;/code&gt;을 생성할 때 하나의 값을 가진 클래스는 하나의 필드만 가지고 있어야 한다고 생각했다. 왜냐하면 클래스에 값이 형태 상관 없이 하나만 들어오게 되어있으니깐 그럴 것이라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 모든 구현이 끝나고 하나의 궁금증이 생겼다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 굳이 하나의 형식만을 입력값으로 받는걸까? 여러 형식의 값을 입력받을 수는 없는걸까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 궁금증이 생긴 이유는 내가 모델을 생성하고 유효성 검사를 하는 데 있었다.&lt;br /&gt;기본적으로 입력받은 값들이 string값으로 들어오기에, 형 변환을 그냥 이 string 값을 받아서 변환해가면서 유효성 검사를 하도록 구현 해두었다.&lt;br /&gt;하지만 나중에 코드량이 늘어난다면 헷갈려질 것 같으면서도 추후에 다른 곳에서 초기 형태의 값이 아닌 다른 형태(number, arr) 등에 해당하는 같은 모델에 다른 형태의 값이 들어올 때 테스트를 진행해야 한다면 어떡하지? 라는 고민이 생겼었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이번 주차에서는 하나의 값에 하나의 클래스를 적용했고, 입력값의 경우 string 문자열을 받아서 처리하는 로직을 구현했다.&lt;/li&gt;
&lt;li&gt; 더 나은 방법이 있을 것이라고 분명 생각하고 있고, 이를 다음 주에 적용해보려고 한다.&lt;/li&gt;
&lt;li&gt;확실히 아직 클래스를 능숙하게 다루는 단계는 아니다 보니 기초적인 형태로 여러 모델을 만들고 실험했던 것 같다. 다음번엔 더 발전된 클래스를 작성하기 위해 정보를 모아봐야겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;안심하세요, 이번에는 억까가 없었어요&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/7TjzpR0.png&quot; alt=&quot;&quot; /&gt;&lt;img src=&quot;https://i.imgur.com/q9XXde8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사진을 보아 알 수 있듯이 이번엔 예기치 못한 오류 없이, 폴더 구조를 변경하지 않아도 정상적으로 테스트가 통과되었다는 것을 알 수 있다. 정말 다행이다. 휴&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로또 자동으로 10억원어치 사면 당첨 되나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문득 이번 주에 올라온 로또 번호를 여기에 넣어보면 당첨이 될까 싶어 내가 만든 프로그램에서 돌려보기로 했다. 구매금액은 &lt;code&gt;JavaScript&lt;/code&gt;의 &lt;code&gt;Number&lt;/code&gt;범위 제한으로 인해 2억원씩 5번 총 10억원어치 구매해보았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이때 아차 싶었다. &lt;code&gt;BigInt&lt;/code&gt;처리를 할 생각을 이때는 왜 못했을까..! ㅠ
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라고 생각했지만 누가 한번에 10억원어치 구매를 하겠는가. 2억원이어도 20만장인데 ㅋㅋㅋㅋ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/2RSKqss.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;두근두근한 순간이다. 그래도 100만장을 사는 건데 하나는 당첨되지 않을까?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/WPS2VLj.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;번호를 입력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1회차 - 2억(20만장) 구매&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/PvjRuqo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;수익: 2980,0000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2회차 - 2억(20만장) 구매&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/WhT8Hiw.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;수익: 3440,0000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3회차 - 2억(20만장) 구매&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1TqVfAj.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;수익: 3600,0000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4회차 - 2억(20만장) 구매&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/AcFVwOP.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;수익: 2800,0000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5회차 - 2억(20만장) 구매&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/2vHSIkf.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;수익: 3240,0000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 수익: 약1,6060,0000&lt;br /&gt;총 구매: 100만장(20만X5)&lt;br /&gt;1억 6천만원 벌려고 10억을 태운거야..?&lt;br /&gt;그리고 이와중에 1등은 커녕 2등 아무것도 안나온거...&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/l3bqnh9.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;묻고.. 가!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;묻어야지 그냥 뭘 자동 돌리려고 그래&lt;br /&gt;이럴꺼면 9억을 기부하고 말지&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/개인</category>
      <category>2024</category>
      <category>6기</category>
      <category>로또</category>
      <category>우아한 테크코스</category>
      <category>우테코</category>
      <category>프리코스</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/261</guid>
      <comments>https://time-map-installer.tistory.com/261#entry261comment</comments>
      <pubDate>Thu, 9 Nov 2023 00:01:25 +0900</pubDate>
    </item>
    <item>
      <title>&amp;lt;우아한테크코스 2024 프리코스&amp;gt; 2주차 후기: 자동차게임</title>
      <link>https://time-map-installer.tistory.com/260</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzmNU6/btszyDRI7W6/WZkcGMWzuxBTbU9y1I3ug1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzmNU6/btszyDRI7W6/WZkcGMWzuxBTbU9y1I3ug1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzmNU6/btszyDRI7W6/WZkcGMWzuxBTbU9y1I3ug1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzmNU6%2FbtszyDRI7W6%2FWZkcGMWzuxBTbU9y1I3ug1%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;741&quot; height=&quot;450&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;이 포스팅은 회고 겸 TroubleShooting 겸 한 주 간의 고민과정을 모두 정리해둔 글임을 알립니다&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/TMInstaller/javascript-racingcar-6&quot;&gt;Visit GitHub Repository&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/258&quot;&gt;1주차 후기 바로가기&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;코수타, 코치들과의 수다 타임&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;물론 이 시점에서도 나는 수업을 듣고있었기에 집에 가는 길에 모두 들으며 유익한 시간을 보냈다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=xwqIBM-vOdU&quot;&gt;https://www.youtube.com/watch?v=xwqIBM-vOdU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;내 개인적인 입장에서는 참 아쉬운 시간대라고 생각한다. 하필 수업이 시작하는 그 시점에 이런 재미있는 행사들이 벌어지니 말이다. 이 이후에도 진행했던 5기 분들과의 이야기 시간 또한 수업시간과 겹쳐서 굉장히 아쉽다고 생각하고는 있다. 그래도 다시보기가 있어 참 다행이라고 생각하는데, 여기서 재밌는 말 하나를 들었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;요구사항 분석을 제대로 하지 않고 작성한 코드는 결국 양념소스만 잘 되어있는 덜익은 치킨과 같아요.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;어느 순간부터 음식과 관련된 이야기가 나오다가 질문을 받더니 음식으로 멋진 비유를 해주신 코치님들, 개인적으로 이해하기 쉽게 비유하기를 좋아하는 나에게는 정말 인상깊은 말이라 메모해두었다. 그 말이 맞다고 생각하는 것이, 요구사항을 모두 지키지 않고 작성한 코드는 겉보기에는 정상적으로 완성된 것 같은 양념치킨 같아보여도 안을 들여다보면 덜익어서 먹었을 때 탈이 날 수도 있다는 것과 같기 때문이다.&lt;br&gt;이 외에도 여러 질문들과 답변들이 오갔고, 이를 들으면서 유익한 시간을 보내며 집으로 돌아갔다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;1주차보다 월등히 향상된 실력, 적응해서일까?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;적응해서인지 내가 잘해져서인지는 알 수 없다. 다만 요구분석부터 잘 돌아가는 코드로직을 짜는 그 순간까지는 전주차보다 훨씬 빨라져있었다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;말 그대로이다. 시간을 재 둔 것이 있어서 이를 통해 비교를 해보았었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;요구사항 분석 및 구현할 기능 정리&lt;ul&gt;
&lt;li&gt;3시간 -&amp;gt; 45분&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코드로직 + 예외처리 작성&lt;ul&gt;
&lt;li&gt;5시간 -&amp;gt; 2시간&lt;br&gt;확실히 둘 다 절반 그 이하로 시간이 단축된 것을 알 수 있었는데, 이는 그 당시 몸상태가 좋지 않은 상태였음에도 몰입이 제대로 되었다는 것을 의미한다.&lt;br&gt;물론 여러 이유들이 있을 것일라고 추측하고 있다. 1주차에 처음 써보는 객체지향형 프로그래밍에 적응하는 시간이 필요했을 수도 있고, 이미 한 번 해 본 상태에서 전주차보다 조금 더 로직이 쉬워서(혹은 쉽게 느껴져서) 그러는 것일수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/fYTd5bd.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이번 과제는 지난 주 보다는 반복되는 로직이 적어 더 쉽게 느껴졌다.&lt;/p&gt;
&lt;p&gt;가장 큰 이유가 있다면 이번에 2주차 안내할 때 말해주었던 한 가지의 안내사항과 관련이 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/iGHxbIS.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;여기서 함수를 분리하고 각 함수별로 테스트를 작성하는 것에 익숙해지는 것, 이 중 테스트를 작성하는 것에 대해서는 지난 주 &lt;code&gt;특정 에러&lt;/code&gt;를 잡기위해 테스트를 뜯어보았던 것이 굉장히 도움이 되었다고 생각한다. 물론 이번에 조금 더 복잡하고 상세한 테스트를 구성하였다면 좋았겠지만, 그래도 예외처리가 제대로 돌아가는지 만큼은 확실히 확인해볼 수 있었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;이번에 코드 어떻게 분리했어요?&lt;/h3&gt;
&lt;p&gt;1주차 때 함수를 분리하지 않고 그냥 한 파일에 모두 다 넣어놓았다면 이번에는 큰 기능을 하는 단위로 클래스를 분리하고, 그 안에 비슷한 기능을 하는 메서드들을 모아두는 형태로 코드를 분리했다.&lt;br&gt;또한 공통적으로 사용하는 조건이 붙어있는 숫자, 에러코드들을 모아두었으며, 에러코드에서 공통적으로 사용 되는 컨벤션인 &lt;code&gt;[ERROR]&lt;/code&gt; 문자열 또한 분리해서 관리했다.&lt;br&gt;이렇게 정리하니 확실히 전보다 유지보수가 더 좋아진 느낌이었고, 오류가 발생했을 때 손쉽게 해결할 수 있어서 좋았다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;이번에 테스트 어떻게 구성했어요?&lt;/h3&gt;
&lt;p&gt;테스트는 이번에는 주로 예외처리에 관한 코드들로 구성하였다. 적어도 내가 직접 만들고 작성했던 부분에 대한 대부분의 예외에서는 테스트를 다 해보자 라는 생각으로 코드를 구성하였다.&lt;/p&gt;
&lt;p&gt;주로 사전에 주어진 mocking된 데이터에서 오류가 나는 지 확인하는 방법으로 구성을 하였고, 예외처리와 관련된 테스트코드가 주를 이루었다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ShOLeea.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;직접 추가하고 돌렸던 테스트가 성공적으로 돌아가는 모습을 보니 기분이 좋았다.&lt;/p&gt;
&lt;p&gt;위에 있는 &lt;code&gt;Test Suites&lt;/code&gt;는 테스트 파일의 개수인 것 같았고,&lt;br&gt;아래에 있는 &lt;code&gt;Tests&lt;/code&gt;는 모든 테스트 개수를 의미하는 것 같았다.&lt;br&gt;이렇게 추가해서 코드가 돌아가는 모습을 보니 이제 프로젝트를 할 때 테스트코드도 작성하며 할 수 있을 것 같은 느낌이 들었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;마지막에 예기치 못한 오류가..! 그래도 해결해냈다!&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/2vlZHY6.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;물론 지금은 모두 해결을 한 상태로, 문제없이 예제 테스트에 통과하였지만, 처음 오류가 발생하였을 때에는 굉장히 당황했었다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/LCIbnKE.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;아마 누구라도 제출했을 때 저런 오류가 발생한다면 굉장히 당황했을 것이다. 그 당시 나는 실제로 두려웠다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&amp;quot;분명 실행도 잘 되었고, 테스트도 모두 통과했는데.. 왜..?&amp;quot;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;다행히 1주차때 많은 사람들이 올려준 해결방법들이 있었기에 하나하나 적용해보며 해결할 수 있었다.&lt;/p&gt;
&lt;p&gt;이 문제는 빌드 오류로, 코드를 빌드하는 과정에서 어떤 문제가 생겼음을 나타내는 오류라고 한다.&lt;br&gt;그리고 아래에 올라와있는 해결방법과 시도방법들을 적어두었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;프리코스의 경우에 사전에 작성되어있는 &lt;code&gt;ApplicationTest.js&lt;/code&gt; 파일이 먼저 실행되기에 여기에 &lt;code&gt;App.js&lt;/code&gt;가 올바른 경로로 &lt;code&gt;import&lt;/code&gt; 되어있는 지 확인해야 한다.&lt;ul&gt;
&lt;li&gt;이미 제대로 적용되어있기에 나에게는 적용되지 않는 문제였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;불필요한 &lt;code&gt;console.log&lt;/code&gt;가 남아있는 경우 이럴 수 있다.&lt;ul&gt;
&lt;li&gt;실제로 &lt;code&gt;console.log&lt;/code&gt;가 남아있었기에 이를 지우고 &lt;code&gt;commit&lt;/code&gt; 해보았지만 똑같은 오류가 발생했었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;node_modules&lt;/code&gt;와 &lt;code&gt;package-lock.json&lt;/code&gt; 의 종속성 문제일 수 있기에 이를 지우고 &lt;code&gt;npm i&lt;/code&gt; 를 이용해서 다시 설치해보면 될 것이다.&lt;ul&gt;
&lt;li&gt;가장 유력한 답이라고 생각했지만 해결되지는 않았다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;저는 이렇게 해결했어요!&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;프리코스 커뮤니티의 힘은 굉장하다!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;나는 커뮤니티에 &amp;quot;예기치 못한 오류&amp;quot; 라는 키워드로 검색을 하며 관련해서 사람들이 겪은 문제에 대해 알아보기 시작했고, &lt;code&gt;src&lt;/code&gt; 내에 추가적인 폴더가 있는 경우에 이런 오류가 발생할 수 있다는 대화내용을 보았다.&lt;/p&gt;
&lt;p&gt;그 당시 나는 아래와 같이 파일을 분리해서 코드를 작성했었고, 이는 그 대화내용의 오류 상황과 일치했다.&lt;br&gt;폴더를 통해 한번 더 묶어서 관리를 했고, IDE에서 잘 돌아갔고, 보기에도 예뻤지만, 제출 환경에서는 이를 원하지 않았던 모양이다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Zww83sZ.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그래서 폴더 내부에 있던 모든 파일들을 src 바로 아래에 들어오도록 변경하였다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Mn5v81S.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이렇게 변경하고 제출하니 빌드 오류는 사라졌고, 정상적으로 테스트가 실행되면서 이슈를 해결할 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/2vlZHY6.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;와우! 정말 조마조마했는데 다행이네요!&lt;br&gt;모두 src 안으로 이동했지만 파일명을 잘 지어두어서 알아보기 편해 다행이었다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;과연 나는 1주차 공통 피드백을 2주차에 잘 적용했을까?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;프리코스에서 1주차에 대한 공통적인 피드백을 보내왔다. 이번 주차 후기에서는 이 피드백이 잘 적용되어있는 지 확인해보는 걸 중점적으로 해보겠다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;피드백 내용은 아래와 같았다.&lt;/p&gt;
&lt;h3&gt;1. 요구사항을 정확히 준수한다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;과제 제출 전에 기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항의 항목을 모두 잘 지켰는지 다시 한 번 점검한다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;
&lt;li&gt;기능 요구사항 및 프로그래밍 요구 사항 정확히 지키려고 함&lt;ul&gt;
&lt;li&gt;실행환경 예시와 정확히 똑같은 결과가 나오도록 하기 위한 코드를 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 커밋 메시지를 의미 있게 작성한다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;커밋 메시지에 해당 커밋에서 작업한 내용에 대한 이해가 가능하도록 작성한다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/PhvRCWd.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;아마 이정도면 내용에 대해 괜찮게 작성된 것이라고 본다. 물론 피드백을 받는다면 더 나아질 수 있을 것이다.&lt;/p&gt;
&lt;h3&gt;3. git을 통해 관리할 자원에 대해서도 고려한다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;code&gt;node_modules&lt;/code&gt; 는 &lt;code&gt;package.json&lt;/code&gt; 파일이 있으면 설치할 수 있고 버전 관리를 직접 하지 않으므로 git으로 관리하지 않아도 된다.&lt;br&gt;Intellij의 .idea 폴더, VS Code의 .vscode 폴더 또한 개발 도구가 자동으로 생성하는 폴더이기 때문에 굳이 git으로 관리하지 않아도 된다.&lt;br&gt;앞으로 git에 코드를 추가할 때는 git을 통해 관리할 필요가 있는지를 고려해볼 것을 추천한다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이부분은 간혹 깃허브에 &lt;code&gt;DS Store&lt;/code&gt;이나 &lt;code&gt;.vscode&lt;/code&gt; 등의 코드가 올라온 경우가 있어서 나온 피드백인 것 같다. 이미 잘 작성되어있는 &lt;code&gt;.gitignore&lt;/code&gt;을 통해 올라가지 않는 상황이라 여기는 패스&lt;/p&gt;
&lt;h3&gt;4. Pull Request를 보내기 전 브랜치를 확인한다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;기능 구현 작업을 fork된 Repository의 main branch가 아닌, 기능 구현을 위해 새로 만든 브랜치에서 작업한 후 PR을 보낸다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;여기는 내가 초반에 실수했던 부분이다. 브랜치를 만들어두고 체크아웃을 깜빡한 상태로 코드를 일부 작성했었다. 이를 깨닫고 나서 바로 브랜치를 옮긴 후 코드를 작성했다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/9M69mvg.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3&gt;5. PR을 한 번 작성했다면 닫지 말고 추가 커밋을 한다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;PR을 이미 한 번 보냈다면, 새로운 PR을 생성할 필요가 없다. 수정이 필요하다면 추가 커밋을 하면 자동으로 반영된다. 단, 미션 제출 기간 이후에는 추가 커밋을 하지 않는다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이부분은 기존 프로젝트들을 통해 익혔던 내용으로 잘 지키고 있었다. 이번에는 &lt;code&gt;예기치 못한 오류&lt;/code&gt; 를 해결하기 위해 추가 커밋을 하였다.&lt;/p&gt;
&lt;h3&gt;6. 이름을 통해 의도를 드러낸다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;나 자신, 다른 개발자와의 소통을 위해 가장 중요한 활동 중의 하나가 좋은 이름 짓기이다. 변수 이름, 함수(메서드) 이름, 클래스 이름을 짓는데 시간을 투자하라. 이름을 통해 변수의 역할, 함수의 역할, 클래스의 역할에 대한 의도를 드러내기 위해 노력하라. 연속된 숫자를 덧붙이거나(a1, a2, ..., aN), 불용어(Info, Data, a, an, the)를 추가하는 방식은 적절하지 못하다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이 부분은 리팩터링 하면서 많은 고심을 했던 부분이다. 10분 테코톡과 같은 여러 기업들에서 제공하는 영상들을 보며 클린코드에 대한 것들을 익히고 있는데, 이름만 보고도 확실히 구분할 수 있는 변수명, 메서드명을 짓기 위한 시도들을 했다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/HpTdpfx.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/odDjFS5.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;그래도 읽었을 때 무슨 의미를 하는 지 1주차보다는 더 명확해진 것 같아서 기분이 좋았다.&lt;/p&gt;
&lt;h3&gt;7. 축약하지 않는다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;의도를 드러낼 수 있다면 이름이 길어져도 괜찮다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;위에서 잘 지키고 있는 것 같다고 판단하여 넘어가겠다.&lt;/p&gt;
&lt;h3&gt;8. 축약 예외&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;누구나 실은 클래스, 메서드, 또는 변수의 이름을 줄이려는 유혹에 곧잘 빠지곤 한다. 그런 유혹을 뿌리쳐라. 축약은 혼란을 야기하며, 더 큰 문제를 숨기는 경향이 있다. 클래스와 메서드 이름을 한 두 단어로 유지하려고 노력하고 문맥을 중복하는 이름을 자제하자. 클래스 이름이 Order라면 shipOrder라고 메서드 이름을 지을 필요가 없다. 짧게 ship()이라고 하면 클라이언트에서는 order.ship()라고 호출하며, 간결한 호출의 표현이 된다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;ul&gt;
&lt;li&gt;객체 지향 생활 체조 원칙 5: 줄여쓰지 않는다 (축약 금지)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;여기는 피드백을 받아들여야 하는 부분이다. 지금 작성하면서 확인해보니, 중복되어있으며, 간결한 호출 표현이 되지 않은 네이밍을 발견할 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/7S1uI9M.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;클래스이름과 메서드 이름에 Exception이 중복되어있지 않은가.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;하지만 여기서 궁금한 점이 있다. 실제로 이 부분은 호출되지 않고 결국 이 모든 것을 묶어서 한번에 호출한 &lt;code&gt;check()&lt;/code&gt;라는 메서드만 다른 파일에서 호출되는데, 이를 줄일 필요가 있을까?&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;실제로 이 클래스가 호출되는 &lt;code&gt;App.js&lt;/code&gt; 에서는 &lt;code&gt;InputCarsException.check()&lt;/code&gt; 처럼 간결하게 호출되고 있기 때문이다.&lt;br&gt;대신, &lt;code&gt;check()&lt;/code&gt; 메서드 내에서는 빠르게 확인하기 위해 축약하지 않고 작성하였다. 잘 작성된 코드인 것인지 궁금하다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/XUWz0mc.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;이런 느낌으로 호출 시점에서는 간결하게 표현되어있는 것 같다.&lt;br&gt;아, 물론 &lt;code&gt;RacingGame.racing()&lt;/code&gt;은 한 번 변경하는 것도 괜찮아 보인다고 생각이 들기도 한다.&lt;/p&gt;
&lt;h3&gt;9. 공백도 코딩 컨벤션이다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;if, for, while문 사이의 공백도 코딩 컨벤션이다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이 부분은 이미 &lt;code&gt;prettier&lt;/code&gt;과 &lt;code&gt;eslint&lt;/code&gt; 를 통해 자동으로 관리되고 있기에 넘어갔다.&lt;/p&gt;
&lt;h3&gt;10. 공백 라인을 의미 있게 사용한다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;공백 라인을 의미 있게 사용하는 것이 좋아 보이며, 문맥을 분리하는 부분에 사용하는 것이 좋다. 과도한 공백은 다른 개발자에게 의문을 줄 수 있다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;공백 라인은 메서드마다 1줄로 설정하였고, 그 사이 공백에 주석을 통해 구분되도록 설정하였다.&lt;br&gt;주석이 필요없는 메서드/코드의 경우 그 사이를 공백 1줄로만 남겨두었다.&lt;/p&gt;
&lt;h3&gt;11. space와 tab을 혼용하지 않는다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;들여쓰기에 space와 tab을 혼용하지 않는다. 둘 중에 하나만 사용한다. 확신이 서지 않으면 pull request를 보낸 후 들여쓰기가 잘 되어 있는지 확인하는 습관을 들인다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;기본적으로 IDE에 잘 마련되어있는 기능이기도 하고, 이 또한 prettier에서 설정이 가능한 부분이기도 하다.&lt;br&gt;코드를 배울 때 space 를 통해 들여쓰기를 하지 않는 기본기를 익혀두었기에 이 부분도 잘 적용되어있다고 보고 넘어가겠다.&lt;/p&gt;
&lt;h3&gt;12. 의미 없는 주석을 달지 않는다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;변수 이름, 함수(메서드) 이름을 통해 어떤 의도인지가 드러난다면 굳이 주석을 달지 않는다. 모든 변수와 함수에 주석을 달기보다 가능하면 이름을 통해 의도를 드러내고, 의도를 드러내기 힘든 경우 주석을 다는 연습을 한다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이 부분은 추가적으로 작업이 필요한 부분이다. 리팩터링을 통해 이름을 변경하였기에 그 전에 달아두었던 주석이 필요없어졌을 수도 있다. 확인해보고 추가적으로 변경하고, 필요하면 주석을 달고 필요없으면 주석을 제거해야겠다.&lt;/p&gt;
&lt;h3&gt;13. linter와 Code Formatter의 기능을 활용한다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;가능하면 eslint와 prettier를 이용해 더욱 생산적으로 코드를 작성하자.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;린트(lint)는 소스 코드에 문제가 있는지 탐색하는 작업을 의미하며, 린터(linter)는 이 작업을 도와주는 소프트웨어를 말한다. 자바스크립트와 같은 인터프리터 언어의 경우, 런타임 에러가 발생할 확률이 높기 때문에, 이 린트 작업을 통해 사전에 에러를 최대한 잡아준다면 훨씬 생산성 높은 개발을 할 수 있다. lint 중 eslint는 자바스크립트 진영의 오픈소스로 확장되고 있는 정적 분석 도구이다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;prettier는 일종의 Code Formatter이다. Code Formatter란 개발자가 작성한 코드가 정해진 코딩 스타일을 따르도록 변환해주는 도구이다. 이 두 가지 도구를 이용하면 코드를 짜는데 발생할 수 있는 오류를 미리 예방하고 쉽게 정돈할 수 있다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/211&quot;&gt;https://time-map-installer.tistory.com/211&lt;/a&gt;&lt;br&gt;이미 이전에 작성해둔 글이 있기에 공유하겠다. 이 중요성은 몇 번의 프로젝트와 개발 경험으로 잘 알고 있다.&lt;/p&gt;
&lt;h3&gt;13. EOL(End Of Line)&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;최종 제출하는 코드에서 EOL을 확인한다. 환경에 따라 의도한 바와 다르게 개행 문자 처리가 되지 않도록 EOL 설정을 확인한다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이 부분은 처음 듣는 부분이었다. 그래서 알아보았는데, UNIX 환경으로 개발된 Mac과 다르게 Windows 환경에서는 줄바꿈 처리가 다른 문자열로 처리된다고 한다. VSCode에서 설정할 수 있다고 하기에 후에 팀 협업 시 이를 고려해야겠다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/xrxm9mm.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3&gt;14. 불필요한 console.log를 남기지 않는다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;디버깅을 위해 사용한 console.log가 최종 제출하는 코드에 의미 없이 남아있지 않도록 주의한다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이 부분은 확인과정에서 남아있는 코드를 발견하였고, 제거하였다.&lt;/p&gt;
&lt;h3&gt;15. JavaScript에서 제공하는 API를 적극 활용한다&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;함수(메서드)를 직접 구현하기 전에 JavaScript API에서 제공하는 기능인지 검색을 먼저 해본다.&lt;br&gt;JavaScript API에서 제공하지 않을 경우에 직접 구현한다.&lt;br&gt;예를 들어 우승자를 출력할 때 우승자가 2명 이상이면 쉼표(,) 기준으로 출력을 위한 문자열은 다음과 같이 구현할 수 있다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;pre&gt;&lt;code&gt;const members = [&amp;#39;east&amp;#39;, &amp;#39;west&amp;#39;, &amp;#39;south&amp;#39;];
members.map((member) =&amp;gt; member).join(&amp;#39;,&amp;#39;); // &amp;quot;east,west,south&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 부분에 대해서는 잘 활용한 것 같다. 코드리뷰를 거치면서 이 부분도 피드백 받으면 좋을 것 같다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;프리코스의 힘, 연관 검색어에 뜰 정도라고?&lt;/h3&gt;
&lt;p&gt;분명 변수명을 위해 구글에 전진을 영어로 어떻게 하는 지 검색을 했을 뿐인데..&lt;br&gt;자동차 전진 영어로가 뜨는 것을 보면 정말 많은 사람들이 열정적으로 프리코스에 참여하고 있다는 것을 알 수 있었다. 재미있는 에피소드였기에 한 번 적어보았다  &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/0GQAF46.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;마치며: 감기와 함께했던 2주차, 몰입이 이런거구나!&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;진정한 몰입은 지금 현재 내 몸상태가 어떻든 그 상태를 잊고 일정시간 집중할 수 있는 상태에 도달하는 것이다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이 말은 내가 지어낸 말이며, 계속되는 감기와 함께 진행했던 프리코스 2주차에서 얻어낸 깨달음이다.&lt;br&gt;최근 반년 이상 건강했다가 갑작스럽게 환절기가 왔다고 이런 고생을 하다니.. 몸이 아프니 수면제 성분의 약을 지속적으로 먹어야 했고 생각보다 오래 깨어있을 수가 없어 아쉬웠다.&lt;br&gt;그럼에도 프리코스에 몰입을 하는 그 순간만큼은 아프고 몸이 나른한 느낌이 들지는 않았다. 아니면 몸이 아프니깐 더 집중해서 빠르게 끝내야한다라는 생각이 들어서였을수도 있다.&lt;/p&gt;
&lt;p&gt;그동안에 무언가 몰입하는 느낌을 알고있었고, 몰입이 끝나면 그 날 하루가 쾌적하게 느껴져서 참 좋았는데 이번에는 몸이 아프다보니 몰입이 끝나면 에너지가 소진되어 잠에 들기를 반복했던 것 같다.&lt;br&gt;무언가 이상했다. 분명 몰입이 끝나면 에너지가 순환이 되면서 하루가 더 쾌적해야 할 텐데? 라고 생각했지만&lt;br&gt;목 부음 + 비염 + 식은땀 + 어지러움증 + 근육통이 같이 찾아온 상태라는 것을 생각해보면 몰입하면서 에너지를 몰아서 쓴 것이 아닐까 싶다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;목~금: 목감기로 인해 아파서 병원가서 주사맞고 약 처방&lt;/li&gt;
&lt;li&gt;주말 외부행사 진행&lt;/li&gt;
&lt;li&gt;월: 월! 월! 월! 월!&lt;/li&gt;
&lt;li&gt;화: 몸상태 악화되어 휴식&lt;/li&gt;
&lt;li&gt;수: 근육통 발생해서 휴식&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이런 상태에서도 몰입이 되는 것을 보고 &lt;code&gt;현재 상태와 상관없이 가능하다&lt;/code&gt; 라는 것을 알 수 있었다.&lt;/p&gt;
&lt;p&gt;물론 시간 투자가 평소보다는 적어졌지만, 그래도 할 수 있다는 것이 어디일까?&lt;br&gt;다음 과제에서는 몸이 회복되고 더 많은 에너지를 투자할 수 있게되면서 재밌는 일들이 많이 일어날 것 같다.&lt;br&gt;이번 주 후기를 마치도록 하겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;End&lt;/strong&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/개인</category>
      <category>2주차</category>
      <category>우아한테크코스</category>
      <category>자동차게임</category>
      <category>프리코스</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/260</guid>
      <comments>https://time-map-installer.tistory.com/260#entry260comment</comments>
      <pubDate>Thu, 2 Nov 2023 00:01:01 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] class는 교실이 아니라 객체이다</title>
      <link>https://time-map-installer.tistory.com/259</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2544&quot; data-origin-height=&quot;2734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPAt2N/btszfugl5UN/P1akIgolwApLZRpnxWXVYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPAt2N/btszfugl5UN/P1akIgolwApLZRpnxWXVYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPAt2N/btszfugl5UN/P1akIgolwApLZRpnxWXVYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPAt2N%2Fbtszfugl5UN%2FP1akIgolwApLZRpnxWXVYk%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;430&quot; data-origin-width=&quot;2544&quot; data-origin-height=&quot;2734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;들어가며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 참여하고 있는 우아한테크코스 프리코스의 1, 2주차를 지나며 그동안 해오던 것과는 다른 방식인 객체지향 프로그래밍을 사용하여 코드를 작성하기 시작했습니다. 이젠 서로 다른 두 방법을 모두 익히게 되어 이를 통해 프로그래밍 그 자체에 대한 더 깊은 이해로 나아가기 위한 초석이 될 것이라 믿고 있습니다. 그리고 여기에 스스로 익힌 클래스에 대한 내용을 정리해두도록 하겠습니다.&lt;br /&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/258&quot;&gt;https://time-map-installer.tistory.com/258&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1698384460448&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;&amp;lt;우아한테크코스 2024 프리코스&amp;gt; 1주차 후기: 숫자야구게임, 그런데 객체로 만든다고?&quot; data-og-description=&quot;이 포스팅은 회고 겸 TroubleShooting 겸 한 주 간의 고민과정을 모두 정리해둔 글임을 알립니다 Visit GitHub Repository 프리코스가 그렇게 유명하다던데? 지난 번 Software Maestro에 도전했던 올해 초가 기억&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/258&quot; data-og-url=&quot;https://time-map-installer.tistory.com/258&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/HRV0n/hyUj74ID07/y0auyPiy9f7y1hzXVLbe2k/img.png?width=741&amp;amp;height=450&amp;amp;face=0_0_741_450,https://scrap.kakaocdn.net/dn/baRMPO/hyUkgUS4db/Cp0Cbm78fpZdTNZ3CND1TK/img.png?width=741&amp;amp;height=450&amp;amp;face=0_0_741_450,https://scrap.kakaocdn.net/dn/bRJTnV/hyUkcLKlJL/mGsLhN6lp7Cuwlkg45a261/img.png?width=741&amp;amp;height=450&amp;amp;face=0_0_741_450&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/258&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/258&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/HRV0n/hyUj74ID07/y0auyPiy9f7y1hzXVLbe2k/img.png?width=741&amp;amp;height=450&amp;amp;face=0_0_741_450,https://scrap.kakaocdn.net/dn/baRMPO/hyUkgUS4db/Cp0Cbm78fpZdTNZ3CND1TK/img.png?width=741&amp;amp;height=450&amp;amp;face=0_0_741_450,https://scrap.kakaocdn.net/dn/bRJTnV/hyUkcLKlJL/mGsLhN6lp7Cuwlkg45a261/img.png?width=741&amp;amp;height=450&amp;amp;face=0_0_741_450');&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;&amp;lt;우아한테크코스 2024 프리코스&amp;gt; 1주차 후기: 숫자야구게임, 그런데 객체로 만든다고?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 포스팅은 회고 겸 TroubleShooting 겸 한 주 간의 고민과정을 모두 정리해둔 글임을 알립니다 Visit GitHub Repository 프리코스가 그렇게 유명하다던데? 지난 번 Software Maestro에 도전했던 올해 초가 기억&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;JavaScript&lt;/code&gt;는 &lt;code&gt;객체 지향 프로그래밍(OOP)&lt;/code&gt;을 지원하는 유연한 언어입니다.&lt;br /&gt;ES6에서 도입된 &lt;code&gt;class&lt;/code&gt;는 보다 명확하고 직관적인 방식으로 객체를 생성하고 상속하도록 합니다.&lt;br /&gt;&lt;code&gt;JavaScript&lt;/code&gt; 클래스의 기본 구조, 사용 방법, 다른 파일에 &lt;code&gt;import&lt;/code&gt;하는법과 클래스의 확장에 대해 살펴볼 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 클래스의 기본 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript의 클래스는 주로 &lt;code&gt;constructor&lt;/code&gt;, &lt;code&gt;메서드&lt;/code&gt;, 그리고 &lt;code&gt;속성&lt;/code&gt;의 세 가지 주요 구성 요소로 이루어져 있습니다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Constructor
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;constructor&lt;/code&gt;는 클래스로부터 객체를 생성할 때 호출되는 특별한 메서드입니다.&lt;/li&gt;
&lt;li&gt;이 메서드는 객체의 초기 상태를 설정하는데 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;class Example {
  constructor(value) {
    this.value = value;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서, &lt;code&gt;Example&lt;/code&gt; 클래스는 &lt;code&gt;value&lt;/code&gt;라는 속성을 가지고 있으며, 이 속성은 객체를 생성할 때 &lt;code&gt;constructor&lt;/code&gt;를 통해 설정됩니다.&lt;br /&gt;여기에 다른 클래스의 메서드에서 반환되는 값을 설정하여 사용할 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;메서드(method)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메서드는 객체가 수행할 수 있는 행동을 정의합니다.&lt;/li&gt;
&lt;li&gt;클래스 내부에서 정의되며, 객체를 통해 호출될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;class Example {
  constructor(value) {
    this.value = value;
  }

  displayValue() {
    console.log(this.value);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서, &lt;code&gt;displayValue&lt;/code&gt; 메서드는 객체의 &lt;code&gt;value&lt;/code&gt; 속성을 콘솔에 출력합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;속성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;속성은 객체의 상태를 나타내는 변수를 의미합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;constructor&lt;/code&gt; 내부에서 &lt;code&gt;this&lt;/code&gt; 키워드를 사용해서 설정될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;class Example {
  constructor(value) {
    this.value = value;  // 'value'는 Example 클래스의 속성입니다.
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서, &lt;code&gt;value&lt;/code&gt;는 &lt;code&gt;Example&lt;/code&gt; 클래스의 속성으로, 객체의 초기 상태를 설정하는데 사용됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체를 생성하고 사용하려면?&lt;/h3&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;const obj = new Example('Hello, World!');
obj.displayValue();  // 출력: Hello, World!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서, &lt;code&gt;new Example('Hello, World!')&lt;/code&gt;을 통해 &lt;code&gt;Example&lt;/code&gt; 클래스의 인스턴스를 생성하고, &lt;code&gt;displayValue&lt;/code&gt; 메서드를 호출하여 &lt;code&gt;value&lt;/code&gt; 속성의 값을 출력합니다. 이 방식으로 다른 클래스 내에서 특정 클래스 메서드의 값을 받아서 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적인 요소를 포함해서 다시 확인해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`); 
    // this.name에 받아온 name값을 받아왔기에 this.name을 활용합니다.
  }
}

const animal = new Animal('Animal'); // 'Animal'은 Animal클래스 constructor의 name에 매핑됩니다.
animal.speak(); // 출력: Animal makes a noise.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서 &lt;code&gt;Animal&lt;/code&gt; 클래스는 &lt;code&gt;name&lt;/code&gt; 속성과 &lt;code&gt;speak&lt;/code&gt; 메서드를 가지고 있습니다. &lt;code&gt;new&lt;/code&gt; 키워드를 사용하여 인스턴스를 생성하면, &lt;code&gt;animal&lt;/code&gt; 객체를 통해 메서드를 호출할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 클래스 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스는 객체를 생성하고 관리하는 일종의 템플릿입니다. &lt;code&gt;new&lt;/code&gt; 키워드를 사용하여 클래스의 인스턴스를 생성하면, 해당 클래스의 메서드와 속성에 접근할 수 있습니다. 여기서 클래스 괄호에 넣는 특정 값은 메서드로 전달하는 값이라고 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;const myAnimal = new Animal('Dog'); // 'Dog'는 Animal class의 constructor에서 선언되고 사용합니다.
myAnimal.speak(); // 출력: Dog makes a noise.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 다른 파일에서 클래스 import 하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 시스템을 사용하면 클래스를 다른 파일로 분리하고 필요한 곳에서 불러와 사용할 수 있습니다. 예를 들어, &lt;code&gt;Animal&lt;/code&gt; 클래스가 정의된 &lt;code&gt;Animal.js&lt;/code&gt; 파일이 있다면, 이를 다른 파일에서 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// Animal.js
export class Animal {
  // ... 클래스 정의
}

// main.js
import { Animal } from './Animal.js';

const myAnimal = new Animal('Cat');
myAnimal.speak();&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 클래스 확장하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스는 &lt;code&gt;extends&lt;/code&gt; 키워드를 사용하여 다른 클래스로부터 상속받을 수 있습니다. 이를 통해 부모 클래스의 모든 메서드와 속성을 사용할 수 있으며, 필요에 따라 오버라이드하거나 새로운 메서드와 속성을 추가할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class Dog extends Animal {
  speak() {
    console.log(`${this.name} barks.`);
  }
}

const myDog = new Dog('Rex');
myDog.speak(); // 출력: Rex barks.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 확장은 근본이 되는 클래스에 공통적인 기능을 정의하고, 이를 상속받아 추가 기능이나 수정된 기능을 가진 새로운 클래스를 만들 때 유용합니다.&lt;br /&gt;예를 들어, 웹 애플리케이션에서 다양한 UI 컴포넌트를 다루어야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;BaseComponent&lt;/code&gt;라는 클래스에 모든 컴포넌트의 공통 기능을 정의하고,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ButtonComponent&lt;/code&gt; 클래스에서는 이를 상속받아 버튼 특유의 기능을 추가합니다.&lt;br /&gt;이렇게 확장을 하면서 코드의 재사용성이 향상되고, 각 컴포넌트의 관리가 훨씬 수월해집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Plus. 확장 불가능한 클래스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Object.freeze&lt;/code&gt; 메서드를 사용하여 클래스의 확장을 방지할 수 있습니다. 이 방법은 클래스의 모든 인스턴스가 동일한 메서드와 속성을 갖도록 하여 일관성을 유지하려 할 때 유용합니다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class Cat extends Animal {
  // ... 클래스 정의
}

Object.freeze(Cat);

class BigCat extends Cat {
  // 이 코드는 에러를 발생시킬 것입니다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론, 그리고 질문&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript의 클래스는 코드의 구조를 개선하고, 객체의 생성과 관리를 보다 효율적으로 수행할 수 있도록 도와줍니다. 클래스의 확장성과 모듈화는 대규모 애플리케이션 개발에 있어 필수적인 요소이며, 이를 통해 보다 유지보수가 용이하고 확장 가능한 코드베이스를 구축할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 백엔드 및 JavaScript Backend 개발환경에서는 객체지향 프로그래밍을 활용해서 프로그램을 개발합니다.&lt;br /&gt;하지만 현재 사용되고 있는 프론트엔드 로직 개발 방향성은 OOP에서 함수형 프로그래밍으로 대체되는 상황입니다. 이렇게 변화하는 이유는 무엇일까요?&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>class</category>
      <category>JavaScript</category>
      <category>객체</category>
      <category>자바스크립트</category>
      <category>클래스</category>
      <category>확장</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/259</guid>
      <comments>https://time-map-installer.tistory.com/259#entry259comment</comments>
      <pubDate>Fri, 27 Oct 2023 14:32:51 +0900</pubDate>
    </item>
    <item>
      <title>&amp;lt;우아한테크코스 2024 프리코스&amp;gt; 1주차 후기: 숫자야구게임, 그런데 객체로 만든다고?</title>
      <link>https://time-map-installer.tistory.com/258</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfu8Ff/btsy8ilMcw4/VeDXsU5dGPzmuNrWztel2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfu8Ff/btsy8ilMcw4/VeDXsU5dGPzmuNrWztel2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfu8Ff/btsy8ilMcw4/VeDXsU5dGPzmuNrWztel2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcfu8Ff%2Fbtsy8ilMcw4%2FVeDXsU5dGPzmuNrWztel2K%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;364&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅은 회고 겸 TroubleShooting 겸 한 주 간의 고민과정을 모두 정리해둔 글임을 알립니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/TMInstaller/javascript-baseball-6&quot;&gt;Visit GitHub Repository&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프리코스가 그렇게 유명하다던데?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 번 Software Maestro에 도전했던 올해 초가 기억나는 시작이다.&lt;br /&gt;이번에는 가장 인지도가 높고 성장하기에 특화되어있는 교육 프로그램인 &amp;lt;우아한 테크코스 2024&amp;gt;에 합류하기 위한 단계인 &amp;lt;프리코스&amp;gt;에 도전하기로 했다. 사전 과제를 통한 평가단계이고, 과제전형이기에 그냥 과제만 풀고 끝이 아닐까 싶겠지만 지난 5기때의 많은 후기들, 검증된 몰입할 수 있는 과제들이 이 자체에 참여하는것만으로도 많은 도움이 될 것이라는 신호를 보내고 있었다.&lt;br /&gt;유명한 데에는 다 이유가 있었다. 1주차를 마친 나 또한 이 프리코스가 왜 유명한 지 일부나마 이해할 수 있었으며, 지금부터 1주차를 마치며 그 내용을 정리하려고 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫인상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업 후 집에가는 길에 프리코스 OT를 들으며 집으로 돌아온 후 노트북을 열었다. 도착한 메일을 열고, 프리코스 레포에 접속하여 초기 클론을 받았다. &quot;숫자 야구 게임&quot;, 처음 코딩을 시작한다 하면 거의 모두가 이 알고리즘을 만들어보고 프로그래밍에 대한 재미를 붙이기 시작하곤 한다. 나 역시도 20살 때 처음 자바를 이용하여 이걸 만들어 본 경험이 있어서 그런지 기분이 묘했다. 다시 처음 하는 기분으로 이걸 시작할 수 있다? 난 더 좋다는 마인드로 요구조건에 대한 간단한 사용자 플로우를 다이어그램으로 제작하고 코드를 작성하기 시작했다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/A5qCPZE.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇 번 수정하고 나니 생각보다 볼만 한 다이어그램이 나왔다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;처음 써보는 클래스 형태의 자바스크립트&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요구조건은 이제 알겠어, 그런데 객체로 코드를 짜야하네?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안 웹개발에 입문하고 프론트엔드로 활동하며 React, Next.js 등을 써보았고, 거기에서 사용되는 함수형으로만 프로그래밍을 해 보았다. 간혹 검색할 때 옛날 코드에서 객체 형태로 코드가 짜여져 있다면 그냥 넘어가곤 했는데.. 이제는 반대로 그런 코드들을 참고해서 프로그래밍을 해야한다고? 처음에는 막막했다. 어떻게 실행시키는 지 찾는 데에만 해도 1시간 정도가 걸렸으니 정말 먼 길을 걸어가겠구나 싶었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그동안 해왔던 것이 허수는 아니었다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말 그대로이다. 그래도 그동안 함수형에 대해 얼마나 많은 의문을 가졌었고, 이를 문서화하며 궁금증을 풀었는지 모르겠다. 그래서 그런가 이 둘 사이의 공통점을 찾았고, 몇 가지 깨달음을 얻을 수 있었다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클래스와 함수형, 근본적으로 이 둘 안에 코드를 작성하는 방식은 크게 다르지 않다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 부분은 내가 한 클래스 내에서 모든 코드를 구현해서 그런 것일수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메서드에서 인자를 받아오는 것과 함수에서 인자를 받아오는 방식은 비슷하다.&lt;/li&gt;
&lt;li&gt;메서드와 함수 내부에 JS 코드를 작성하는 방식은 거의 비슷하다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단은 이런 공통점이 있었고, 객체의 this가 무엇을 의미하는지도 알 수 있었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class의 this는 지금 현재 위치하고 있는 클래스를 의미하는구나!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 App 클래스 내부에서 &lt;code&gt;this.method()&lt;/code&gt;를 통해 메서드를 호출하면 여기서 this는 App을 의미하게 되는 것이었다. 이렇게 한 번 경험해본다고 바로 이해된다니! 정말 뿌듯했다. 직접 써보면서 부딪히는 것만큼 이렇게 효과가 좋은 건 없을 것이다 싶었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;순조로운 로직 설계&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와 나 이거 3시간만에 끝내는거 아니야?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 생각했었다. 로직을 설계할때까지는 말이다. 전부터 기초적인 코딩 테스트 연습은 해왔다 보니 기초적으로 로직을 짜는 것은 문제가 없었다. 클래스를 실행시켜 돌려보았을 때에도 로직 자체는 제대로 돌아가는 것을 보고 &quot;와 나 천재다&quot; 라고 생각했었는데, 그 생각은 예외처리와 테스트코드를 돌리기 시작하면서부터 산산조각이 났다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;하지만 예외가 있었죠&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누구나 다 그럴싸한 계획은 가지고 있다. 테스트를 돌려보기 전까진 말이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 말은 이렇게 했지만 테스트를 위한 예외 코드를 짜는 것 까지도 괜찮았었다. 그 과정에서도 &lt;code&gt;throw&lt;/code&gt;와 &lt;code&gt;try/catch&lt;/code&gt; 가 어떻게 작동하는지에 대해 이해할 수 있기도 했고 배움도 많았지만 그래도 2일 이상 소요되는 과정은 아니었다. 10개가 안되는 메서드들을 통해 모두 구현을 하고 몇 번의 자체적인 테스트에서 잘 통과하는 것을 확인했을 때 나는 기뻐했다. 내 야구게임이 드디어 완성되었다!&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀적인 호출이 테스트코드 순서를 변경한다고..?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 예외가 있었죠. 잘 돌아가야만 하던 테스트코드에 예상과 다르게 입력되어야 할 값이 아닌 다른 값이 들어가는 일이 발생했다. 로그를 통해 확인하고, 이를 고치면서 이는 재귀적으로 호출이 될 때 이렇게 꼬이는 현상이 나타난다고 한다는 것을 알았다. 정말 놀라웠지만 생각해보니 전에도 코딩테스트 연습을 하면서 이런 일이 있었던 것 같아서 기분이 묘했다. 이 부분을 알아내기 위해 테스트코드 자체를 뜯어보면서 어떤식으로 동작하는지에 대해서도 익힐 수 있던 것을 보면 왜 프리코스에서 혼자 해결해야한다고 말했는지 알 것 같았다. 특정 문제 하나를 고치기 위해 많은 것들을 얻어가는 이런 경험은 흔하지 않은데, 뭔가 느낌이 좋다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가장 오래걸렸던 '그 오류'&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 보고있는 이 오류는 기간동안 풀어낼 수 없었다. 아직도 궁금하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 모든 테스트케이스, 예외처리는 정상적으로 작성되었고, 'npm test' 를 통해 Jest 환경에서 테스트를 했을 때 모든 케이스에 대한 pass가 출력되면서 잘 작성된 로직인것만 같았다. 하지만 계속해서 같은 오류가 테스트 완료 이후 발생하면서 다양한 시도를 할 수 있는 시간이 있었다. 그리고 그 오류는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;Jest did not exit one second after the test run has completed.

'This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;당신의 비동기적인 작업들이 테스트에서 멈추지 않았다.&quot; 라는 내용의 메시지로, 이를 해결하기 위해 아래와 같은 가설들을 세울 수 있었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가설: 이래서 테스트가 끝나지 않은 것일까?&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;현재 사용자의 입력을 받는 &lt;code&gt;Console&lt;/code&gt; 라이브러리(프리코스용 커스텀 라이브러리)가 닫히지 않아 문제가 발생하였을 것이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while(true){}&lt;/code&gt;, &lt;code&gt;재귀 호출&lt;/code&gt; 등 무한반복이 사용되었기에 특정 조건에서 탈출되지 않은 상태로 테스트가 끝났을 것이다.&lt;/li&gt;
&lt;li&gt;예외처리가 일부 제대로 처리되지 않았지만 테스트에서는 정상적으로 처리되는 특수한 상황일 것이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 가설들을 세울 수 있었고, 이를 위해 해결하기 전에 우선 메서드 이름을 더 깔끔하게 정리하고 가고 싶었다.&lt;br /&gt;그래서 기존의 구간별로 메서드를 다시 만들고 이름들을 다음과 같이 설정하였다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시작 멘트: &lt;code&gt;systemStartMent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;야구게임 무작위 정답 생성: &lt;code&gt;systemGenerateRandomAnswer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;사용자의 정답 입력: &lt;code&gt;userInputAnswer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;사용자의 정답에 따른 결과 출력: &lt;code&gt;systemCheckAnswer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;야구게임 재시작/종료 여부 입력: &lt;code&gt;userSelectRestartOrQuit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;입력된 여부에 따른 시스템 처리: &lt;code&gt;systemRestartOrQuitProcess&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템, 유저의 행동임을 이름만 보고도 읽을 수 있게 바꾸었고, 전보다 메서드명이 길어지긴 했지만 가독성이 더 좋아져서 코드를 수정할 때 더 빠르게 인식할 수 있어서 좋았다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가설검증&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위 3가지의 큰 카테고리의 가설을 검증하기 위해 나는 여러 시도를 해보았다. 그리고 아래에 적는 것들은 그 생각과정을 담아두었다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;현재 사용자의 입력을 받는 &lt;code&gt;Console&lt;/code&gt; 라이브러리(프리코스용 커스텀 라이브러리)가 닫히지 않아 문제가 발생하였을 것이다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;우선 제공하는 라이브러리 코드가 어떤 것인 지 확인해보아야겠다&lt;/li&gt;
&lt;li&gt;비동기적으로 사용자의 입력을 처리하는 코드가 있네? 이게 닫히지 않았던 것일까?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;readline&lt;/code&gt;이 닫히지 않는 것이 원인이라면 여기에 닫는 메서드를 추가하면 해결되겠다!&lt;/li&gt;
&lt;li&gt;라이브러리는 수정하면 안될 것 같으니 &lt;code&gt;App.js&lt;/code&gt;로 들고와서 확장해서 만들어보자!&lt;/li&gt;
&lt;/ol&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;li&gt;&lt;code&gt;while(true){}&lt;/code&gt;, &lt;code&gt;재귀 호출&lt;/code&gt; 등 무한반복이 사용되었기에 특정 조건에서 탈출되지 않은 상태로 테스트가 끝났을 것이다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;간혹 이 &lt;code&gt;while&lt;/code&gt; 문이나 &lt;code&gt;재귀 호출&lt;/code&gt; 등 반복적으로 돌아가는 로직으로 인해 종료되지 않는다는 사실이 검색하다보니 종종 보이는 것 같아&lt;/li&gt;
&lt;li&gt;이 while문, 재귀 호출을 모두 없애면 해결되지 않을까?&lt;/li&gt;
&lt;/ol&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;li&gt;예외처리가 일부 제대로 처리되지 않았지만 테스트에서는 정상적으로 처리되는 특수한 상황일 것이다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;예외처리가 안되어서 될 만한 부분이 있었던가?&lt;/li&gt;
&lt;li&gt;반복문에서 나갈 때 어떤 로직이 추가로 필요했는 지 알아보자!&lt;/li&gt;
&lt;/ol&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;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 과정들을 거쳤지만 결국 해결하려는 문제에 대해서는 알아갈 수 없었고, 이후에도 몇몇 도전들을 해나갔다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트는 총 2개로 test 2에서 종료되지 않은 것이라면, 이 test 2의 내용이 지정된 문자열의 길이를 넘겨 출력했을 때 에러를 핸들링하는 과정에서 무언가 문제가 있어서 종료되지 않은 것일까?
&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;li&gt;play() 메서드 내부에서 try/catch문을 Promise를 호출하도록 변경하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;try문에 resolve()를, catch문에 reject(e)를 넣어 출력해보았지만 결국 같은 에러로 원점화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 전부 시도했음에도 해결되지는 않았고, 어느덧 제출기간이 되었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며: 이제는 정리해볼 시간!&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;아무래도 다른 사람들의 PR이 올라오는 시간인 만큼 더 이상 기존 과제 branch의 코드는 수정하지 않고 새로운 브랜치를 만들어서 거기서 한을 풀어봐야겠다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/sM21qE3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제출기간은 총 2일, PR을 올리고 지원 사이트에 과제를 제출하면 끝나는 마지막 단계에 돌입했다. 지정된 양식에 맞게 PR을 보내고, 사이트에 소감과 함께 제출을 하였고 테스트를 실행했다. 오류가 떠있어서 통과하지 못하는 것이 아닐까 걱정이 되었지만 정말 다행히 모두 통과하면서 잘 마무리가 되었다. README 정도만 업데이트를 간단히 하고 코드는 손대지 않을 생각으로 제출을 완료하니 뭔가 뿌듯했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 했던 일은 다른 사람들이 올린 코드를 구경해보는 일이었다. 커뮤니티와 오픈톡방 등에서 사람들이 &quot;이렇게 작성하고 있어요&quot; 라는 말을 듣고 내심 궁금했던 차 구경을 해보았더니 놀라지 않을 수 없었다.&lt;br /&gt;코드를 체계적으로 분리하고, 파일을 잘 나눠두고, 객체에 문자열들을 모아서 관리하고, 몇 명 안본 것 같은데도 거의 다 다른 방식으로 잘 작성되어있었다. 나는 그저 한 파일에 모든 코드를 넣어두고 문제해결하기에 바쁘게 보냈던 것 같은데, 이런 식으로도 코드 구조가 만들어질 수 있구나 깨달았다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며: 그래서 이제 뭐할꺼라고?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모든 일이 그저 코딩 입문이라고 볼 수 있는 숫자야구게임 하나에서 일어났다. 역시 프리코스다 싶었고, 다음 과제는 어떤 것이 나올 지 기대가 되기 시작했다. 이제 제출기한이 끝나기 전까지 후기 작성을 마치고 이 글이 올라가는 26일 00시 이후에는 PR, 코드리뷰 등을 통해 내 코드를 돌아보고 앞으로 나아갈 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/개인</category>
      <category>1주차</category>
      <category>숫자야구게임</category>
      <category>우아한테크코스</category>
      <category>프리코스</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/258</guid>
      <comments>https://time-map-installer.tistory.com/258#entry258comment</comments>
      <pubDate>Thu, 26 Oct 2023 00:01:06 +0900</pubDate>
    </item>
    <item>
      <title>[Automatic Batching] 리액트 18에 등장한 렌더링 묶음판매</title>
      <link>https://time-map-installer.tistory.com/257</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;React.png&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HugWg/btsxspTW1h8/2zQaPCZ7gqaOF5Dana9NZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HugWg/btsxspTW1h8/2zQaPCZ7gqaOF5Dana9NZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HugWg/btsxspTW1h8/2zQaPCZ7gqaOF5Dana9NZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHugWg%2FbtsxspTW1h8%2F2zQaPCZ7gqaOF5Dana9NZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;280&quot; height=&quot;280&quot; data-filename=&quot;React.png&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; Automatic Batching이란 무엇일까?&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[[React]] 18에서 나온 새로운 기능, Automatic Batching에 대한 문서이다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Batching이란, 이벤트 핸들러나 hooks 안에서 상태 업데이트를 묶어서 동작하도록 만들어준다. 즉, 렌더링을 한 번만 일어나도록 해주는 것이다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;code&gt;Automatic Batching&lt;/code&gt; = &lt;code&gt;자동화된 일괄 렌더링&lt;/code&gt; 이라고 생각할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 생각해보면 이상하다. 기존의 함수 내부에서 여러 setState()를 실행시키면 이미 Batching이 이루어졌을까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그 이유는 React에는 여러 번의 &lt;code&gt;state update&lt;/code&gt; 작업을 &lt;code&gt;Queue&lt;/code&gt;에 몰아넣고 일정 주기마다 &lt;code&gt;Queue&lt;/code&gt;에 등록된 작업을 순차적으로 일괄 시행하면서 불필요한 리렌더링을 방지하는 특징이 있기 때문이다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드를 통해 확인해보자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 &lt;code&gt;batching&lt;/code&gt; 이 각각의 &lt;code&gt;React event&lt;/code&gt; 마다 한 번씩 이루어졌었다&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// Before: only React events were batched.
setTimeout(() =&amp;gt; {
  setCount(c =&amp;gt; c + 1);
  setFlag(f =&amp;gt; !f);
  // React will render twice, once for each state update (no batching)
}, 1000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 위 코드에서는 각각의 이벤트들마다 렌더링이 되므로, 두 번 렌더링 되었다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;setCount()&lt;/code&gt;에서 한 번&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setFlag()&lt;/code&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;기존(~React 17)에는 오직 React의 이벤트 핸들러 내부의 state update 작업에 대해서만 &lt;code&gt;batching&lt;/code&gt;이 가능했다. 이 말은 &lt;code&gt;Promise&lt;/code&gt;, &lt;code&gt;setTImeout&lt;/code&gt;, &lt;code&gt;native event handler&lt;/code&gt; 내부의 작업은 불가능했었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 18버전으로 넘어가면서 &lt;code&gt;리액트 이벤트 핸들러&lt;/code&gt; 내부 뿐만 아니라 &lt;code&gt;promises&lt;/code&gt;, &lt;code&gt;setTimeout&lt;/code&gt;, 여러 &lt;code&gt;native event handler&lt;/code&gt; 안에서도 배칭이 적용된다&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// After: updates inside of timeouts, promises,
// native event handlers or any other event are batched.
setTimeout(() =&amp;gt; {
  setCount(c =&amp;gt; c + 1);
  setFlag(f =&amp;gt; !f);
  // React will only re-render once at the end (that's batching!)
}, 1000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 &lt;code&gt;setTimeout()&lt;/code&gt; 안에있는 내용들에 &lt;code&gt;Automatic Batching&lt;/code&gt;이 적용되면서 렌더링을 한번만 발생시킨다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;발동조건&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 언제나 이렇게 되는 것은 아니라는 것을 주의해야 한다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;.createRoot() method&lt;/code&gt; 를 사용했을때만 &lt;code&gt;automatic batching&lt;/code&gt;이 적용된다&lt;/li&gt;
&lt;li&gt;만약 이전 레거시인 &lt;code&gt;.render() method&lt;/code&gt; 를 사용할 경우 React 18이어도 적용되지 않는다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정을 적용해서 사용하려면?&lt;br /&gt;App.js 와 이를 설정하는 index.js 두 파일로 설명을 해보겠다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;App.js&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React from 'react';

export default function App() {
  const [count, setCount] = React.useState(0);

  const handleClick = () =&amp;gt; {
    setCount(prev =&amp;gt; prev + 1);
    setCount(prev =&amp;gt; prev + 1);
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;Count: {count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;Increase Twice&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;.createRoot() 사용 시 Automatic Batching 적용&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 [[React Concurrent Mode]]의 기능을 활용할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const root = document.getElementById('root');
const reactRoot = ReactDOM.createRoot(root);
reactRoot.render(&amp;lt;App /&amp;gt;);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;.render() 사용 시 Automatic Batching 적용&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, React 18버전 이상일 경우 이는 적용되지 않는다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(&amp;lt;App /&amp;gt;, document.getElementById('root'));&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;+ 만약 Automatic Batching을 무시해야 하는 상황이라면?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-dom 라이브러리에 추가된 &lt;code&gt;ReactDOM.flushSync()&lt;/code&gt; 메서드를 사용한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단, React에서는 공식적으로 해당 메서드의 사용을 추천하지는 않는다
&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;아래에는 이 메서드를 활용한 렌더링 확인용 Counter와 렌더링이 될 때마다 로그를 찍도록 하는 간단한 예제를 만들어보았다&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// App.js
import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';

export default function App() {
  const [count, setCount] = React.useState(0);

  const handleClick = () =&amp;gt; {
    ReactDOM.flushSync(() =&amp;gt; { // 여기서 첫 렌더링
      setCount(prev =&amp;gt; prev + 1);
    });
    setCount(prev =&amp;gt; prev + 1); // 이 이후 일괄 적용 렌더링
    setCount(prev =&amp;gt; prev + 1);
  };

  useEffect(() =&amp;gt; {
    console.log('Component rendered with count:', count);
  });

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;Count: {count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;Increase Thrice&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 확인해 볼 경우 &lt;code&gt;.flushSync()&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-origin-width=&quot;1014&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0qaol/btsxgiJgZ3r/PwrIffM6qtYkwcCOk76io1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0qaol/btsxgiJgZ3r/PwrIffM6qtYkwcCOk76io1/img.png&quot; data-alt=&quot;위의 코드처럼 .flushSync()를 적용하자 15부터 2번씩 렌더링되는 모습을 볼 수 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0qaol/btsxgiJgZ3r/PwrIffM6qtYkwcCOk76io1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0qaol%2FbtsxgiJgZ3r%2FPwrIffM6qtYkwcCOk76io1%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;1014&quot; height=&quot;612&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위의 코드처럼 .flushSync()를 적용하자 15부터 2번씩 렌더링되는 모습을 볼 수 있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;References&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://react.dev/blog/2022/03/29/react-v18&quot;&gt;https://react.dev/blog/2022/03/29/react-v18&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@dbwjd5864/React-18-automatic-batching%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90#:~:text=automatic%20batching%2C%20%EC%9E%90%EB%8F%99%20%EB%B0%B0%EC%B9%AD%EC%9D%B4%EB%9E%80,%EC%95%88%EC%97%90%EC%84%9C%EB%8F%84%20%EB%B0%B0%EC%B9%AD%EC%9D%B4%20%EC%A0%81%EC%9A%A9%EB%90%9C%EB%8B%A4&quot;&gt;https://velog.io/@dbwjd5864/React-18-automatic-batching%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90#:~:text=automatic%20batching%2C%20%EC%9E%90%EB%8F%99%20%EB%B0%B0%EC%B9%AD%EC%9D%B4%EB%9E%80,%EC%95%88%EC%97%90%EC%84%9C%EB%8F%84%20%EB%B0%B0%EC%B9%AD%EC%9D%B4%20%EC%A0%81%EC%9A%A9%EB%90%9C%EB%8B%A4&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@rookieand/React-18%EC%97%90%EC%84%9C-%EC%B6%94%EA%B0%80%EB%90%9C-Auto-Batching-%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot;&gt;https://velog.io/@rookieand/React-18%EC%97%90%EC%84%9C-%EC%B6%94%EA%B0%80%EB%90%9C-Auto-Batching-%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Development Study/Frontend</category>
      <category>automatic batching</category>
      <category>batching</category>
      <category>flushSync</category>
      <category>react</category>
      <category>react-dom</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/257</guid>
      <comments>https://time-map-installer.tistory.com/257#entry257comment</comments>
      <pubDate>Fri, 6 Oct 2023 21:44:59 +0900</pubDate>
    </item>
    <item>
      <title>UI 버벅임은 이제 그만! Web Worker 를 적절히 이용하여 UX 증진시키기</title>
      <link>https://time-map-installer.tistory.com/256</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;React.png&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6Z6xz/btsxh9qmyRd/Fg2eafzAmu3M9mDRxwfCwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6Z6xz/btsxh9qmyRd/Fg2eafzAmu3M9mDRxwfCwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6Z6xz/btsxh9qmyRd/Fg2eafzAmu3M9mDRxwfCwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6Z6xz%2Fbtsxh9qmyRd%2FFg2eafzAmu3M9mDRxwfCwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;280&quot; height=&quot;280&quot; data-filename=&quot;React.png&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;280&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;비동기 처리는 API에서만 할 수 있는 것일까? 사용자가 상호작용하는 모든 것에 API가 들어있지는 않을 텐데, 그렇다면 어떡하지?&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;Web Worker, 이거 Promise랑 다른건가?&lt;/b&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;그래서 이참에 Web Worker에 대해 알아보기로 했다&lt;/p&gt;
&lt;hr data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Web Worker란 무엇일까?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글스레드인 자바스크립트에서 많은 양의 데이터를 받아와 가공해야하는 경우 시간이 오래걸리게 되면서 병목이 발생, 따라서 다른 스레드에서 병렬적으로 처리해주는 역할을 하는 것이 web-worker다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 API의 일부 함수는 &lt;code&gt;멀티 스레드&lt;/code&gt;로 처리가 되는 것도 있지만, 사용자가 직접 데이터를 처리하는 부분에 대해서는 &lt;code&gt;싱글 스레드&lt;/code&gt;로 동작된다.&lt;br /&gt;따라서 데이터 처리가 많아질수록 &lt;code&gt;병목현상&lt;/code&gt;이 생려 렌더링 프레임에 영향을 미치게 된다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 정확히는 스크립트 연산을 웹 어플리케이션의 &lt;code&gt;주 실행 스레드&lt;/code&gt;와 분리된 별도의 &lt;code&gt;백그라운드 스레드에서&lt;/code&gt; 실행할 수 있는 기술이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 브라우저에서 싱글 스레드 기반으로 동작하기에 &lt;code&gt;web-worker&lt;/code&gt; 를 사용하지 않았을 때 UI 동작과 핸들링이 순차적인 방식으로 처리가 된다&lt;br /&gt;반면, &lt;code&gt;web-worker&lt;/code&gt; 를 사용하면 병렬 처리가 가능해진다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제 상황 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 예시와 함께 이해해보는 것이 좋을 것이라 생각하여 위의 설명에 따라 먼저 &lt;code&gt;web-worker&lt;/code&gt; 를 사용하지 않을 경우 버벅일만한 예제를 만들어보았다&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React from 'react'

export default function App(): JSX.Element {
    // 큰 계산 작업을 수행하는 함수
    const heavyCompute = () =&amp;gt; {
        let sum = 0;
        for (let i = 0; i &amp;lt; 1e9; i++) {
            sum += i;
        }
        console.log(&quot;계산 완료&quot;, sum);
    }

    // &quot;계산 시작&quot; 버튼 클릭 시 heavyCompute 함수를 실행
    const onStartCompute = () =&amp;gt; {
        console.log(&quot;계산 시작&quot;);
        heavyCompute();
        console.log(&quot;계산 종료&quot;);
    }

    // &quot;다른 작업&quot; 버튼 클릭 시 콘솔에 메세지 출력
    const onOtherTask = () =&amp;gt; console.log(&quot;다른 작업 진행 중&quot;);

    return (
        &amp;lt;&amp;gt;
            &amp;lt;button
                type=&quot;button&quot;
                onClick={onStartCompute}
                style={{ background: '#4789e4', marginRight: '10px'}}
            &amp;gt;                    
                계산 시작
            &amp;lt;/button&amp;gt;

            &amp;lt;button
                type=&quot;button&quot;
                onClick={onOtherTask}
                style={{ background: '#47e476' }}
            &amp;gt;    
                다른 작업
            &amp;lt;/button&amp;gt;
        &amp;lt;/&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 &lt;code&gt;계산 시작&lt;/code&gt; 버튼을 누를 경우 계산이 오랫동안 진행될 것이고, 이로인해 &lt;code&gt;다른 작업&lt;/code&gt; 버튼을 눌러도 계산이 완료되기 전까지는 로그가 뜨지 않을 것이다&lt;br /&gt;이 상황은 다음과 같이 표현될 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5cn0B/btsxgeTptf3/rg3FsUJSckkHrcTjGLP3AK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5cn0B/btsxgeTptf3/rg3FsUJSckkHrcTjGLP3AK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5cn0B/btsxgeTptf3/rg3FsUJSckkHrcTjGLP3AK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5cn0B%2FbtsxgeTptf3%2Frg3FsUJSckkHrcTjGLP3AK%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;486&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;836&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;code&gt;web-worker&lt;/code&gt;를 사용한다&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Web Worker 파일을 생성하여 해결하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 &lt;code&gt;web-worker&lt;/code&gt; 파일은 public 폴더에서 관리된다&lt;br /&gt;&lt;code&gt;public&lt;/code&gt; 폴더에 &lt;code&gt;computeWorker.js&lt;/code&gt; 파일을 생성해서 아래와 같이 작성한다&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;// public/computeWorker.js
self.onmessage = function(event) {
    if (event.data.command === 'startCompute') {
        let sum = 0;
        for (let i = 0; i &amp;lt; 1e9; i++) {
            sum += i;
        }
        // 계산이 완료되면 결과를 메인 스레드에 보냅니다.
        self.postMessage({ status: 'completed', result: sum });
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 App 컴포넌트에서 Web Worker를 생성하고, 메세지를 전송해서 계산을 요청하도록 처리한다&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';

export default function App(): JSX.Element {
    const [worker, setWorker] = useState&amp;lt;Worker | null&amp;gt;(null);

    useEffect(() =&amp;gt; {
        // Web Worker를 생성한다
        const newWorker = new Worker('computeWorker.js');
        setWorker(newWorker);

        // Web Worker로부터의 메세지를 처리한다
        newWorker.onmessage = (event) =&amp;gt; {
            if (event.data.status === 'completed') {
                console.log('계산 완료:', event.data.result);
            }
        };
    }, []);

    const onStartCompute = () =&amp;gt; {
        console.log('계산 시작');
        worker?.postMessage({ command: 'startCompute' });    
    };

    const onOtherTask = () =&amp;gt; console.log('다른 작업 수행 중');

    return (
        &amp;lt;&amp;gt;
            &amp;lt;button
                type=&quot;button&quot;
                onClick={onStartCompute}
                style={{ background: '#4789e4', marginRight: '10px' }}
            &amp;gt;
                계산 시작
            &amp;lt;/button&amp;gt;

            &amp;lt;button
                type=&quot;button&quot;
                onClick={onOtherTask}
                style={{ background: '#47e476' }}
            &amp;gt;
                다른 작업
            &amp;lt;/button&amp;gt;
        &amp;lt;/&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 작성한다면 로직의 프로세스는 다음과 같이 변화한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDmTgK/btsxaLDZ0RU/uPRShM4wEMCWLrk9a1ksIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDmTgK/btsxaLDZ0RU/uPRShM4wEMCWLrk9a1ksIk/img.png&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;836&quot; data-is-animation=&quot;false&quot; style=&quot;width: 34.4691%; margin-right: 10px;&quot; data-widthpercent=&quot;34.87&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDmTgK/btsxaLDZ0RU/uPRShM4wEMCWLrk9a1ksIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDmTgK%2FbtsxaLDZ0RU%2FuPRShM4wEMCWLrk9a1ksIk%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;688&quot; height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LWAbq/btsxkDreBe7/3s214ly8NdK6Bqz04SRK51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LWAbq/btsxkDreBe7/3s214ly8NdK6Bqz04SRK51/img.png&quot; data-origin-width=&quot;1294&quot; data-origin-height=&quot;842&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;65.13&quot; style=&quot;width: 64.3681%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LWAbq/btsxkDreBe7/3s214ly8NdK6Bqz04SRK51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLWAbq%2FbtsxkDreBe7%2F3s214ly8NdK6Bqz04SRK51%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;1294&quot; height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;이전 프로세스(좌), 이후 프로세스(우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그럼에도 Web Worker를 잘 사용하지 않는다던데...&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 장점을 가지고 있음에도 실제 프로젝트에서 &lt;code&gt;web-worker&lt;/code&gt; 를 잘 사용하지 않는 것을 볼 수 있는데, 그 이유는 다음과 같다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서버에서 대부분의 데이터를 처리하기에 굳이 &lt;code&gt;web-worker&lt;/code&gt; 를 사용하게 될 만한 일들이 없다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;web-worker&lt;/code&gt; 를 사용할 만큼 부하가 걸리는 작업을 안한다고 볼 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DOM 제어 및 Window 객체의 일부 함수는 메인 스레드에서만 가능하고 &lt;code&gt;web-worker&lt;/code&gt; 에서 사용이 불가능하다&lt;/li&gt;
&lt;li&gt;단순 계산식에서는 &lt;code&gt;web-worker&lt;/code&gt; 를 사용하지 않는 것이 더 빠르기도 하고, 메인 스레드와 Web Worker 사이의 데이터 전송 과정에서 비용이 발생한다&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그렇다면 도데체 언제 Web Worker를 사용하지?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확실히 특수성이 짙은 기능인 만큼 사용하면 좋은 조건들을 가지고 있다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;화면 동작에 영향을 미치는 연산 동작, 즉, 바이너리 파일 핸들링 또는 복잡한 계산이 필요한 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지나 비디오 편집 웹 애플리케이션에서 필터나 변환 작업을 할 때, 메인 스레드에서 수행하면 UI가 멈출 수 있으므로, Web Worker에서 처리하면 유용하다&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;실시간 데이터 분석 또는 모니터링 웹 앱에서 백그라운드에서 지속적으로 데이터를 체크하고 분석하는 작업을 수행할 때, Web Worker를 사용하여 메인 스레드의 부담을 줄일 수 있다&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;싱글 페이지 애플리케이션에서 페이지 전환 시 데이터를 미리 로딩하거나, 복잡한 알고리즘을 백그라운드에서 실행하여 사용자에게 빠른 응답성을 제공하는 경우에 유용하다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 이제 Web Worker를 적절하게 사용하여 더욱 쾌적한 웹 환경을 만들어보자!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;References&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@Evelyn.Taylor/how-do-web-workers-in-reactjs-transform-app-performance-31ee8e6d2d1e&quot;&gt;https://medium.com/@Evelyn.Taylor/how-do-web-workers-in-reactjs-transform-app-performance-31ee8e6d2d1e&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://myung-ho.tistory.com/92&quot;&gt;https://myung-ho.tistory.com/92&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tech.kakao.com/2021/09/02/web-worker/#:~:text=%EC%B2%AB%20%EB%B2%88%EC%A7%B8%EB%A1%9C%2C%20%ED%99%94%EB%A9%B4%20%EB%8F%99%EC%9E%91,%EC%9C%A0%EC%9A%A9%ED%95%98%EA%B2%8C%20%EC%82%AC%EC%9A%A9%EB%90%A0%20%EC%88%98%20%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot;&gt;https://tech.kakao.com/2021/09/02/web-worker/#:~:text=%EC%B2%AB%20%EB%B2%88%EC%A7%B8%EB%A1%9C%2C%20%ED%99%94%EB%A9%B4%20%EB%8F%99%EC%9E%91,%EC%9C%A0%EC%9A%A9%ED%95%98%EA%B2%8C%20%EC%82%AC%EC%9A%A9%EB%90%A0%20%EC%88%98%20%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Development Study/Frontend</category>
      <category>react</category>
      <category>web-worker</category>
      <category>병렬처리</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/256</guid>
      <comments>https://time-map-installer.tistory.com/256#entry256comment</comments>
      <pubDate>Fri, 6 Oct 2023 13:33:59 +0900</pubDate>
    </item>
    <item>
      <title>[Techeer Partners] Git Session</title>
      <link>https://time-map-installer.tistory.com/255</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파트너스에서 진행한 Git Session입니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4QEjI/btsv5kgQGvp/LpDqI32BuFE0bdxZexhJAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4QEjI/btsv5kgQGvp/LpDqI32BuFE0bdxZexhJAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4QEjI/btsv5kgQGvp/LpDqI32BuFE0bdxZexhJAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4QEjI%2Fbtsv5kgQGvp%2FLpDqI32BuFE0bdxZexhJAK%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;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&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;a href=&quot;https://youtu.be/k2bGh_My78U&quot;&gt;https://youtu.be/k2bGh_My78U&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=k2bGh_My78U&quot; data-video-width=&quot;0&quot; data-video-height=&quot;0&quot; data-video-origin-width=&quot;0&quot; data-video-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;[Techeer Partners] Git 세션&quot; data-video-thumbnail=&quot;&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/k2bGh_My78U&quot; width=&quot;0&quot; height=&quot;0&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>활동내역.zip/Techeer</category>
      <category>GIT</category>
      <category>partners</category>
      <category>Techeer</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/255</guid>
      <comments>https://time-map-installer.tistory.com/255#entry255comment</comments>
      <pubDate>Fri, 29 Sep 2023 19:30:44 +0900</pubDate>
    </item>
    <item>
      <title>[NextJS AppRouter] 초간단 API Mocking 하기(with Route Handlers)</title>
      <link>https://time-map-installer.tistory.com/254</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Nextjs.png&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EU2w4/btsv0yfhk5O/UumuvNWQedE0fAKnOEAB7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EU2w4/btsv0yfhk5O/UumuvNWQedE0fAKnOEAB7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EU2w4/btsv0yfhk5O/UumuvNWQedE0fAKnOEAB7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEU2w4%2Fbtsv0yfhk5O%2FUumuvNWQedE0fAKnOEAB7K%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;548&quot; height=&quot;336&quot; data-filename=&quot;Nextjs.png&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;.with &lt;code&gt;Route Handlers&lt;/code&gt;&lt;br&gt;&lt;code&gt;목표&lt;/code&gt; : (구) API Routes라고 불리는 현 Route Handler를 활용해서 API를 Mocking해보자&lt;br&gt;&lt;code&gt; Framework&lt;/code&gt; : NextJS 13(App Router)&lt;/p&gt;
&lt;p&gt;여기서 사용 할 예시 API는 &lt;code&gt;POST&lt;/code&gt; &lt;code&gt;api/v1/users/login&lt;/code&gt; 로그인 기능이다.&lt;br&gt;돌아가는 지 확인만 해 볼 예정으로 간단하게만 mocking 해 볼 예정&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;1. app directory에 api 경로 생성&lt;/h3&gt;
&lt;p&gt;우선, API 명세서의 엔드포인트에 맞게 경로를 생성한다.&lt;br&gt;&lt;code&gt;app&lt;/code&gt; directory 내부에 다음과 같이 생성한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app
 ┗ api 
 ┃ ┗ v1
 ┃ ┃ ┗ users
 ┃ ┃ ┃ ┣ login  
 ┃ ┃ ┃ ┃ ┗ route.ts&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;만약 엔트포인트 끝에 &lt;code&gt;id&lt;/code&gt;와 같은 특정 값이 들어오는 경우&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;route.ts&lt;/code&gt; -&amp;gt; &lt;code&gt;[id].ts&lt;/code&gt; 로 변경한다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. route.ts 파일 작성&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;route.ts&lt;/code&gt; 에 mocking 한 데이터와 로직을 작성한다.&lt;br&gt;[[Route Handlers]] 활용하여 로직을 작성하였다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;로직은 API 명세서에 맞게 작성하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// app/api/v1/users/login/route.ts

export async function POST(request: Request) {
  const { user_email, password } = await request.json()

  // 실제 데이터베이스 로직 대신 mock 데이터를 사용합니다.
  const mockUsers = [
    {
      user_email: &amp;#39;existing@example.com&amp;#39;,
      password: &amp;#39;password123&amp;#39;,
      user_uid: &amp;#39;adadsdasdasd33&amp;#39;,
    },
  ]

  const user = mockUsers.find((u) =&amp;gt; u.user_email === user_email &amp;amp;&amp;amp; u.password === password)

  if (!user) {
    return new Response(
      JSON.stringify({
        message: &amp;#39;유저 등록이 안돼있음.&amp;#39;,
        result: null,
      }),
      {
        status: 202,
        headers: {
          &amp;#39;Content-Type&amp;#39;: &amp;#39;application/json;charset=UTF-8&amp;#39;,
        },
      },
    )
  }

  // 유저를 찾았을 경우 로그인 성공 메시지를 반환합니다.
  return new Response(
    JSON.stringify({
      message: &amp;#39;로그인 성공&amp;#39;,
      result: {
        user_uid: user.user_uid,
        // 필요한 다른 데이터도 이곳에 추가할 수 있습니다.
      },
    }),
    {
      status: 200,
      headers: {
        &amp;#39;Content-Type&amp;#39;: &amp;#39;application/json;charset=UTF-8&amp;#39;,
      },
    },
  )
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;3. Postman을 활용해서 API Mocking 확인해보기&lt;/h3&gt;
&lt;p&gt;이제, Postman을 활용하여 mocking이 정상적으로 처리되는 지 확인해보자&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;POST&lt;/code&gt; &lt;code&gt;http://localhost:3000/api/v1/users/login&lt;/code&gt; 설정하기&lt;ul&gt;
&lt;li&gt;위 URL은 프로젝트, API에 따라 유동적으로 입력 할 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Body -&amp;gt; raw -&amp;gt; JSON 선택하기&lt;/li&gt;
&lt;li&gt;데이터 입력하기&lt;ul&gt;
&lt;li&gt;이 데이터 또한 API 명세에 맞게, 작성한 Mock data에 따라 적절하게 입력해볼 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;user_email&amp;quot;: &amp;quot;existing@example.com&amp;quot;,
  &amp;quot;password&amp;quot;: &amp;quot;password123&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;결과 확인하기&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;{
    &amp;quot;message&amp;quot;: &amp;quot;로그인 성공&amp;quot;,
    &amp;quot;result&amp;quot;: {
        &amp;quot;user_uid&amp;quot;: &amp;quot;adadsdasdasd33&amp;quot;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;이제, Mocking을 활용해서 병렬적으로 개발해보자!&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/routing/route-handlers&quot;&gt;https://nextjs.org/docs/app/building-your-application/routing/route-handlers&lt;/a&gt;&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>API Mocking</category>
      <category>api routes</category>
      <category>Mocking</category>
      <category>Next</category>
      <category>nextjs</category>
      <category>Route Handlers</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/254</guid>
      <comments>https://time-map-installer.tistory.com/254#entry254comment</comments>
      <pubDate>Thu, 28 Sep 2023 20:59:36 +0900</pubDate>
    </item>
    <item>
      <title>클라이언트까지 꼭 데이터가 와야할까? RCS(React Server Components)에 대해 알아보자</title>
      <link>https://time-map-installer.tistory.com/253</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;React.png&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QBIJR/btsv9OnouUY/axPrW0WMwIi7KyW8C6Uvok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QBIJR/btsv9OnouUY/axPrW0WMwIi7KyW8C6Uvok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QBIJR/btsv9OnouUY/axPrW0WMwIi7KyW8C6Uvok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQBIJR%2Fbtsv9OnouUY%2FaxPrW0WMwIi7KyW8C6Uvok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;280&quot; height=&quot;280&quot; data-filename=&quot;React.png&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;React 18에 등장한 React Server Components&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 18에서 새롭게 도입된 개념인 &lt;code&gt;React Server Component(RSC)&lt;/code&gt; 에 대해 살펴봅시다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React Server Components(RSC)란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RSC는 서버에서만 렌더링되어 전달되는 컴포넌트를 의미합니다.&lt;br /&gt;이는 HTML이 아닌 특별한 형태로 서버에서 렌더링되어 클라이언트에게 전달됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특히, &lt;code&gt;NextJS App Router&lt;/code&gt;는 이를 기본적으로 지원합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기존 방식의 문제점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트 구조는 다음과 같이 설정됩니다&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;const App = () =&amp;gt; {
    return (
        &amp;lt;Wrapper&amp;gt;
            &amp;lt;ComponentA /&amp;gt;
            &amp;lt;ComponentB /&amp;gt;
        &amp;lt;/Wrapper&amp;gt;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 방식은 각 컴포넌트에서 API 호출을 통해 데이터를 렌더링합니다.&lt;br /&gt;이렇게 하면 각 컴포넌트는 자신에게 필요한 데이터만을 받게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 컴포넌트는 아래와 같은 형식으로 구성되어있습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const Component = () =&amp;gt; {
    const [data, setData] = useState(null);

    useEffect(() =&amp;gt; {
        // API 호출 등의 로직으로 dataB를 설정
        // setData(responseData);
    }, []);

    return (
        &amp;lt;div className=&quot;component&quot;&amp;gt;
            {data ? &amp;lt;h1&amp;gt;{data.title}&amp;lt;/h1&amp;gt; : &quot;데이터를 불러오는 중...&quot;}
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 경우 아래와 같은 문제점이 발생합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 컴포넌트가 동시에 API를 호출하면 불필요한 네트워크 요청이 발생&lt;/li&gt;
&lt;li&gt;일부 컴포넌트에서 데이터를 가져오는 데 시간이 오래 걸릴 경우 전체 렌더링 성능의 저하 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서버 컴포넌트로 어떻게 해결할 수 있을까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RSC는 서버에서 렌더링되어 전체 페이지나 특정 부분에 필요한 데이터를 한 번에 가져올 수 있습니다.&lt;br /&gt;조금 더 쉽게 말해보자면 페이지의 가장 상단에서 데이터들을 호출하고 하위 컴포넌트들에 props로 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;효과&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트에서의 불필요한 네트워크 요청 감소&lt;br /&gt;-&amp;gt; API 호출 시 발생하는 네트워크비용, 렌더링 지연 감소&lt;/li&gt;
&lt;li&gt;전체 애플리케이션의 퍼포먼스 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;// ServerComponent.server.js
import { db } from './database';  // 예시로 데이터베이스 연결을 가정합니다.
function ServerComponent() {
    // 서버에서 필요한 데이터를 직접 요청합니다.
    const dataA = db.getDataForComponentA();
    const dataB = db.getDataForComponentB();

    return (
        &amp;lt;Wrapper&amp;gt;
            &amp;lt;ComponentA data={dataA} /&amp;gt;
            &amp;lt;ComponentB data={dataB} /&amp;gt;
        &amp;lt;/Wrapper&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;// Wrapper 컴포넌트는 동일하게 유지됩니다.
const Wrapper = ({ children }) =&amp;gt; {
    return (
        &amp;lt;div className=&quot;wrapper&quot;&amp;gt;
            {children}
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// ComponentA
const ComponentA = ({ data }) =&amp;gt; {
    return (
        &amp;lt;div className=&quot;componentA&quot;&amp;gt;
            &amp;lt;h1&amp;gt;{data.title}&amp;lt;/h1&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

// ComponentB
const ComponentB = ({ data }) =&amp;gt; {
    return (
        &amp;lt;div className=&quot;componentB&quot;&amp;gt;
            &amp;lt;h1&amp;gt;{data.title}&amp;lt;/h1&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서버 컴포넌트 사용 시 주의사항&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 컴포넌트에도 주의사항이 있습니다. 어떤 점들을 유의해야 할까요?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;상태 관리를 위한 로직 분리&lt;/b&gt;
&lt;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;/ol&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// Example.server.js (서버 컴포넌트)
import fetch from 'node-fetch';

function ServerComponent() {
    const data = fetch('https://api.example.com/data').then(res =&amp;gt; res.json());
    return &amp;lt;div&amp;gt;Data: {data}&amp;lt;/div&amp;gt;;
}

// Example.client.js (클라이언트 컴포넌트)
import React, { useState } from 'react';

function ClientComponent() {
    const [count, setCount] = useState(0);

    return (
        &amp;lt;div&amp;gt;
            &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;Click me!&amp;lt;/button&amp;gt;
            &amp;lt;p&amp;gt;You clicked {count} times&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;서버 컴포넌트와 클라이언트 컴포넌트 사이의 데이터 전달&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 컴포넌트로부터 데이터를 가져와 클라이언트 컴포넌트에게 props로 전달해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// App.server.js (서버 컴포넌트에서 데이터 가져오기)
import fetch from 'node-fetch';
import ClientComponent from './Example.client.js';

function App() {
    const data = fetch('https://api.example.com/data').then(res =&amp;gt; res.json());
    return &amp;lt;ClientComponent serverData={data} /&amp;gt;;
}

// Example.client.js (클라이언트 컴포넌트에서 데이터 사용하기)
import React from 'react';

function ClientComponent({ serverData }) {
    return (
        &amp;lt;div&amp;gt;
            &amp;lt;h1&amp;gt;Data from Server: {serverData}&amp;lt;/h1&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시는 간단한 형태로 &lt;code&gt;RSC&lt;/code&gt;를 통한 데이터 전달 및 클라이언트에서의 상태 관리를 보여주는 예시 코드입니다.&lt;br /&gt;실제 구현 시에는 데이터 처리, 에러 핸들링 등의 추가적인 작업을 유동적으로 수행해주세요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;+1 서버 컴포넌트의 장점&lt;/b&gt;: 번들 사이즈 감소&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;번들 사이즈 감소(&lt;code&gt;zero-bundle-size&lt;/code&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;서버 컴포넌트가 클라이언트 번들에 포함되지 않는다는 것으로 사실상 번들의 크기가 0이라고 볼 수 있습니다. 따라서 RSC는 &lt;code&gt;zero-bundle-size React Server Components&lt;/code&gt; 라고도 불립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;+2 서버 컴포넌트의 장점&lt;/b&gt;: 자동 코드 분할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 컴포넌트와 자동 코드 분할을 함께 사용하면 더욱 효과적입니다.&lt;br /&gt;React Server Components는 필요한 컴포넌트만을 동적으로 불러와 퍼포먼스를 향상시킬 수 있습니다.&lt;br /&gt;React에서 전통적으로 사용되는 클라이언트 컴포넌트의 코드 분할과 서버 컴포넌트를 이용한 코드 분할의 차이점을 보면서 확인해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 전통적인 클라이언트 컴포넌트의 코드 분할&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React의 클라이언트 사이드에서는 &lt;code&gt;React.lazy()&lt;/code&gt;와 함께 &lt;code&gt;import()&lt;/code&gt; 문법을 사용하여 컴포넌트를 동적으로 불러올 수 있습니다.&lt;br /&gt;-&amp;gt; 즉, &lt;code&gt;레이지 로딩(lazy loading)&lt;/code&gt;을 사용하는 방법입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { Suspense } from 'react';

const LazyComponent = React.lazy(() =&amp;gt; import('./LazyComponent'));

function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Suspense fallback={&amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;}&amp;gt;
        &amp;lt;LazyComponent /&amp;gt;
      &amp;lt;/Suspense&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 &lt;code&gt;LazyComponent&lt;/code&gt;는 실제로 필요할 때만(렌더링 시점에) 동적으로 불러와지며, 그 전까지는 불러오지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. &lt;code&gt;React Server Components&lt;/code&gt;를 사용한 자동 코드 분할&lt;/b&gt;&lt;br /&gt;반면에 서버 컴포넌트를 사용하면, 클라이언트에서 불러오는 번들에 포함되지 않는 컴포넌트나 라이브러리를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// MyServerComponent.server.js

import ExpensiveLibrary from 'expensive-library';  // ZERO IMPACT on client bundle size

function MyServerComponent() {
  const result = ExpensiveLibrary.someFunction();
  return &amp;lt;div&amp;gt;{result}&amp;lt;/div&amp;gt;;
}

export default MyServerComponent;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 &lt;code&gt;ExpensiveLibrary&lt;/code&gt;는 클라이언트의 번들에 포함되지 않습니다.&lt;br /&gt;따라서 클라이언트 측에서의 번들 사이즈는 이 라이브러리의 크기만큼 감소합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 서버 컴포넌트는 무거운 라이브러리나 코드를 클라이언트 번들에서 제외할 수 있어 초기 로딩 시간을 크게 줄일 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 컴포넌트와 클라이언트 컴포넌트의 적절한 조합은 React 애플리케이션의 성능과 사용자 경험을 크게 향상시킬 수 있습니다.&lt;br /&gt;NextJS의 Server Components와 함께 사용하여 그 효과를 극대화시켜보아요!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;References&lt;/h3&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nookpi.tistory.com/35&quot;&gt;reference1&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://xionwcfm.tistory.com/393#:~:text=react%2018%20%EB%B2%84%EC%A0%84%EB%B6%80%ED%84%B0%20%EB%8F%84%EC%9E%85,%EB%90%98%EC%96%B4%20%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EC%97%90%EA%B2%8C%20%EC%A0%84%EB%8B%AC%EB%90%A9%EB%8B%88%EB%8B%A4.&quot;&gt;reference2&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://velog.io/@zerone/React-Server-Component-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%84%9C%EB%B2%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%99%9C%EC%9A%A9%ED%95%A0%EA%B9%8C&quot;&gt;reference3&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/react-server-components/&quot;&gt;reference4&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://react.dev/blog/2020/12/21/data-fetching-with-react-server-components&quot;&gt;reference5&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://legacy.reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html&quot;&gt;reference6&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://ui.toast.com/weekly-pick/ko_20210119&quot;&gt;reference7&lt;/a&gt;&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>react</category>
      <category>React 18</category>
      <category>React Server Components</category>
      <category>Server Components</category>
      <category>서버 컴포넌트</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/253</guid>
      <comments>https://time-map-installer.tistory.com/253#entry253comment</comments>
      <pubDate>Thu, 28 Sep 2023 00:15:58 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js App Router] Next.js 13 주요 변경사항 알아보기</title>
      <link>https://time-map-installer.tistory.com/252</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 Next.js 13버전이 Stable이 되고 시간이 흘러 안정되었다고 하여 이를 프로젝트에 적용해보고자 하였고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Page Router였던 12버전 대신 App Router을 사용할 예정이므로 큰 변환점을 중심으로 하여 리뷰해보려고 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2774&quot; data-origin-height=&quot;1520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XEeGC/btsueKOr5LU/adUKrmxcKrfCMAEeI5pakk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XEeGC/btsueKOr5LU/adUKrmxcKrfCMAEeI5pakk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XEeGC/btsueKOr5LU/adUKrmxcKrfCMAEeI5pakk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXEeGC%2FbtsueKOr5LU%2FadUKrmxcKrfCMAEeI5pakk%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;2774&quot; height=&quot;1520&quot; data-origin-width=&quot;2774&quot; data-origin-height=&quot;1520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다들 Next.js가 React를 기반으로 한 프레임워크라는 것을 알고계신다는 전제 하에 진행하도록 하겠습니다!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;간단하게 알아볼 5가지 특징들(App Router 기준)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2242&quot; data-origin-height=&quot;1252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb0344/btsuICBltsk/zmJ10xpGaWdvLnKa863DXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb0344/btsuICBltsk/zmJ10xpGaWdvLnKa863DXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb0344/btsuICBltsk/zmJ10xpGaWdvLnKa863DXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb0344%2FbtsuICBltsk%2FzmJ10xpGaWdvLnKa863DXK%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;2242&quot; height=&quot;1252&quot; data-origin-width=&quot;2242&quot; data-origin-height=&quot;1252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 알아볼 5가지는 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;App Router로 넘어오면서 새로 등장한 &lt;b&gt;app directory&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;App Router로 넘어오면서 변경된 &lt;b&gt;Rendering&lt;/b&gt; 설정방법&lt;/li&gt;
&lt;li&gt;개선된 Navigation tag, &lt;b&gt;&amp;lt;Link&amp;gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;API Routes&lt;/b&gt;를 통한 백엔드 접근&lt;/li&gt;
&lt;li&gt;보다 개선된 이미지 렌더링을 위해 사용된 &lt;b&gt;&amp;lt;Image&amp;gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Using App Directory&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2232&quot; data-origin-height=&quot;1236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TPPab/btsut2H3gWf/CV6uLRuNOvq2GteX3sLYZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TPPab/btsut2H3gWf/CV6uLRuNOvq2GteX3sLYZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TPPab/btsut2H3gWf/CV6uLRuNOvq2GteX3sLYZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTPPab%2Fbtsut2H3gWf%2FCV6uLRuNOvq2GteX3sLYZ0%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;2232&quot; height=&quot;1236&quot; data-origin-width=&quot;2232&quot; data-origin-height=&quot;1236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 12버전까지 Page Router를 사용하면서 `&lt;b&gt;/page&lt;/b&gt;` directory 내부에 페이지를 이루는 파일들을 생성해서 관리했다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;13버전 이후에는 App Router로 변경되면서 `&lt;b&gt;/app&lt;/b&gt;` directory 내부에 `&lt;b&gt;/page&lt;/b&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;h4 data-ke-size=&quot;size20&quot;&gt;2. How to set rendering method in App Router&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pMp0v/btsudM7CWJZ/GMJyHm6A9hPbymgFv8nP31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pMp0v/btsudM7CWJZ/GMJyHm6A9hPbymgFv8nP31/img.png&quot; data-origin-width=&quot;1724&quot; data-origin-height=&quot;1110&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.2353%; margin-right: 10px;&quot; data-widthpercent=&quot;50.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pMp0v/btsudM7CWJZ/GMJyHm6A9hPbymgFv8nP31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpMp0v%2FbtsudM7CWJZ%2FGMJyHm6A9hPbymgFv8nP31%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;1724&quot; height=&quot;1110&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6OFPM/btsuGwn6ISX/GKqAFYjOkYicW2M2xJN9g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6OFPM/btsuGwn6ISX/GKqAFYjOkYicW2M2xJN9g1/img.png&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;1130&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.6019%;&quot; data-widthpercent=&quot;49.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6OFPM/btsuGwn6ISX/GKqAFYjOkYicW2M2xJN9g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6OFPM%2FbtsuGwn6ISX%2FGKqAFYjOkYicW2M2xJN9g1%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;1698&quot; height=&quot;1130&quot;/&gt;&lt;/span&gt;&lt;/div&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;기존에는 SSR(Server Side Rendering), CSR(Client Side Rendering), SSG(Static Side Generation)라는 용어를 통해 문서화 되어있었고, 이에 따른 렌더링을 했었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 App Router로 넘어오변서 Client Component와 Server Component로 이름이 변경되면서 그 적용방식도 바뀌었습니다.&lt;/p&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;u&gt;&lt;b&gt;Client Component&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSR을 적용하기 위한 방법이 정말 간단해졌습니다. 바로 코드파일 위해 &lt;b&gt;'use client'&lt;/b&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;아래와 같이 즉시 많은 렌더링이 되어야 하는 페이지라면 이 &lt;b&gt;'use client'&lt;/b&gt;를 활용해 보는 것이 좋겠죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;258147865-b244a2ec-8f0a-4238-92dd-d21c6ac0a488.gif&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1vk6a/btsugUqmF3B/XyG3ilgdYsfy2zJ2LTO24k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1vk6a/btsugUqmF3B/XyG3ilgdYsfy2zJ2LTO24k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1vk6a/btsugUqmF3B/XyG3ilgdYsfy2zJ2LTO24k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/1vk6a/btsugUqmF3B/XyG3ilgdYsfy2zJ2LTO24k/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;1436&quot; height=&quot;626&quot; data-filename=&quot;258147865-b244a2ec-8f0a-4238-92dd-d21c6ac0a488.gif&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;626&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;u&gt;&lt;b&gt;Server Component&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR은 Server Component로 여기서 그 의미가 변경되었습니다. Next.js의 기본적인 특징이 서버 사이드 렌더링이므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;'use server'&amp;nbsp;&lt;/b&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;h4 data-ke-size=&quot;size20&quot;&gt;3. 개선된 Navigation(Routing method), &amp;lt;Link&amp;gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2186&quot; data-origin-height=&quot;1220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWUNj7/btsudOEo96X/I3nsMh7VEMWghVwdkWOiL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWUNj7/btsudOEo96X/I3nsMh7VEMWghVwdkWOiL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWUNj7/btsudOEo96X/I3nsMh7VEMWghVwdkWOiL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWUNj7%2FbtsudOEo96X%2FI3nsMh7VEMWghVwdkWOiL0%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;2186&quot; height=&quot;1220&quot; data-origin-width=&quot;2186&quot; data-origin-height=&quot;1220&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 에서 개선되고 발전 된 &lt;b&gt;라우팅 방식&lt;/b&gt;과 &lt;b&gt;네비게이션 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 &lt;b&gt;&amp;lt;Link&amp;gt;&lt;/b&gt; 태그를 통해 라우트를 진행하며 위에서 볼 수 있다시피 &lt;b&gt;query&lt;/b&gt;를 통해 &lt;b&gt;params&lt;/b&gt;를 지정해서 넘길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 태그 내부에 &amp;lt;a&amp;gt;태그가 들어있었지만 업데이트가 되면서 이 부분이 생략되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 폴더 라우트가 적용되어있어서 위에서 볼 수 있듯 &lt;b&gt;/app 하위의 경로&lt;/b&gt;를 입력하면 해당 디렉터리로 라우팅되는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. API Routes&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2210&quot; data-origin-height=&quot;1220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t3Sfy/btsueLOfsnI/bb1CsJtXkH6ULxjJIm3Rzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t3Sfy/btsueLOfsnI/bb1CsJtXkH6ULxjJIm3Rzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t3Sfy/btsueLOfsnI/bb1CsJtXkH6ULxjJIm3Rzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft3Sfy%2FbtsueLOfsnI%2Fbb1CsJtXkH6ULxjJIm3Rzk%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;2210&quot; height=&quot;1220&quot; data-origin-width=&quot;2210&quot; data-origin-height=&quot;1220&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 분들이 프론트엔드 프레임워크라고 말하고 있는 이 Next.js는 엄밀히 따져보자면 &lt;b&gt;풀스택 프레임워크&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 이 &lt;b&gt;API Routes를 통해 DB에 접근&lt;/b&gt;할 수 있다는 점이 그 이유이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2134&quot; data-origin-height=&quot;1198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EAXjS/btsuAq9xvSF/a1fGEW28DqoUXFJIklvMY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EAXjS/btsuAq9xvSF/a1fGEW28DqoUXFJIklvMY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EAXjS/btsuAq9xvSF/a1fGEW28DqoUXFJIklvMY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEAXjS%2FbtsuAq9xvSF%2Fa1fGEW28DqoUXFJIklvMY1%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;2134&quot; height=&quot;1198&quot; data-origin-width=&quot;2134&quot; data-origin-height=&quot;1198&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 많은 분들이 React를 통해 프로젝트를 만들어 볼 때 &lt;b&gt;(MSW)Mock Service Worker&lt;/b&gt;를 통해 &lt;b&gt;API를 Mocking&lt;/b&gt;하고 백엔드와 병렬적으로 개발을 해왔었는데요, Next.js 프로젝트에서는 백엔드와 따로 작업을 하므로 이 &lt;b&gt;API Routes&lt;/b&gt;를 활용하여 &lt;b&gt;Mocking&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;API Routes를 활용하기 위해서는 간단하게 아래와 같이 apis 라는 디렉터리를 만든 후 그 안에 일정 형식을 맞추어 사용하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;1042&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmZFlz/btsugUw7KOc/kGlIwJ7BDK6cYHaiN4YHUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmZFlz/btsugUw7KOc/kGlIwJ7BDK6cYHaiN4YHUk/img.png&quot; data-alt=&quot;실제 프로젝트에서 적용해 보았던 directory&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmZFlz/btsugUw7KOc/kGlIwJ7BDK6cYHaiN4YHUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmZFlz%2FbtsugUw7KOc%2FkGlIwJ7BDK6cYHaiN4YHUk%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;1666&quot; height=&quot;1042&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;1042&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실제 프로젝트에서 적용해 보았던 directory&lt;/figcaption&gt;
&lt;/figure&gt;
&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;5. 보다 개선된 이미지 렌더링을 위해 사용된 &amp;lt;Image&amp;gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1806&quot; data-origin-height=&quot;1150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vtZXR/btsugRAnOzT/FqhaTozVEGKDHYhmr1gkg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vtZXR/btsugRAnOzT/FqhaTozVEGKDHYhmr1gkg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vtZXR/btsugRAnOzT/FqhaTozVEGKDHYhmr1gkg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvtZXR%2FbtsugRAnOzT%2FFqhaTozVEGKDHYhmr1gkg0%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;1806&quot; height=&quot;1150&quot; data-origin-width=&quot;1806&quot; data-origin-height=&quot;1150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;next/image를 import&lt;/b&gt;해서 &lt;b&gt;&amp;lt;Image&amp;gt;&lt;/b&gt; 보다 개선된 &lt;b&gt;&amp;lt;img&amp;gt;&lt;/b&gt;인 &lt;b&gt;&amp;lt;Image&amp;gt;&lt;/b&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&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;References&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/docs&lt;/a&gt;&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>13</category>
      <category>app router</category>
      <category>Client</category>
      <category>next.js</category>
      <category>rendering</category>
      <category>tag</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/252</guid>
      <comments>https://time-map-installer.tistory.com/252#entry252comment</comments>
      <pubDate>Mon, 18 Sep 2023 19:51:53 +0900</pubDate>
    </item>
    <item>
      <title>[2nd Good Night Hackathon] 해커톤 프레임워크 어떤걸로 할까? Svelte Go?</title>
      <link>https://time-map-installer.tistory.com/251</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;[Good Night Hackathon] 2023/08/26 10:00 ~ 2023/08/28 00:00&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 반년만에 열린 2회차 Good Night Hackathon에 참여하였습니다.&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;a href=&quot;https://time-map-installer.tistory.com/175&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://time-map-installer.tistory.com/175&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1693215567541&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;[Good Night Hackathon] SpringBoot! 맛 좀 보자!&quot; data-og-description=&quot;2023 Serieses [ 2023 ] CheckPoint, 2023년 2023년동안 작성했던 회고록들을 모아 둔 게시글이다 2023년 동안 작성한 회고들을 계속해서 업데이트 해 나갈 예정이다 [ January ] - 변화의 시작, 1월 [2022WinterBootcam&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/175&quot; data-og-url=&quot;https://time-map-installer.tistory.com/175&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cy80EW/hyTINs823K/UGW1jG2jZR5vT3ObKXExQ0/img.png?width=800&amp;amp;height=360&amp;amp;face=547_21_607_87,https://scrap.kakaocdn.net/dn/b6SvS1/hyTMbFTJqe/uyTNkR7lEbYWXS7397aFGK/img.png?width=800&amp;amp;height=360&amp;amp;face=547_21_607_87,https://scrap.kakaocdn.net/dn/bu3HKr/hyTMebx2HQ/sEUH12JLh3KRlzBJDKCz00/img.png?width=1430&amp;amp;height=644&amp;amp;face=0_0_1430_644&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/175&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/175&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cy80EW/hyTINs823K/UGW1jG2jZR5vT3ObKXExQ0/img.png?width=800&amp;amp;height=360&amp;amp;face=547_21_607_87,https://scrap.kakaocdn.net/dn/b6SvS1/hyTMbFTJqe/uyTNkR7lEbYWXS7397aFGK/img.png?width=800&amp;amp;height=360&amp;amp;face=547_21_607_87,https://scrap.kakaocdn.net/dn/bu3HKr/hyTMebx2HQ/sEUH12JLh3KRlzBJDKCz00/img.png?width=1430&amp;amp;height=644&amp;amp;face=0_0_1430_644');&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;[Good Night Hackathon] SpringBoot! 맛 좀 보자!&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2023 Serieses [ 2023 ] CheckPoint, 2023년 2023년동안 작성했던 회고록들을 모아 둔 게시글이다 2023년 동안 작성한 회고들을 계속해서 업데이트 해 나갈 예정이다 [ January ] - 변화의 시작, 1월 [2022WinterBootcam&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;이번에는 한 가지의 기술스택이 아닌 작성 시점 기준 가장 활발하게 채용시장에서 사용되는 프레임워크들을 선택해서 사용하도록 하게 하였는데요, 무언가 선택지가 넓어지고 평소에 접해보지 못했던 것들을 해보면서 굉장히 도움이 되었습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이번에는 하루는 백엔드를, 그리고 그 다음날에는 프론트엔드를 하도록 하여 어제 만들었던 나의 API를 직접 프론트로 연동해서 띄워볼 수 있던 너무 좋았던 시간이었습니다.&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1일차 Backend, Go를 활용한 API 개발해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 날에는 백엔드 프레임워크를 하나 선택하여 미리 운영진이 작성해둔 요구조건을 통해 API를 개발해보는 것이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해커톤의 오프닝과 간단한 백엔드에 관련된 세션들(ORM (데이터베이스 접근하는 방법), 패키지 설계) 시간이 있었지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파트너스 신규 인원 모집에 따른 면접 진행으로 인해 듣지 못해서 아쉬웠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Kotlin + Spring Boot&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;TypeScript + Nest.js&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Go&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 세 가지 중 하나를 선택해서 할 수 있었고, 저는 Google에서 개발했고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점점 시장에서 점유율을 높여가는 Go를 선택해서 개발해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Backend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Backend&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1693216160336&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - TMInstaller/Good-Night-2nd-Hackathon-Backend: Team Joon : Good Night Hackathon 입문자 대상으로 API 를 만들어&quot; data-og-description=&quot;Team Joon : Good Night Hackathon 입문자 대상으로 API 를 만들어 보는 것을 목표로, 줌에서 8/26 10pm ~ 끝날때 까지 진행합니다. - GitHub - TMInstaller/Good-Night-2nd-Hackathon-Backend: Team Joon : Good Night Hackathon 입문자 &quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Backend&quot; data-og-url=&quot;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Backend&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DULm2/hyTINzW6jQ/mosXp16wKgAqzkHHKO5f3K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Backend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Backend&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DULm2/hyTINzW6jQ/mosXp16wKgAqzkHHKO5f3K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - TMInstaller/Good-Night-2nd-Hackathon-Backend: Team Joon : Good Night Hackathon 입문자 대상으로 API 를 만들어&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Team Joon : Good Night Hackathon 입문자 대상으로 API 를 만들어 보는 것을 목표로, 줌에서 8/26 10pm ~ 끝날때 까지 진행합니다. - GitHub - TMInstaller/Good-Night-2nd-Hackathon-Backend: Team Joon : Good Night Hackathon 입문자&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미완성으로 끝나긴 했으나 일부 API가 돌아간다는 점에서 만족하는 1일차였습니다.&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;Go를 만났던 첫인상&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 Google에서 개발하고 밀어주는 언어이다 보니 처음엔 기대가 되었습니다.&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;파이썬 + JavaScript를 닮은 언어&lt;/li&gt;
&lt;li&gt;뭔가 이상하게 귀여운 시그니처 캐릭터&lt;/li&gt;
&lt;li&gt;Node.js처럼 정해진 구조 없이 코드 구성을 해도 되는 언어&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도 느낌을 받고 시작했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 IDE는 Jetbrain의 Goland를 사용하였습니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NHJGY/btsslclIhRX/GPHyftorAWnJFdSMNY8kN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NHJGY/btsslclIhRX/GPHyftorAWnJFdSMNY8kN1/img.png&quot; data-alt=&quot;이런 느낌의 언어였습니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NHJGY/btsslclIhRX/GPHyftorAWnJFdSMNY8kN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNHJGY%2FbtsslclIhRX%2FGPHyftorAWnJFdSMNY8kN1%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;721&quot; height=&quot;351&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;351&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;h4 data-ke-size=&quot;size20&quot;&gt;Go를 했을 때의 끝인상&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안 Java에 고통받으며 지내왔던 시절에 비해서 생각보다 코드의 길이가 길지는 않아서 좋았습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그나마 길었던 부분이 있다면 변수명 정도였을까요 ㅋㅋㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 작성해보는 것이었는데도 그동안 숙련되어온 GPT를 활용하면서 꽤 많은 부분을 구현해냈기에 너무나도 좋았습니다.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2일차 Frontend, Svelte를 활용한 API 개발해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 날에는 Frontend 프레임워크를 선택하여 해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 4가지의 선택지가 있었으며 아래와 같았습니다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;React.js + TypeScript&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Next.js + TypeScript&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Svelte + TypeScript&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Flutter + Dart&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1JJPYr6de_A4TZ9nyYscd7t_PCCKyq6WlwQ2NWEodSf0/edit?usp=sharing&quot;&gt;https://docs.google.com/presentation/d/1JJPYr6de_A4TZ9nyYscd7t_PCCKyq6WlwQ2NWEodSf0/edit?usp=sharing&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1693218416369&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;Frontend 프레임워크 선택 가이드&quot; data-og-description=&quot;React? Next? Svelte? 어떤 프레임워크를 사용해야할까? Good Night Hackathon 참가자들을 위한 가이드 발표자: 백동열&quot; data-og-host=&quot;docs.google.com&quot; data-og-source-url=&quot;https://docs.google.com/presentation/d/1JJPYr6de_A4TZ9nyYscd7t_PCCKyq6WlwQ2NWEodSf0/edit?usp=sharing&quot; data-og-url=&quot;https://docs.google.com/presentation/d/1JJPYr6de_A4TZ9nyYscd7t_PCCKyq6WlwQ2NWEodSf0/edit?usp=sharing&amp;amp;usp=embed_facebook&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eamzwB/hyTIJ5lhKt/5I3xDFydlkOKOIwFoDE6D1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1JJPYr6de_A4TZ9nyYscd7t_PCCKyq6WlwQ2NWEodSf0/edit?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.google.com/presentation/d/1JJPYr6de_A4TZ9nyYscd7t_PCCKyq6WlwQ2NWEodSf0/edit?usp=sharing&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eamzwB/hyTIJ5lhKt/5I3xDFydlkOKOIwFoDE6D1/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;Frontend 프레임워크 선택 가이드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;React? Next? Svelte? 어떤 프레임워크를 사용해야할까? Good Night Hackathon 참가자들을 위한 가이드 발표자: 백동열&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;약 10분정도 진행하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/rR2eSIq4jwA&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://youtu.be/rR2eSIq4jwA&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=rR2eSIq4jwA&quot; data-video-width=&quot;0&quot; data-video-height=&quot;0&quot; data-video-origin-width=&quot;0&quot; data-video-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;굿나잇 해커톤 프론트엔드 프레임워크 선택가이드 세션영상&quot; data-video-thumbnail=&quot;&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/rR2eSIq4jwA&quot; width=&quot;0&quot; height=&quot;0&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&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;이 외에도 컴포넌트와 Axios에 대한 세션이 진행되었고, 저는 Svelte를 선택하여 2일차 과제를 수행하기 시작하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Frontend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Frontend&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1693218683153&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - TMInstaller/Good-Night-2nd-Hackathon-Frontend: Team Joon : Good Night Hackathon 입문자를 대상으로 영화 리뷰&quot; data-og-description=&quot;Team Joon : Good Night Hackathon 입문자를 대상으로 영화 리뷰 앱 페이지를 만들어 보는 것을 목표로, 줌에서 8/27 10pm ~ 끝날때 까지 진행합니다. - GitHub - TMInstaller/Good-Night-2nd-Hackathon-Frontend: Team Joon : Good &quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Frontend&quot; data-og-url=&quot;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Frontend&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bY25vk/hyTL2PIMJU/OKFWM8gOpjhcFXrxeNPX3k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Frontend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/TMInstaller/Good-Night-2nd-Hackathon-Frontend&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bY25vk/hyTL2PIMJU/OKFWM8gOpjhcFXrxeNPX3k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - TMInstaller/Good-Night-2nd-Hackathon-Frontend: Team Joon : Good Night Hackathon 입문자를 대상으로 영화 리뷰&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Team Joon : Good Night Hackathon 입문자를 대상으로 영화 리뷰 앱 페이지를 만들어 보는 것을 목표로, 줌에서 8/27 10pm ~ 끝날때 까지 진행합니다. - GitHub - TMInstaller/Good-Night-2nd-Hackathon-Frontend: Team Joon : Good&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Svelte를 만났던 첫인상&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Svelte, 2015년에 만들어져서 최근에 한 번 반짝 했다가 지금은 점유율을 점점 높여가고 있는 언어인데요,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 보았을 때 이게 진짜 JavaScript 기반 언어라고? 라는 생각이 들 정도로 구조가 특이했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 &amp;lt;script&amp;gt; 태그 안에다가는 JS 코드를, 그 밖에 다른 html 태그들에는 웹에서 표현되는 부분에 대한 코드를,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;style&amp;gt; 태그 안에는 CSS 코드를 입력하면 되는 굉장히 간단한 구조였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심지어 &lt;b&gt;yarn create vite &amp;lt;project_name&amp;gt;&lt;/b&gt; 으로 생성하고 템플릿과 언어를 선택하면 되는 굉장히 간단한 생성과정을 거쳤습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;나의 백엔드는 미완성이었기에..&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGqznc/btssk8RbmMa/VC0bJLhy2sVKeRGmY3Yii0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGqznc/btssk8RbmMa/VC0bJLhy2sVKeRGmY3Yii0/img.png&quot; data-alt=&quot;Swagger 파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGqznc/btssk8RbmMa/VC0bJLhy2sVKeRGmY3Yii0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGqznc%2Fbtssk8RbmMa%2FVC0bJLhy2sVKeRGmY3Yii0%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;1222&quot; height=&quot;736&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;736&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Swagger 파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 만들었던 백엔드는 미완성 버전이라 정답 코드로 올라왔던 멘토분의 Go 레포를 클론해서 연결했습니다&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;Svelte를 했을 때의 끝인상&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적응을 하고나니 완전한 신세계를 겪었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 굉장히 간편하고, 구성도 좋고, typescript도 아래처럼 lang='ts'만 쓰면 되니 신경 쓸 것들이 적어서 좋았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4BcGf/btssvU5ugih/4xp7ShvBIBxjUI5aZrDA1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4BcGf/btssvU5ugih/4xp7ShvBIBxjUI5aZrDA1k/img.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;486&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;47.93&quot; style=&quot;width: 47.3757%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4BcGf/btssvU5ugih/4xp7ShvBIBxjUI5aZrDA1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4BcGf%2FbtssvU5ugih%2F4xp7ShvBIBxjUI5aZrDA1k%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;624&quot; height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EqBgC/btssgl4V0ul/ekzp7CCK9elKjwPuNdYcCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EqBgC/btssgl4V0ul/ekzp7CCK9elKjwPuNdYcCK/img.png&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;489&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.4615%;&quot; data-widthpercent=&quot;52.07&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EqBgC/btssgl4V0ul/ekzp7CCK9elKjwPuNdYcCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEqBgC%2Fbtssgl4V0ul%2Fekzp7CCK9elKjwPuNdYcCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;489&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1Xt6M/btssqmO9Ddm/lBMaCejbJCcYVaFAkrvth1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1Xt6M/btssqmO9Ddm/lBMaCejbJCcYVaFAkrvth1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1Xt6M/btssqmO9Ddm/lBMaCejbJCcYVaFAkrvth1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1Xt6M%2FbtssqmO9Ddm%2FlBMaCejbJCcYVaFAkrvth1%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;668&quot; height=&quot;292&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;292&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;Router 설정도 Next.js에서 지원하는 것과 같은 파일 시스템 기반 라우팅을 이용할 수도 있었고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 작성했던 것처럼 React Router처럼 설정도 할 수 있었습니다.&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-origin-width=&quot;728&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m0L8L/btssfT1VSx2/aj2we191xW1hgVhzEZWrU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m0L8L/btssfT1VSx2/aj2we191xW1hgVhzEZWrU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m0L8L/btssfT1VSx2/aj2we191xW1hgVhzEZWrU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm0L8L%2FbtssfT1VSx2%2Faj2we191xW1hgVhzEZWrU1%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;728&quot; height=&quot;508&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;508&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;코드의 길이가 짧고 렌더링이 빨라 간단한 프로젝트를 만들 때 특히 더 도움이 되지 않을까 싶었습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 개인적으로 참여하기로 한 프로젝트 하나에 Svelte를 활용하여 프론트를 구성해 볼 계획을 가지고 있습니다.&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과물&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;화면-기록-2023-08-28-오후-7.55.42.gif&quot; data-origin-width=&quot;1772&quot; data-origin-height=&quot;1174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxRaB0/btssgJLonlt/dKh3Oh2ODjRpEU46siOzf0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxRaB0/btssgJLonlt/dKh3Oh2ODjRpEU46siOzf0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxRaB0/btssgJLonlt/dKh3Oh2ODjRpEU46siOzf0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bxRaB0/btssgJLonlt/dKh3Oh2ODjRpEU46siOzf0/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;1772&quot; height=&quot;1174&quot; data-filename=&quot;화면-기록-2023-08-28-오후-7.55.42.gif&quot; data-origin-width=&quot;1772&quot; data-origin-height=&quot;1174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽게도 현 시점에서는 API가 연동되어서 나타나는 모습을 보여드리지는 못하는 상황인데요,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 Three.js로 구현한 메인화면은 건져서 다행이네요&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&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;아무래도 이 만족스럽다 라는 말을 자주 쓰는 것을 보면 더 그런가봅니다&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;</description>
      <category>활동내역.zip/Techeer</category>
      <category>go</category>
      <category>Hackathon</category>
      <category>svelte</category>
      <category>TypeScript</category>
      <category>해커톤</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/251</guid>
      <comments>https://time-map-installer.tistory.com/251#entry251comment</comments>
      <pubDate>Mon, 28 Aug 2023 20:03:29 +0900</pubDate>
    </item>
    <item>
      <title>[RememberPlus] 리더로 활동하면서 어떻게 성장했을까? 2번째 부트캠프를 마치며.</title>
      <link>https://time-map-installer.tistory.com/250</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;태그: Techeer&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6주 동안의 일들을 담아냈기에 다소 양이 많을 수 있다는 점을 미리 알립니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;활동명: &lt;code&gt;SilliconValley 2023 Summer Bootcamp&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할: &lt;code&gt;리더&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기여 분야: &lt;code&gt;Frontend&lt;/code&gt;, &lt;code&gt;배포&lt;/code&gt;, &lt;code&gt;문서화&lt;/code&gt;, &lt;code&gt;팀원 관리&lt;/code&gt;, &lt;code&gt;최종 발표&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub: &lt;a href=&quot;https://github.com/SV-Summer-BootCamp-Team-F&quot;&gt;https://github.com/SV-Summer-BootCamp-Team-F&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691667637362&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;profile&quot; data-og-title=&quot;SV-Summer-BootCamp-Team-F&quot; data-og-description=&quot;SV-Summer-BootCamp-Team-F has 4 repositories available. Follow their code on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/SV-Summer-BootCamp-Team-F&quot; data-og-url=&quot;https://github.com/SV-Summer-BootCamp-Team-F&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MWwZ3/hyTCDh55ZQ/KvrKNttXk0mp9L7ZUbzXk0/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/bMEiM5/hyTBJEhCGI/7DEdGxvEKOfG0HFiKoCKNK/img.png?width=1280&amp;amp;height=879&amp;amp;face=0_0_1280_879,https://scrap.kakaocdn.net/dn/dxjxkK/hyTCJo6qye/gcp9aVNVOv2ApLyz1D04Fk/img.png?width=640&amp;amp;height=640&amp;amp;face=0_0_640_640&quot;&gt;&lt;a href=&quot;https://github.com/SV-Summer-BootCamp-Team-F&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/SV-Summer-BootCamp-Team-F&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MWwZ3/hyTCDh55ZQ/KvrKNttXk0mp9L7ZUbzXk0/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/bMEiM5/hyTBJEhCGI/7DEdGxvEKOfG0HFiKoCKNK/img.png?width=1280&amp;amp;height=879&amp;amp;face=0_0_1280_879,https://scrap.kakaocdn.net/dn/dxjxkK/hyTCJo6qye/gcp9aVNVOv2ApLyz1D04Fk/img.png?width=640&amp;amp;height=640&amp;amp;face=0_0_640_640');&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;SV-Summer-BootCamp-Team-F&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SV-Summer-BootCamp-Team-F has 4 repositories available. Follow their code on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Notion&lt;/p&gt;
&lt;figure id=&quot;og_1691667635055&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;2023 Summer Bootcamp Team-F&quot; data-og-description=&quot;⬇️&amp;nbsp;프로젝트 포트폴리오 바로가기&quot; data-og-host=&quot;changeable-team-242.notion.site&quot; data-og-source-url=&quot;https://changeable-team-242.notion.site/2023-Summer-Bootcamp-Team-F-b2cd552a334f498fb6e047a6fc8aa4ae?pvs=4&quot; data-og-url=&quot;https://changeable-team-242.notion.site/b2cd552a334f498fb6e047a6fc8aa4ae&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://changeable-team-242.notion.site/2023-Summer-Bootcamp-Team-F-b2cd552a334f498fb6e047a6fc8aa4ae?pvs=4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://changeable-team-242.notion.site/2023-Summer-Bootcamp-Team-F-b2cd552a334f498fb6e047a6fc8aa4ae?pvs=4&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;2023 Summer Bootcamp Team-F&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;⬇️&amp;nbsp;프로젝트 포트폴리오 바로가기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;changeable-team-242.notion.site&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;발표영상(1:24:14~1:29:40)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/live/gnqpdrnDG9g?feature=share&amp;amp;t=5054&quot;&gt;https://www.youtube.com/live/gnqpdrnDG9g?feature=share&amp;amp;t=5054&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=gnqpdrnDG9g&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/ckesXE/hyTCxvpKuA/sLhPsFn0KdjiaeDmJefmI0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=114_36_1180_686&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/gnqpdrnDG9g&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Demo&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/Orf4dlkqGfo&quot;&gt;https://youtu.be/Orf4dlkqGfo&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=Orf4dlkqGfo&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/Fv5sG/hyTCMszJq2/plh8SrCJ93PkPXXiKE1oAk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/Orf4dlkqGfo&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에도 42일간 진행된 24시간 부트캠프가 종료되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 말, 어찌 보면 2023년의 시작과 함께 나의 웹 개발에 대한 커리어가 시작되고 어느덧 8개월이 넘는 시간이 흘러갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 정말 아무것도 모르는 상태로 부트캠프에 들어왔던 나는 여러 활동들을 거쳐 어느새 팀원에서 리더라는 역할을 부여받고 활동을 하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &quot;내가 리더라는 역할을 잘 해낼 수 있을까?&quot;, &quot;내 실력이 아직 그 정도는 아닌 것 같은데..&quot;라는 생각들이 지배적이었지만, 그래도 이런 기회가 주어졌다는 건 주위에서 보았을 때 내가 많이 성장했다는 점을 보았기 때문일 것이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 리더가 되면서 어떤 일들을 했을까?&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;하지만 백엔드의 경우는 경험이 부족하였으며, 사용해 본 적 없는 기술들을 사용하는 프로젝트였기에 많은 도움을 주지 못해 아쉬웠다.&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;이제 생각을 해보자, 프로젝트의 시작부터 나는 어떤 것들을 해왔는지 말이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Week 0 (23/06/25~23/07/02)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 놀랍게도 지난 부트캠프도, 이번 부트캠프로 F팀으로 참여했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;F팀의 팀원에서 리더로 참여해서 그런 것인지, 그 의미가 더욱 깊은 것 같았다.&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;결과는 성공적이었고, 팀 전원을 성장곡선 위에 올려두었다고 평가받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0주 차는 팀원의 적응을 도와주기 위한 활동들을 했던 시간이 대부분이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 팀원 파악 및 아이스브레이킹&lt;/h3&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;각 팀원들의 첫인상, 초반 대화, 평소의 표정, 말투, 분위기 등의 초기 인상을 통해 팀의 전체적인 느낌을 파악했으며, 우리 팀의 경우에는 모든 사람들이 전부 다른 느낌의 샐러드 볼 같은 그런 팀으로 느껴졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샐러드 볼 같은 경우는 잘 섞여있고, 추가적인 조리를 가하지 않았을 때 가장 최적의 결과가 나오는 상태이므로, 나는 개개인의 특성을 존중하면서도 의지를 부여하는 그런 전략을 시행해야겠다고 마음먹었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 팀 노션 페이지 사전 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;놀랍게도 1년 동안 참여했던 같은 부트캠프의 다른 팀이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측은 팀원으로 참여했던 나의 F팀이고, 우측은 리더가 된 나의 F팀이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6N85V/btsqKxewibV/PtAxJqJVYM04A7ANi46PbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6N85V/btsqKxewibV/PtAxJqJVYM04A7ANi46PbK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;770&quot; data-filename=&quot;Untitled.png&quot; style=&quot;width: 43.8608%; margin-right: 10px;&quot; data-widthpercent=&quot;44.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6N85V/btsqKxewibV/PtAxJqJVYM04A7ANi46PbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6N85V%2FbtsqKxewibV%2FPtAxJqJVYM04A7ANi46PbK%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;822&quot; height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PTNjo/btsqKErGHJQ/sXM8x3cfOyYcKXcJK0FUhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PTNjo/btsqKErGHJQ/sXM8x3cfOyYcKXcJK0FUhk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;562&quot; data-filename=&quot;Untitled 1.png&quot; style=&quot;width: 54.9764%;&quot; data-widthpercent=&quot;55.62&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PTNjo/btsqKErGHJQ/sXM8x3cfOyYcKXcJK0FUhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPTNjo%2FbtsqKErGHJQ%2FsXM8x3cfOyYcKXcJK0FUhk%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;752&quot; height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;/div&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;노베이스로 시작하는 사람들도 많을 것이고, 이 부분을 미리 생성해 두는 것만큼 팀원들에게 도움이 되는 부분이 많을 것이라고 생각했다. 실제로도 많은 도움이 되었다고 한다는 점&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 팀원들에게 제공하는 가이드라인&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8PB22/btsqP0f8ZyC/XREnGRO720jZAXb55FK6CK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8PB22/btsqP0f8ZyC/XREnGRO720jZAXb55FK6CK/img.png&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;733&quot; data-filename=&quot;Untitled 2.png&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.3993%; margin-right: 10px;&quot; data-widthpercent=&quot;47.96&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8PB22/btsqP0f8ZyC/XREnGRO720jZAXb55FK6CK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8PB22%2FbtsqP0f8ZyC%2FXREnGRO720jZAXb55FK6CK%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;637&quot; height=&quot;733&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ViYLn/btsqRGIgVBE/UAtdKwvlhK1immvCIjKpeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ViYLn/btsqRGIgVBE/UAtdKwvlhK1immvCIjKpeK/img.png&quot; data-origin-width=&quot;613&quot; data-origin-height=&quot;650&quot; data-filename=&quot;Untitled 3.png&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.4379%;&quot; data-widthpercent=&quot;52.04&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ViYLn/btsqRGIgVBE/UAtdKwvlhK1immvCIjKpeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FViYLn%2FbtsqRGIgVBE%2FUAtdKwvlhK1immvCIjKpeK%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;613&quot; height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/div&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;할 용어가 많은 점과 어느 정도의 실습을 하며 개발에 대한 감을 잡아야 하는 부분이다.&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;팀원으로 참여했을 때 느꼈던 이 부분을 그대로 리더가 되어 시행했고, 그 결과는 성공적이었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Week 1 (23/07/03~23/07/09)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 리더 one-on-one 생성, 진행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부트캠프에는 one-on-one이라는 제도가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멘토로 지정된 사람들과 특정 시간대에 1 대 1로 기술적인, 멘탈, 리더십, 팔로워십 등 여러 많은 분야에서 상담을 하며 부트캠프의 고민들을 해결해 나가는 시간이라고 볼 수 있는데,&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;여담이지만 이땐 나와 안 친해서 신청했던 거라고 했던 팀원의 말을 인용하겠다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 주제 결정 완료&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 C(ollect) Birds라는 주제가 정해졌었다.&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;결국 0주 차 발표 때 이 주제는 의미가 없다는 피드백을 받고 지금의 RememberPlus가 되었다고 한다~&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Git 협업 Flow 생성&lt;/h3&gt;
&lt;figure id=&quot;og_1691667659812&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;[GitHub] Git에서 팀 단위로 협업하기&quot; data-og-description=&quot;0. 문제 상황 파악하기 가장 먼저 해야 할 일은 현재 프로젝트에서 내가 무엇을 해야하는가에 대한 이해와 이 문제를 해결하기 위해 어떤 것을 활용하느냐 입니다. 그 부분을 잘 알고 있으시다면&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/249&quot; data-og-url=&quot;https://time-map-installer.tistory.com/249&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dF5e0V/hyTBA8lY0p/NUkY3hLQsLDxz4dD5qNCVK/img.gif?width=800&amp;amp;height=230&amp;amp;face=0_0_800_230,https://scrap.kakaocdn.net/dn/CWhtv/hyTCCjb58X/xOaKCvDiDrwKKOhl0uwKu1/img.gif?width=800&amp;amp;height=230&amp;amp;face=0_0_800_230,https://scrap.kakaocdn.net/dn/cKIoda/hyTBuG6bLi/Zn1KDrvqbcZmkFUTYUyNb1/img.png?width=1032&amp;amp;height=743&amp;amp;face=0_0_1032_743&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/249&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/249&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dF5e0V/hyTBA8lY0p/NUkY3hLQsLDxz4dD5qNCVK/img.gif?width=800&amp;amp;height=230&amp;amp;face=0_0_800_230,https://scrap.kakaocdn.net/dn/CWhtv/hyTCCjb58X/xOaKCvDiDrwKKOhl0uwKu1/img.gif?width=800&amp;amp;height=230&amp;amp;face=0_0_800_230,https://scrap.kakaocdn.net/dn/cKIoda/hyTBuG6bLi/Zn1KDrvqbcZmkFUTYUyNb1/img.png?width=1032&amp;amp;height=743&amp;amp;face=0_0_1032_743');&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;[GitHub] Git에서 팀 단위로 협업하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;0. 문제 상황 파악하기 가장 먼저 해야 할 일은 현재 프로젝트에서 내가 무엇을 해야하는가에 대한 이해와 이 문제를 해결하기 위해 어떤 것을 활용하느냐 입니다. 그 부분을 잘 알고 있으시다면&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 부트캠프를 하고, 테커에 들어와서 경험했던 프로젝트들에서 얻은 Git flow를 이번에도 적용시켜보려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 기억하고 있는 협업의 과정을 문서화하여 &lt;code&gt;이슈 - 브랜치 생성 - 작업 - Pull Request - develop에 적용&lt;/code&gt; 의 순서를 반복하도록 팀원들에게 알려주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 라벨, 이슈, PR 템플릿들도 생성하여 조금 더 직관적인 작업이 가능하도록 만들었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 처음으로 접한 팀원들에게는 다소 복잡한 과정이라 느껴질 수 있어서 약 2주 동안 익숙해질 때까지 계속해서 인내를 가지고 알려주었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Week 2 (23/07/10~23/07/16)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 프런트, 백엔드 분업 시작&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때부터는 프런트엔드, 백엔드의 분업이 시작되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나를 포함한 3명의 팀원은 프런트엔드를, 다른 3명의 팀원들을 백엔드를 맡아서 각각의 태스크를 진행하기 시작했는다.&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;다행히도 팀원들이 굉장히 잘 따라주었고, 각자 학습하고 서로 이해하기 쉽게 설명해 주는 등 적극적인 학습활동을 진행하면서 잘 나아간 것 같아서 기분이 좋았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 페이지 퍼블리싱&lt;/h3&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;어려움을 호소하면서 잘 해냈다는 건 재능을 일깨운 것이거나 엄살이 아니었을까 하는 잠깐의 고민이 있었다 ㅋㅋㅋㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시각화 특화 프로젝트다 보니 여기에 가장 많은 시간을 들였고, 그 결과는 성공적이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Three.js를 활용한 우주의 구현, 그리고 D3를 통한 관계도의 시각화까지!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 자랑 좀 해보았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Neo4j 학습&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 관계에 특화된 프로젝트이고, 관계를 RDB로 구현하기에는 너무나도 복잡하고 성능이 하락하는 경우가 있기에 Neo4j DB에 대해 학습하는 시간이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 파트 중 한 분이 여기에 큰 흥미를 느꼈고, 곧 모든 설계한 api에 대한 더미데이터와 쿼리를 직접 만들어오는 경지에 이르렀다(굉장히 놀라웠다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그걸 간접적으로 바라보면서 여러모로 많이 배워간 것 같아서 좋았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 행아웃 데이&lt;/h3&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Week 3 (23/07/17~23/07/23)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. API 관련 스펙 결정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느 정도 페이지 퍼블리싱과 백엔드의 세팅이 끝나가는 시기에 API 스펙을 팀원들과 함께 설계하고 결정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이 부분이 Neo4j를 사용하는 부분이다 보니 출력값의 형식이 달라질 수 있지 않을까 고민했지만, 다행히도 RDB와 크게 다르지 않게 json형식으로 request, response 데이터를 받아올 수 있었어서 이 부분에 대해서는 안도의 한숨을 내쉬었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 핵심 기능인 관계도 페이지가&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 서버에 더미데이터 넣기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 스펙만 완성되었다고 하면 실질적으로 서버를 켤 때마다 처음부터 모든 데이터를 다 넣고 진행을 해야 하는 일이 반복될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 그 귀찮은 작업을 방지하기 위해 백엔드 서버 쪽에 미리 데이터를 넣어두고 서버가 켜질 때 실행되면서 데이터가 들어가도록 하는 작업을 하였다. 물론 이건 추후에 배포를 진행할 때에는 관련 코드를 제거한 버전으로 배포하였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 번아웃 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공포의 3주 차, 24시간으로 진행되는 활동이다 보니 압축해서 많은 양의 학습을 자연스레 진행하는데,&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;번아웃이 올 때 그 시기를 어떻게 보내느냐에 따라 현재 하고 있는 활동의 지속 여부가 결정되곤 하는데,&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Week 4 (23/07/24~23/07/30)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. MSW를 활용한 API Mocking&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API를 백엔드에서 개발 중이었다 보니 진행상황이 다소 빠른 편이었던 프런트엔드에서도 어떤 작업을 하여야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 선택한 방법이 Mock Service Worker를 사용한 API Mocking이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전에는 Next.js에서 제공하는 API Routes를 사용하여 간편하게 했었는데, 이런 기능이 기본 제공되지 않는 라이브러리인 React의 경우에는 이렇게 외부 라이브러리를 사용해서 구현한다는 사실을 알았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mocki ng이긴 했지만, 결국 임의로 받아 온 데이터가 실제로 프런트엔드 상에서 구현이 되는 것처럼 보이는 경험은 언제 보아도 짜릿했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Mocking을 한 위치에 나중에 실제 서버와 연결을 하면서 도메인만 바꾸어 적용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인도 일괄적으로 관리하기 위한 파일을 만들어서 관리하였다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Tech Talk&lt;/h3&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=jVHm5XE1geM&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/pYeNq/hyTCD3udWc/ihqBYylfkeBTh68QHFktIk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/jVHm5XE1geM&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 팀에서 시간이 남아 팀원들끼리 조사해 온 내용을 공유하는 데에서 시작 한 이 Tech Talk은 갑작스러운 계기로 전체적인 규모로 바뀌어 참여하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀에서는 나와 백엔드 한 분이 함께 참여하셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우에는 지난번 테커톡으로 진행했던 ES6를 다시 한번 더 주제로 선정하여 발표하였고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원분의 경우에는 다소 생소한 관계형 Database인 Neo4j를 다루었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 이렇게 발표까지 경험하기에는 정말 많은 용기와 각오가 필요했을 텐데 여러모로 성장 가능성이 높은 팀원이라고 생각했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Week 5 (23/07/31~23/08/05)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 뭔가 갑자기 잘 풀리는데?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4주 차 때 무언가가 막힌 듯 잘 진행되지 않았다면 5주 차에 진입하고 나서 모여있는 모든 것들이 터져 나오듯이 하나둘씩 완성되어 가기 시작했다. 그 말은 즉슨 디자인 개선, 막혔던 기능 완성, API 연동 완료 등의 완성을 위한 모든 퍼즐조각들이 올바른 판의 위치에 들어가기 시작했다는 소리이다. 굉장히 뿌듯했고, 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;README에 올려둔 디자인은 대부분 이 시기에 수정되어 완성도가 높아졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 갑자기 잘 풀린 게 아니라 그동안 실전으로 압축된 성과가 드디어 나타났던 게 아닐까 싶다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 배포가 생각보다 쉽다??&lt;/h3&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;그리고 성공했다. 뭐지?? 생각보다 엄청 높게만 느껴졌던 벽 앞에서 점프를 하니 바로 뛰어넘어진 기분이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2 세팅에서 조금 애를 먹긴 했지만 그 이후는 무언가가 수월하게 풀려나갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인도 한 시간 정도 기다리니 바로 연결이 되었고, &lt;a href=&quot;http://remember-plus.site&quot;&gt;remember-plus.site라는&lt;/a&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;물론 지금은 닫아두었다. 나중에 리메이크하면서 제대로 배포해봐야겠다 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https도 뭔가 욕심이 났다 이것도 해봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 나중에 간단하게 정리해서 포스팅해 볼까 생각 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CI/CD를 아직 붙이지 못했다는 것이 아쉽지만 이건 조만간 프로젝트를 정리하면서 써 보면 될 것이다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 진심을 다한 발표준비&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부트캠프에서 어찌 보면 가장 중요한 퍼포먼스인 발표이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 7번 대본을 갈아엎고, PPT를 갈아엎으면서 어느 포인트를 짚어야 사람들에게 임팩트가 있을지 점점 감을 잡아나아 갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일주일 정도 발표준비를 했지만, 프로젝트가 계속 개선되고 있던 상황에서 발표는 계속 갈아엎어지고 있었지만 그래도 어째 잘 준비가 되었나 보다 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막엔 지난번에 그랬던 것처럼 밤샘을 하며 발표를 준비한 끝에 받았던 대부분의 피드백을 대본과 PPT에 적용시키는 데 성공하였고, 발표 또한 성공적으로 마무리하였다  &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 2번의 F팀, 2번의 2등, 성공적인 리더로서의 마무리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 표 차이로 1, 2, 3등이 나뉠 만큼 굉장히 치열한 평가가 이루어졌다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 3등을 발표할 때 &amp;ldquo;아, 상은 못 받아도 이번엔 많은 것을 얻어갔으니 난 괜찮아&amp;rdquo;라고 생각했는데,&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;끝나고 팀원들, 친해진 다른 팀원분들과 모여 축하한다는 말을 했고, 다들 행복해했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 우리 팀에 우수 장학생으로 선발된 팀원이 있어서 많은 축하의 말을 건네었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Week + (23/08/06~)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적으로 이제 프로젝트 경험, 리더 경험을 쌓은 나는 앞으로 하고 싶은, 해야 하는 일들을 아래와 같이 정리했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;지금의 Remember Plus 고도화, 프로젝트 노션 총 정리 후 포트폴리오로 변환하기&lt;/li&gt;
&lt;li&gt;기존하고 있던 프로젝트 고도화, 졸업작품 진행하기&lt;/li&gt;
&lt;li&gt;코딩테스트 준비하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번은 가장 우선적으로 해야 할 것 같다고 생각을 했다. 꽤 고퀄리티이고, 깊게 파고들어 가기에도 좋은 프로젝트기에 이번 Remember Plus를 정리하여 고퀄리티의 포트폴리오로 만들어보고 싶다는 생각을 하기 시작했다.&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;아마 이번에 코드리뷰에 대한 글을 쓰지 않을까 싶다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 코딩 테스트 공부를 다시, 더 본격적으로 시작하여 더 높은 목표를 바라볼 수 있도록 하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;42일의 길고도 짧은 시간 동안의 엄청난 일들이 나를 더욱 성장시켰다.&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(23/08/09)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 이후 이 글을 작성하는 도중 나는 2023-2 Techeer Partners 멘토에 선발되어 활동을 시작할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느새 멘토의 자격을 갖추게 되었다는 사실에 스스로 놀랐고, 앞으로도 내가 만족할 수 있는 단계까지 끌어올리기 위해 멈추지 않을 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/Bootcamp</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/250</guid>
      <comments>https://time-map-installer.tistory.com/250#entry250comment</comments>
      <pubDate>Thu, 10 Aug 2023 20:38:23 +0900</pubDate>
    </item>
    <item>
      <title>[GitHub] Git에서 팀 단위로 협업하기</title>
      <link>https://time-map-installer.tistory.com/249</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;0. 문제 상황 파악하기&lt;/h2&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;b&gt;어느정도 초반 세팅이 되어있다면 프로젝트에서는 2~5번을 반복합니다.&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;우선 개인 컴퓨터가 준비가 되셨고, 팀 organization이 있다면 그 안에 프로젝트 레포지토리로 들어가줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원과 함께 프로젝트를 함께 세팅을 했을 것이고, 그 프로젝트 파일이 당신의 컴퓨터에 존재하지 않는다면 이 과정을 거치도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 프로젝트가 진행중인 frontend를 기준으로 설명드리겠습니다.&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM7yJo/btsnvpYn6vy/ks0NSDXWA1UbAj0dvKxoa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM7yJo/btsnvpYn6vy/ks0NSDXWA1UbAj0dvKxoa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM7yJo/btsnvpYn6vy/ks0NSDXWA1UbAj0dvKxoa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM7yJo%2FbtsnvpYn6vy%2Fks0NSDXWA1UbAj0dvKxoa1%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;1026&quot; height=&quot;379&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt; &amp;gt; Code 를 누르고 나오는 링크를 복사해서 클론을 받습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MXqL4/btsnvwwA1Yi/2V8gLVjMaLcwavlts6M921/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MXqL4/btsnvwwA1Yi/2V8gLVjMaLcwavlts6M921/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MXqL4/btsnvwwA1Yi/2V8gLVjMaLcwavlts6M921/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMXqL4%2FbtsnvwwA1Yi%2F2V8gLVjMaLcwavlts6M921%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;672&quot; height=&quot;459&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;459&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;2. 이슈 생성하기&lt;/h2&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;b&gt;여기서 New Issue를 클릭합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1027&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A5LoU/btsnvJoVRKw/P5hRKKQoGR3Tm719nxCMak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A5LoU/btsnvJoVRKw/P5hRKKQoGR3Tm719nxCMak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A5LoU/btsnvJoVRKw/P5hRKKQoGR3Tm719nxCMak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA5LoU%2FbtsnvJoVRKw%2FP5hRKKQoGR3Tm719nxCMak%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;1027&quot; height=&quot;499&quot; data-origin-width=&quot;1027&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서 작업의 종류에 따른 이슈 템플릿을 선택합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1035&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S9O8M/btsnygS7bn0/hQ7OJjzLk3KdrneoummadK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S9O8M/btsnygS7bn0/hQ7OJjzLk3KdrneoummadK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S9O8M/btsnygS7bn0/hQ7OJjzLk3KdrneoummadK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS9O8M%2FbtsnygS7bn0%2FhQ7OJjzLk3KdrneoummadK%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;1035&quot; height=&quot;309&quot; data-origin-width=&quot;1035&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제목과 내용을 입력하고 우측의 항목들을 설정합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;517&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cutl2a/btsnvJCrxo3/MOfIgHSq4a7tUBMXYpIUy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cutl2a/btsnvJCrxo3/MOfIgHSq4a7tUBMXYpIUy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cutl2a/btsnvJCrxo3/MOfIgHSq4a7tUBMXYpIUy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcutl2a%2FbtsnvJCrxo3%2FMOfIgHSq4a7tUBMXYpIUy1%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;1017&quot; height=&quot;517&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;517&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Assignees는 작업하는 사람을 선택합니다 (여기서는 본인이 갈 것이므로 본인을 선택합니다)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Labels는 이 작업을 나타내는 태그를 선택합니다 (여기서는 기능 구현만을 할 것이므로 feature만 선택합니다)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Projects는  Organization에서 생성해 둔 Project를 선택합니다. (저희 프로젝트의 이름인 Rememeber Plus를 선택합니다)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MileStone에는 스프린트 단위로 쪼개놓은, 미리 생성해둔 마일스톤을 선택합니다. (2주차이므로 2주차 프로젝트를 선택합니다)&lt;/b&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;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;284&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXCefy/btsnygMlMqB/QFpa4SZAA30I0Plk5G2tzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXCefy/btsnygMlMqB/QFpa4SZAA30I0Plk5G2tzK/img.png&quot; data-alt=&quot;그렇게 이런 느낌으로 등록을 하고 이슈를 생성합니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXCefy/btsnygMlMqB/QFpa4SZAA30I0Plk5G2tzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXCefy%2FbtsnygMlMqB%2FQFpa4SZAA30I0Plk5G2tzK%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;284&quot; height=&quot;348&quot; data-origin-width=&quot;284&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그렇게 이런 느낌으로 등록을 하고 이슈를 생성합니다&lt;/figcaption&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;661&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctJTf7/btsnwoxCru5/703xrDzyDTNINhYkAkqgf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctJTf7/btsnwoxCru5/703xrDzyDTNINhYkAkqgf0/img.png&quot; data-alt=&quot;선택한 Projects에서는 이렇게 진행중인 프로세스를 보실 수 있습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctJTf7/btsnwoxCru5/703xrDzyDTNINhYkAkqgf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctJTf7%2FbtsnwoxCru5%2F703xrDzyDTNINhYkAkqgf0%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;1280&quot; height=&quot;661&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;661&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;선택한 Projects에서는 이렇게 진행중인 프로세스를 보실 수 있습니다.&lt;/figcaption&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;3. 브랜치 생성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이슈가 생성되면 그 다음에는 브랜치를 만들어서 작업을 하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전에 정한 branch naming convention에 따라 이름을 정하고 브랜치를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 경우에는 기능종류/간단한-기능설명-이슈번호[ex) feat/login-page-123]로 생성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 각자의 브랜치에 체크아웃 한 후 작업을 한 후 commit을 하여 원격 브랜치에 작업내역을 저장합니다.&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;4. Pull Request 요청하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 작업이 끝나면 PR을 요청할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR의 경우에는 아래의 화면에서 오른쪽 초록색 버튼 &lt;b&gt;New pull request&lt;/b&gt;를 눌러 생성해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbJOM7/btsnDkhnLOy/oYwvAKtgkzlFW3Vvth2On0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbJOM7/btsnDkhnLOy/oYwvAKtgkzlFW3Vvth2On0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbJOM7/btsnDkhnLOy/oYwvAKtgkzlFW3Vvth2On0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbJOM7%2FbtsnDkhnLOy%2FoYwvAKtgkzlFW3Vvth2On0%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;1028&quot; height=&quot;431&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;pull request를 누르면 생성되어 있는 브랜치가 하단에 보여집니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 그 브랜치를 눌러서 base 브랜치에 병합 할 pull request를 작성하시거나&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 상단 compare 부분에서 작업하신 브랜치를 선택하고 Create pull request를 누릅니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chTBBU/btsnxVXAvr6/QQhmohJA4renPKKiL4Xlg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chTBBU/btsnxVXAvr6/QQhmohJA4renPKKiL4Xlg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chTBBU/btsnxVXAvr6/QQhmohJA4renPKKiL4Xlg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchTBBU%2FbtsnxVXAvr6%2FQQhmohJA4renPKKiL4Xlg1%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;1036&quot; height=&quot;642&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그렇게 브랜치를 누르실 경우 다음 화면과 같이 커밋 내역과 변경사항들이 보여지게 됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;743&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AUv4Z/btsnDxANPNY/hzpV6GDKXF9Ouw703QQyN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AUv4Z/btsnDxANPNY/hzpV6GDKXF9Ouw703QQyN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AUv4Z/btsnDxANPNY/hzpV6GDKXF9Ouw703QQyN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAUv4Z%2FbtsnDxANPNY%2FhzpV6GDKXF9Ouw703QQyN0%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;1032&quot; height=&quot;743&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;743&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;1023&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3SlGe/btsnDnSLwy9/zPWXXAqA3t6LONLxbcdVS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3SlGe/btsnDnSLwy9/zPWXXAqA3t6LONLxbcdVS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3SlGe/btsnDnSLwy9/zPWXXAqA3t6LONLxbcdVS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3SlGe%2FbtsnDnSLwy9%2FzPWXXAqA3t6LONLxbcdVS1%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;1023&quot; height=&quot;553&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그렇게 미리 제작된 PR 템플릿이 존재한다면 아래와 같이 나타날 것입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVi2lT/btsnDMj89Md/DGkKkzXjFgFMawKMgYM8y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVi2lT/btsnDMj89Md/DGkKkzXjFgFMawKMgYM8y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVi2lT/btsnDMj89Md/DGkKkzXjFgFMawKMgYM8y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVi2lT%2FbtsnDMj89Md%2FDGkKkzXjFgFMawKMgYM8y1%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;1016&quot; height=&quot;754&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;754&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&lt;b&gt;Reviewers는 리뷰를 해주는 사람을 선택합니다. (여기서는 본인이 직접 리뷰어가 되지는 않습니다)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Assignees는 작업 한 사람을 선택합니다 (여기서는 본인이 갈 것이므로 본인을 선택합니다)&lt;br /&gt;Labels는 이 작업을 나타내는 태그를 선택합니다 (여기서는 디자인 작업을 하였으므로  design을 선택합니다)&lt;br /&gt;Projects는  Organization에서 생성해 둔 Project를 선택합니다. (저희 프로젝트의 이름인 Rememeber Plus를 선택합니다)&lt;br /&gt;MileStone에는 이슈와 브랜치가 연결되어있으므로&amp;nbsp;&amp;nbsp;자동으로 선택됩니다.&lt;br /&gt;&lt;br /&gt;그렇게&amp;nbsp;생성을&amp;nbsp;한&amp;nbsp;후&amp;nbsp;Development에&amp;nbsp;이슈가&amp;nbsp;등록되어있는&amp;nbsp;것을&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;319&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by0fib/btsnD6Jsrgj/KlkCBnyAAVoHX6wKjTnEb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by0fib/btsnD6Jsrgj/KlkCBnyAAVoHX6wKjTnEb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by0fib/btsnD6Jsrgj/KlkCBnyAAVoHX6wKjTnEb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby0fib%2FbtsnD6Jsrgj%2FKlkCBnyAAVoHX6wKjTnEb0%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;319&quot; height=&quot;353&quot; data-origin-width=&quot;319&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 연결된 이슈, 브랜치, Pull request는 merge가 완료되면서 동시에 닫히며 적용됩니다.&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;5. 코드리뷰 하기&lt;/h2&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;기본적인 설정에서는 스스로가 병합(Merge)를 할 수 있는 상태이겠지만 스스로 병합을 하는 것은 좋지 않은 습관입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;b&gt;Branch Protection Rules&amp;nbsp;&lt;/b&gt;를 적용하여 브랜치가 무분별하게 병합되는 것을 방지합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhuu1J/btsnD1VKRI9/aOJE4pYjOkSRhK9cfx1T1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhuu1J/btsnD1VKRI9/aOJE4pYjOkSRhK9cfx1T1K/img.png&quot; data-alt=&quot;더 나은 설정이 있을 수도 있습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhuu1J/btsnD1VKRI9/aOJE4pYjOkSRhK9cfx1T1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhuu1J%2FbtsnD1VKRI9%2FaOJE4pYjOkSRhK9cfx1T1K%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;732&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;732&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;여기서는 1명 이상의 리뷰어가 Approve를 해야하고, 모든 리뷰가 해결되어야만 merge 가능하도록 설정하였습니다.&lt;/p&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;코드리뷰는 Files changed 항목에 가서 진행됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6zjmX/btsnygHe6Hn/IwPqyWAguKMw83404L27C0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6zjmX/btsnygHe6Hn/IwPqyWAguKMw83404L27C0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6zjmX/btsnygHe6Hn/IwPqyWAguKMw83404L27C0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6zjmX%2FbtsnygHe6Hn%2FIwPqyWAguKMw83404L27C0%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;1003&quot; height=&quot;676&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 파일들의 viewed를 눌러 봤다고 체크할 수도 있습니다.&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Jul-14-2023 14-58-48.gif&quot; data-origin-width=&quot;1248&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ci4zA/btsnDNKeRzL/sJnHLHbUgbwiKKRTwPSpq1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ci4zA/btsnDNKeRzL/sJnHLHbUgbwiKKRTwPSpq1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ci4zA/btsnDNKeRzL/sJnHLHbUgbwiKKRTwPSpq1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Ci4zA/btsnDNKeRzL/sJnHLHbUgbwiKKRTwPSpq1/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;1248&quot; height=&quot;360&quot; data-filename=&quot;Jul-14-2023 14-58-48.gif&quot; data-origin-width=&quot;1248&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 리뷰 또는 comment를 남기고 나면 본인이 남긴 글 옆에 &lt;b&gt;pending&amp;nbsp;&lt;/b&gt;이라고 나타나 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 pending 상태를 해결하기 위해 Review changes를 눌러 commit이나 approve를 선택하시고 submit review를 눌러줍니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Git을 활용하여 협업을 하는 과정을 정리해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적인 설명이 필요한 경우 추후 업데이트를 통해 보완해두도록 하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>(3D)Dev Deep Dive/Templates &amp;amp; Guides</category>
      <category>GIT</category>
      <category>issue</category>
      <category>Label</category>
      <category>milestone</category>
      <category>pending</category>
      <category>PROJECT</category>
      <category>Pull Request</category>
      <category>Review</category>
      <category>template</category>
      <category>협업</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/249</guid>
      <comments>https://time-map-installer.tistory.com/249#entry249comment</comments>
      <pubDate>Fri, 14 Jul 2023 15:02:25 +0900</pubDate>
    </item>
    <item>
      <title>[TailwindCSS] 디자인 적용이 잘 안된 것 같다면??</title>
      <link>https://time-map-installer.tistory.com/248</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;TailwindCSS를 사용하기 위해 프로젝트에서 작업을 하다가 무언가가 이상하게 디자인이 적용이 안되어서 원인을 파악해보아야겠다고 생각했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 글은 어떤 원인들이 있었고 어떻게 해결했는 지 기록해 둔 글입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 스펙&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Vite, React, TypeScript&lt;/li&gt;
&lt;li&gt;TailwindCSS&lt;/li&gt;
&lt;li&gt;yarn&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인은 여러가지가 있던 것 같았습니다. 나열 해보자면 아래와 같았습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;패키지 설치 시 tailwindcss만 설치한 점&lt;/li&gt;
&lt;li&gt;typescript 프로젝트로 마이그레이션 하면서 tailwind.config.js를 .cjs로 변경한 것&lt;/li&gt;
&lt;li&gt;tailwindcss를 어느 형식의 파일에 적용시킬 것인 지 명시하지 않은 것&lt;/li&gt;
&lt;li&gt;index.css에 import문을 통해 tailwind를 선언한 것&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dH7tvw/btsnD5joCrL/keRyUU7MIwualQYNPXb8gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dH7tvw/btsnD5joCrL/keRyUU7MIwualQYNPXb8gk/img.png&quot; data-alt=&quot;그런가봐요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dH7tvw/btsnD5joCrL/keRyUU7MIwualQYNPXb8gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdH7tvw%2FbtsnD5joCrL%2FkeRyUU7MIwualQYNPXb8gk%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;225&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그런가봐요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결법&lt;/h2&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;u&gt;&lt;b&gt;1. TailwindCSS 최신 버전으로 업데이트, PostCSS, Autoprefixer 설치&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1689312261709&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn add tailwindcss@latest postcss@latest autoprefixer@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@latest&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;u&gt;&lt;b&gt;2. 기본 설정 파일을 삭제하고 tailwind.config.js, postcss.config.js의 보일러 플레이트 코드 파일 생성&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1689312390140&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx tailwindcss init -p&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 설정 파일을 .cjs로 변경하여 생긴 문제라 생각하고 새로 보일러플레이트를 생성한 후 기존에 설정 해두었던 파일을 옮겼습니다.&lt;/p&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;u&gt;&lt;b&gt;3. tailwind.config.js에 구문을 추가하여 모든 프로젝트의 파일에서 TailwindCSS가 적용될 수 있도록 하기&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1689312521821&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;content: [&quot;./index.html&quot;, &quot;./src/**/*.{js,ts,jsx,tsx}&quot;],&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;설정을 아무리 잘 해두었어도 어디서 사용할 지 지정하지 않았다면 당연하게도 디자인이 적용되지 않을 것입니다.&lt;/p&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;u&gt;&lt;b&gt;4. index.css에 선언된 @import문들을 @tailwind로 변경하기&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1689312626809&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이전 코드
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

// 변경 코드
@tailwind base;
@tailwind components;
@tailwind utilities;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vite 프로젝트이므로 index.css에 다음과 같은 작업을 진행하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이렇게 작업을 해두어도 아래와 같이 노란 줄이 뜨기는 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;209&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFKoCz/btsnzGyIfQw/t1FGeOngMQBxYXr8dkW8Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFKoCz/btsnzGyIfQw/t1FGeOngMQBxYXr8dkW8Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFKoCz/btsnzGyIfQw/t1FGeOngMQBxYXr8dkW8Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFKoCz%2FbtsnzGyIfQw%2Ft1FGeOngMQBxYXr8dkW8Bk%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;209&quot; height=&quot;73&quot; data-origin-width=&quot;209&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인을 찾아보니 TailwindCSS, PostCSS, Autoprefixer과 호환되는 Node의 버전이 달라서 생기는 문제라고 합니다.&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;figure id=&quot;og_1689312643576&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;Install Tailwind CSS with Vite - Tailwind CSS&quot; data-og-description=&quot;Setting up Tailwind CSS in a Vite project.&quot; data-og-host=&quot;tailwindcss.com&quot; data-og-source-url=&quot;https://tailwindcss.com/docs/guides/vite&quot; data-og-url=&quot;https://tailwindcss.com/docs/guides/vite&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://tailwindcss.com/docs/guides/vite&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tailwindcss.com/docs/guides/vite&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;Install Tailwind CSS with Vite - Tailwind CSS&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Setting up Tailwind CSS in a Vite project.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tailwindcss.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;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;97&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PoFtF/btsnC6Rccwn/2skKskYKioNHfXi0pv07A0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PoFtF/btsnC6Rccwn/2skKskYKioNHfXi0pv07A0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PoFtF/btsnC6Rccwn/2skKskYKioNHfXi0pv07A0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPoFtF%2FbtsnC6Rccwn%2F2skKskYKioNHfXi0pv07A0%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;97&quot; height=&quot;106&quot; data-origin-width=&quot;97&quot; data-origin-height=&quot;106&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;480&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JrFqR/btsnzFGA4nW/bKdOU2bTh0Wx5kNIdywC11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JrFqR/btsnzFGA4nW/bKdOU2bTh0Wx5kNIdywC11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JrFqR/btsnzFGA4nW/bKdOU2bTh0Wx5kNIdywC11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJrFqR%2FbtsnzFGA4nW%2FbKdOU2bTh0Wx5kNIdywC11%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;480&quot; height=&quot;120&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 적용되는 모습을 보입니다!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>(3D)Dev Deep Dive/TroubleShootings</category>
      <category>AutopreFixer</category>
      <category>postcss</category>
      <category>react</category>
      <category>tailwindcss</category>
      <category>TypeScript</category>
      <category>ViTE</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/248</guid>
      <comments>https://time-map-installer.tistory.com/248#entry248comment</comments>
      <pubDate>Fri, 14 Jul 2023 14:37:22 +0900</pubDate>
    </item>
    <item>
      <title>[Remember Plus 2주차] D3.js를 사용하여 2D 시각화 해보기</title>
      <link>https://time-map-installer.tistory.com/247</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 Repository&lt;/h2&gt;
&lt;figure id=&quot;og_1689236810749&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - SV-Summer-BootCamp-Team-F/frontend&quot; data-og-description=&quot;Contribute to SV-Summer-BootCamp-Team-F/frontend development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/SV-Summer-BootCamp-Team-F/frontend&quot; data-og-url=&quot;https://github.com/SV-Summer-BootCamp-Team-F/frontend&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bPhyRc/hyTjSTVP7u/3F72L4V3dIcJ1SpKIzFEHK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/SV-Summer-BootCamp-Team-F/frontend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/SV-Summer-BootCamp-Team-F/frontend&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bPhyRc/hyTjSTVP7u/3F72L4V3dIcJ1SpKIzFEHK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - SV-Summer-BootCamp-Team-F/frontend&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to SV-Summer-BootCamp-Team-F/frontend development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 D3를 사용하는 페이지를 하나 맡았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래에 있는 사이트처럼 마우스를 통해 넓은 화면을 이동할 수 있도록 하며 확대, 축소 기능까지 넣는 것을 목표로 한다.&lt;/p&gt;
&lt;figure id=&quot;og_1689126799060&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;React D3 Tree&quot; data-og-description=&quot;&quot; data-og-host=&quot;bkrem.github.io&quot; data-og-source-url=&quot;https://bkrem.github.io/react-d3-tree/&quot; data-og-url=&quot;https://bkrem.github.io/react-d3-tree/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://bkrem.github.io/react-d3-tree/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bkrem.github.io/react-d3-tree/&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;React D3 Tree&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bkrem.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 스펙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;React, Vite, TailwindCSS, TypeScript&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Issue 1. D3.js 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 2D 시각화에 특화되어있는 D3를 설치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, 주의해야 할 점이 하나가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 typescript를 사용하는 경우 패키지 설치 방법이 약간 달라진다는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1689135506409&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn add d3

아래의 패키지 또한 설치하여 타입스크립트에 맞는 d3를 설치한다.

yarn add types/d3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Issue 2. function =&amp;gt; const로 바꿀 때 형식 주의하기&lt;/h2&gt;
&lt;pre id=&quot;code_1689145301484&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존에 에러가 났던 이유 = 화살표 함수의 형식을 지키지 않았다
const foo = ()&amp;lt;type&amp;gt;{}

// 해결
const foo = ()&amp;lt;type&amp;gt; =&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;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;추가된 파일들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 전체적으로 추가되고 모듈화가 된 상태의 폴더구조는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1689227529351&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;src
 ┣ components
 ┃ ┣ relation
 ┃ ┃ ┣ Chart.tsx
 ┃ ┃ ┣ ChartContent.tsx
 ┃ ┃ ┣ RelationGraph.tsx
 ┃ ┃ ┗ ZoomableSVG.tsx
 ┣ hooks
 ┣ pages
 ┃ ┣ Relation
 ┃ ┃ ┗ RelationPage.tsx
 ┣ types
 ┃ ┗ types.ts
 ┣ utils
 ┃ ┗ generateDataPoints.ts
 ┣ App.tsx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.tsx에 라우팅을 설정(Router V6 적용)&lt;br /&gt;components/relation 페이지에 관계를 나타내기 위한 기능을 담은 컴포넌트를 분할 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pages에 실제로 relation page를 나타내기 위한 베이스 페이지 파일 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;utils에 return 값이 html이 아닌 함수를 모듈화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;types에 타입들을 모아두기 위해 types 파일 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;Chart.tsx&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 차트 생성을 담당하는 Chart 구성 요소 포함&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;ZoomableSVG 및 ChartContent 구성 요소 사용&lt;/p&gt;
&lt;pre id=&quot;code_1689228315225&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState, useEffect } from &quot;react&quot;;
import { ChartPropsType, DataPointType } from &quot;../../types/types&quot;;
import generateDataPoints from &quot;../../utils/generateDataPoints&quot;;
import ChartContent from &quot;./ChartContent&quot;;
import ZoomableSVG from &quot;./ZoomableSVG&quot;;

const Chart: React.FC&amp;lt;ChartPropsType&amp;gt; = ({ data, n, maxR }) =&amp;gt; {
  // 차트의 크기를 위한 상태 설정 (창의 넓이와 높이)
  const [dimensions, setDimensions] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });
  
  // 로컬 데이터를 위한 상태 설정
  const [localData, setLocalData] = useState&amp;lt;DataPointType[]&amp;gt;(data);

  // 데이터를 업데이트하는 함수
  const updateData = () =&amp;gt; {
    setLocalData(generateDataPoints(n, maxR));
  };

  useEffect(() =&amp;gt; {
    // 창 크기가 변경될 때 차트의 크기를 업데이트하는 함수
    const handleResize = () =&amp;gt; {
      setDimensions({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    };

    // 창 크기가 변경될 때마다 이벤트 리스너 호출
    window.addEventListener(&quot;resize&quot;, handleResize);

    // 컴포넌트가 언마운트될 때 이벤트 리스너 제거 (메모리 누수 방지)
    return () =&amp;gt; window.removeEventListener(&quot;resize&quot;, handleResize);
  }, []);  // 빈 의존성 배열을 사용하여 마운트시에만 이벤트 리스너를 설정

  return (
    // 차트를 그리는 SVG와 그 내용을 ZoomableSVG 컴포넌트와 ChartContent 컴포넌트로 분리
    &amp;lt;ZoomableSVG width={dimensions.width} height={dimensions.height} updateData={updateData}&amp;gt;
      &amp;lt;ChartContent width={dimensions.width} height={dimensions.height} data={localData} /&amp;gt;
    &amp;lt;/ZoomableSVG&amp;gt;
  );
};

export default Chart;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;ChartContent.tsx&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제공된 데이터를 기반으로 차트의 시각적 요소를 생성&lt;/p&gt;
&lt;pre id=&quot;code_1689228874215&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ChartContentPropsType } from &quot;../../types/types&quot;;

// ChartContent는 width, height, data를 props로 받아 차트의 내용을 그려주는 컴포넌트입니다.
const ChartContent : React.FC&amp;lt;ChartContentPropsType&amp;gt; = ({ width, height, data }) =&amp;gt; { 
  // translate를 통해 차트의 중심을 SVG의 중심으로 이동합니다.
  return (
    &amp;lt;g transform={`translate(${width/2}, ${height/2})`}&amp;gt;
      {/* 
        data 배열을 맵핑하여 각 데이터 포인트를 원으로 표현합니다.
        각 원의 중심점은 (x, y)이며, 반지름은 5, 색깔은 skyblue입니다.
      */}
      { data.map(({ x, y }, i) =&amp;gt; (
        &amp;lt;g key={i}&amp;gt;
          &amp;lt;circle cx={x}cy={y}r={5}fill=&quot;skyblue&quot;/&amp;gt;
        &amp;lt;/g&amp;gt;
      ))}
    &amp;lt;/g&amp;gt;
  );
};

export default ChartContent;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;RelationGraph.tsx&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시각화를 위한 기본 상위 구성 요소 역할을 하는 RelationGraph 구성 요소가 포함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임의의 데이터를 초기화하고 차트 컴포넌트를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1689229461227&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState } from &quot;react&quot;;
import { DataPointType } from &quot;../../types/types&quot;;
import generateDataPoints from &quot;../../utils/generateDataPoints&quot;;
import Chart from &quot;./Chart&quot;;

// RelationGraph는 상호작용 가능한 차트를 생성하고 보여주는 컴포넌트입니다.
const RelationGraph: React.FC = () =&amp;gt; {
  const n = 100; // 데이터 포인트의 수를 정의합니다.
  const maxR = 100; // 데이터 포인트의 최대 반지름을 정의합니다.

  // 초기 데이터를 생성하고, 이를 state로 관리합니다.
  // generateDataPoints는 n개의 무작위 데이터 포인트를 생성하는 함수입니다.
  const [data, setData] = useState&amp;lt;DataPointType[]&amp;gt;(generateDataPoints(n, maxR));

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div className=&quot;flex items-center justify-center min-h-screen min-w-full border-black&quot;&amp;gt;
        {/* Chart 컴포넌트는 생성된 데이터를 사용하여 차트를 그리고 상호작용을 처리합니다. */}
        &amp;lt;Chart data={data} n={n} maxR={maxR} /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default RelationGraph;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;ZoomableSVG.tsx&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확대/축소 및 이동 기능이 있는 차트용 SVG 컨테이너를 렌더링&lt;/p&gt;
&lt;pre id=&quot;code_1689229597298&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { zoom, D3ZoomEvent, select } from &quot;d3&quot;;
import { useRef, useState, useEffect } from &quot;react&quot;;
import { ZoomableSVGPropsType } from &quot;../../types/types&quot;;

// ZoomableSVG는 상호작용 가능한 SVG 컴포넌트입니다. 이 컴포넌트는 확대/축소 기능을 제공하며, 
// 데이터가 변경되면 확대/축소 상태를 초기화하는 버튼을 포함하고 있습니다.
const ZoomableSVG: React.FC&amp;lt;ZoomableSVGPropsType&amp;gt; = ({ children, width, height, updateData }) =&amp;gt; {
  // svgRef는 SVG 요소를 참조하는 데 사용되는 참조입니다.
  const svgRef = useRef&amp;lt;SVGSVGElement | null&amp;gt;(null);
  
  // k, x, y는 SVG 요소의 확대/축소 및 이동을 위한 상태 변수입니다.
  const [k, setK] = useState&amp;lt;number&amp;gt;(1); // 확대/축소 비율
  const [x, setX] = useState&amp;lt;number&amp;gt;(0); // x 위치
  const [y, setY] = useState&amp;lt;number&amp;gt;(0); // y 위치

  // resetZoom 함수는 확대/축소 상태를 초기화합니다.
  const resetZoom = () =&amp;gt; {
    setK(1);
    setX(0);
    setY(0);
  };

  useEffect(() =&amp;gt; {
    // zoomHandler는 확대/축소 이벤트를 처리하는 핸들러입니다.
    const zoomHandler = zoom&amp;lt;SVGSVGElement, unknown&amp;gt;().on(
      &quot;zoom&quot;,
      (event: D3ZoomEvent&amp;lt;SVGSVGElement, unknown&amp;gt;) =&amp;gt; {
        // 이벤트에서 변형 정보를 가져와 상태를 업데이트합니다.
        const { x, y, k } = event.transform;
        setK(k);
        setX(x);
        setY(y);
      }
    );

    // SVG 요소가 준비되면 zoom 핸들러를 이에 적용합니다.
    if (svgRef.current) {
      select(svgRef.current).call(zoomHandler);
    }
  }, []);

  return (
    &amp;lt;svg ref={svgRef} width={width} height={height}&amp;gt;
      {/* 
        transform 속성을 이용해, 확대/축소와 이동을 적용합니다. 
        이 때, children은 확대/축소와 이동이 적용되는 SVG 요소들을 나타냅니다.
      */}
      &amp;lt;g transform={`translate(${x}, ${y}) scale(${k})`}&amp;gt;{children}&amp;lt;/g&amp;gt;
      {/* 
        foreignObject 요소는 SVG 내에서 HTML 콘텐츠를 포함할 수 있도록 합니다. 
        여기서는 'Update Data' 버튼을 추가하였습니다.
      */}
      &amp;lt;foreignObject x={10} y={10} width={200} height={200}&amp;gt;
        &amp;lt;button
          onClick={() =&amp;gt; {
            updateData(); // 버튼 클릭 시 데이터를 업데이트하고
            resetZoom(); // 확대/축소 상태를 초기화합니다.
          }}
        &amp;gt;
          Update Data
        &amp;lt;/button&amp;gt;
      &amp;lt;/foreignObject&amp;gt;
      {/* 텍스트 요소를 SVG에 추가합니다. */}
      &amp;lt;text x=&quot;10&quot; y=&quot;10&quot; fill=&quot;black&quot;&amp;gt;
        Hello World
      &amp;lt;/text&amp;gt;
    &amp;lt;/svg&amp;gt;
  );
};

export default ZoomableSVG;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;RelationPage.tsx&lt;/b&gt;&lt;/u&gt;&lt;/h4&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;pre id=&quot;code_1689229629872&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import RelationGraph from &quot;../../components/relation/RelationGraph&quot;;

const RelationPage = () =&amp;gt; {
  return (
    &amp;lt;div className=&quot;flex items-center justify-center w-screen h-screen&quot;&amp;gt;
      &amp;lt;RelationGraph /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default RelationPage;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;types.ts&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 사용되는 TypeScript  type들의 모음&lt;/p&gt;
&lt;pre id=&quot;code_1689229715747&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 각 데이터 포인트를 나타내는 타입입니다. x와 y 좌표를 가집니다.
export type DataPointType = {
  x: number;
  y: number;
};

// ZoomableSVG 컴포넌트의 속성 타입입니다. SVG의 가로, 세로 크기,
// 업데이트 함수, 그리고 자식 요소들을 props로 받습니다.
export type ZoomableSVGPropsType = {
  children: React.ReactNode; // SVG 내부에 렌더링 될 요소들
  width: number; // SVG의 가로 크기
  height: number; // SVG의 세로 크기
  updateData: () =&amp;gt; void; // 데이터를 업데이트하는 함수
};

// ChartContent 컴포넌트의 속성 타입입니다. Chart의 가로, 세로 크기,
// 그리고 데이터를 props로 받습니다.
export type ChartContentPropsType = {
  width: number; // Chart의 가로 크기
  height: number; // Chart의 세로 크기
  data: DataPointType[]; // 데이터 포인트들의 배열
};

// Chart 컴포넌트의 속성 타입입니다. 데이터, n(생성할 데이터 포인트의 수),
// maxR(데이터 포인트 생성에 사용되는 최대 반지름)를 props로 받습니다.
export type ChartPropsType = {
  data: DataPointType[]; // 데이터 포인트들의 배열
  n: number; // 생성할 데이터 포인트의 수
  maxR: number; // 데이터 포인트 생성에 사용되는 최대 반지름
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;generateDataPoints.ts&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시각화에 사용할 임의 데이터 포인트 집합을 생성하는 유틸리티 함수&lt;/p&gt;
&lt;pre id=&quot;code_1689230060431&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// types/types에서 DataPointType을 가져옵니다.
import { DataPointType } from &quot;../types/types&quot;;

// generateDataPoints는 임의의 데이터 포인트를 생성하는 함수입니다.
const generateDataPoints = ( n : number , maxR : number ): DataPointType[] =&amp;gt; {
  // 데이터 포인트를 저장할 빈 배열을 생성합니다.
  const dataPoints: DataPointType[] = [];
  
  // n 개의 데이터 포인트를 생성합니다.
  for (let i = 0; i &amp;lt; n; i++) {
    // 반지름 r은 0부터 maxR 사이의 임의의 값입니다.
    const r = Math.random() * maxR;
    // 각도 t는 0부터 2&amp;pi; 사이의 임의의 값입니다.
    const t = Math.random() * 2 * Math.PI;
    
    // (r cos(t), r sin(t)) 형태의 임의의 점을 생성하여 배열에 추가합니다.
    // 이는 극 좌표 (r, t)를 직교 좌표 (x, y)로 변환하는 과정입니다.
    dataPoints.push({
      x: r * Math.cos(t),
      y: r * Math.sin(t),
    });
  }

  // 생성된 데이터 포인트 배열을 반환합니다.
  return dataPoints;
};

// generateDataPoints 함수를 내보냅니다.
// 이를 통해 다른 파일에서 이 함수를 import하여 사용할 수 있습니다.
export default generateDataPoints;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;.ts와 .tsx의 차이는 무엇일까?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 둘의 이름에 따른 기능적인 차이는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, return 값에 html 형식이 포함되어있다면 .tsx를, 포함되어있지 않다면 .ts로 나누어&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;utils랑 hooks의 차이점은 무엇일까?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 말해보자면 react의 함수인 useState, useRef, useEffect 등이 들어간 함수면 hooks로 분류되고, 그게 아닌 순수 자바스크립트(여기서는 TypeScript)로 이루어진 로직이면 utils에 포함된다.&lt;/p&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;figure id=&quot;og_1689228064469&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;[Folder Structure] Hooks? Utils? 차이점이 뭘까?&quot; data-og-description=&quot;프로젝트 개발을 하던 중에 함수를 따로 모듈화 하던 중 갑자기 궁금해 진 것이 있었습니다. &amp;quot;이렇게 특정 기능을 재사용하거나 관리하기 쉽게 따로 모듈화를 하는 건 좋은데, 어느 폴더에 넣어&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/196&quot; data-og-url=&quot;https://time-map-installer.tistory.com/196&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bg6Xlm/hyTjKhddNE/ln0U3hereuQkGkdjRwmJGK/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/o0Ajb/hyTjSTQYkn/jGs0LicDGSySKJK1FeZih0/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/6C4VB/hyTjF72q43/tMsOeOosybUbNN4Td9Bnk0/img.png?width=1234&amp;amp;height=542&amp;amp;face=802_23_929_161&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/196&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/196&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bg6Xlm/hyTjKhddNE/ln0U3hereuQkGkdjRwmJGK/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/o0Ajb/hyTjSTQYkn/jGs0LicDGSySKJK1FeZih0/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/6C4VB/hyTjF72q43/tMsOeOosybUbNN4Td9Bnk0/img.png?width=1234&amp;amp;height=542&amp;amp;face=802_23_929_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;[Folder Structure] Hooks? Utils? 차이점이 뭘까?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 개발을 하던 중에 함수를 따로 모듈화 하던 중 갑자기 궁금해 진 것이 있었습니다. &quot;이렇게 특정 기능을 재사용하거나 관리하기 쉽게 따로 모듈화를 하는 건 좋은데, 어느 폴더에 넣어&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과 화면(GIPHY Capture. The GIF Maker 사용, AppStore)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Jul-13-2023 16-31-37.gif&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;744&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYP2w9/btsnvFFXrlN/Mo5ZTD5gP59faKqfcfunQk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYP2w9/btsnvFFXrlN/Mo5ZTD5gP59faKqfcfunQk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYP2w9/btsnvFFXrlN/Mo5ZTD5gP59faKqfcfunQk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bYP2w9/btsnvFFXrlN/Mo5ZTD5gP59faKqfcfunQk/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;1188&quot; height=&quot;744&quot; data-filename=&quot;Jul-13-2023 16-31-37.gif&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;744&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Next Issue. 개선 이 필요한 부분들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 업데이트 했을 때 위치 다시 중앙으로 돌리기&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업데이트 버튼을 통해 중앙으로 돌리고 줌 상태도 초기화하는 데 성공했지만 마우스로 드래그를 하려고 할 시 바로 이전의 위치와 줌 상태로 돌아간다는 점을 해결해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjkJUl/btsnnUj2MSJ/K5WztZM7JHJAjqyJGu4f8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjkJUl/btsnnUj2MSJ/K5WztZM7JHJAjqyJGu4f8k/img.png&quot; style=&quot;width: 31.664662564986173%;&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;647&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;32.42&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjkJUl/btsnnUj2MSJ/K5WztZM7JHJAjqyJGu4f8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjkJUl%2FbtsnnUj2MSJ%2FK5WztZM7JHJAjqyJGu4f8k%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;782&quot; height=&quot;647&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QgXDj/btsnrr71kex/fCIMQzjOZMK9WMoLjuLM90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QgXDj/btsnrr71kex/fCIMQzjOZMK9WMoLjuLM90/img.png&quot; style=&quot;width: 33.05759746237321%;&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;592&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;33.84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QgXDj/btsnrr71kex/fCIMQzjOZMK9WMoLjuLM90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQgXDj%2Fbtsnrr71kex%2FfCIMQzjOZMK9WMoLjuLM90%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;747&quot; height=&quot;592&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYCrGi/btsnjceektc/uf7CUhRe14K5nkauIthyCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYCrGi/btsnjceektc/uf7CUhRe14K5nkauIthyCk/img.png&quot; style=&quot;width: 32.95215857729177%;&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;609&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;33.74&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYCrGi/btsnjceektc/uf7CUhRe14K5nkauIthyCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYCrGi%2Fbtsnjceektc%2Fuf7CUhRe14K5nkauIthyCk%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;766&quot; height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;/div&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;2. types 파일의 이름 변경 필요성&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입의 종류를 한 번 더 나누어 유지보수에 도움이 될 형태로 이름을 변경하면 좋을 것으로 보인다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 관계도에 맞는 그래프 새로 설정 필요&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;임시로 점 형태의 그래프를 나타내었지만, 실제로 구현해야 하는 부분은 노드와 노드간의 관계를 나타내는 그래프이므로 이 부분에 대한 작업이 필요하다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. SVG에 배경 넣기&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재는 SVG를 화면 크기에 맞게 넓혀둔 상태이고, 그 위에 UI들을 넣어둔 페이지의 구성을 하고 있다&lt;/li&gt;
&lt;li&gt;SVG에 배경을 넣는 방법을 찾아서 실제 디자인 컨셉에 가깝게 구현할 예정이다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/Bootcamp</category>
      <category>2주차</category>
      <category>bootcamp</category>
      <category>D3</category>
      <category>시각화</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/247</guid>
      <comments>https://time-map-installer.tistory.com/247#entry247comment</comments>
      <pubDate>Thu, 13 Jul 2023 16:34:26 +0900</pubDate>
    </item>
    <item>
      <title>[Prettier] Prettier를 세팅할 때 타입스크립트를 생각해야합니다</title>
      <link>https://time-map-installer.tistory.com/246</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tYca1/btsnaMmr0Mf/HB3dAZf2hL6UPGUk4x3tlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tYca1/btsnaMmr0Mf/HB3dAZf2hL6UPGUk4x3tlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tYca1/btsnaMmr0Mf/HB3dAZf2hL6UPGUk4x3tlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtYca1%2FbtsnaMmr0Mf%2FHB3dAZf2hL6UPGUk4x3tlk%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;450&quot; data-origin-width=&quot;2560&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상황&lt;/b&gt; : .prettierrc 파일에 따로 typescript 설정을 해두지 않았습니다.&lt;/p&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;pre id=&quot;code_1689080677053&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [position, setPosition] = useState&amp;lt;PositionType&amp;gt;({ x: 0, y: 0 });
const [dragging, setDragging] = useState&amp;lt;boolean&amp;gt;(false);
const [rel, setRel] = useState&amp;lt;PositionType | null&amp;gt;(null);

// 위와 같아야 하던 코드가 Prettier 세팅으로 인해 아래와 같이 변경됨

const [position, setPosition] = useState &amp;lt; PositionType &amp;gt; { x: 0, y: 0 };
const [dragging, setDragging] = useState &amp;lt; boolean &amp;gt; false;
const [rel, setRel] = (useState &amp;lt; PositionType) | (null &amp;gt; null);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원인&lt;/b&gt; : .prettierrc 파일에 parser가 babel로 세팅되어있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nXGp1/btsni9GB4oR/k9wC8cADY5bzW8H2pw49U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nXGp1/btsni9GB4oR/k9wC8cADY5bzW8H2pw49U0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nXGp1/btsni9GB4oR/k9wC8cADY5bzW8H2pw49U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnXGp1%2Fbtsni9GB4oR%2Fk9wC8cADY5bzW8H2pw49U0%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;400&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결법&lt;/b&gt; : parser를 babel-ts 또는 typescript로 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1689080787623&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;parser: &quot;babel-ts&quot;, // 사용할 파서(혹은 &quot;typescript&quot; 사용) (이전에는 parser: &quot;babel&quot; 이었음)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>(3D)Dev Deep Dive/TroubleShootings</category>
      <category>babel-ts</category>
      <category>parser</category>
      <category>Prettier</category>
      <category>Troubleshooting</category>
      <category>TypeScript</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/246</guid>
      <comments>https://time-map-installer.tistory.com/246#entry246comment</comments>
      <pubDate>Tue, 11 Jul 2023 22:16:43 +0900</pubDate>
    </item>
    <item>
      <title>[시각화] D3를 이용하여 간단한 노드관계 만들어보기</title>
      <link>https://time-map-installer.tistory.com/245</link>
      <description>&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;그렇기에 오늘은 D3를 이용하여 간단한 노드를 연결하는 작업을 해 볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, D3가 무엇인 지 알아보아야겠죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3를 이전에 정리했던 글에서 살펴보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1688912687527&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;[D3.js] 강력한 성능의 시각화 라이브러리, D3.js에 대해 알아보자&quot; data-og-description=&quot;D3.js 라는 이름을 들어 보셨나요? &amp;quot;Data-Driven Documents&amp;quot;의 줄임말인 D3.js는 웹 표준을 이용해 데이터를 시각화하는 데 사용되는 강력한 자바스크립트 라이브러리입니다. 주로 프론트엔드 고도화의 &quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/223&quot; data-og-url=&quot;https://time-map-installer.tistory.com/223&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b8FziW/hyTfCyJhbp/zkkAiCplkpakoQ98FybXq1/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/w3ngC/hyTgTeuvUr/HQxKfhAykeARrbcimBofkK/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/iJTHu/hyTgM7v7kW/HqkWxJr4itDalvF4526PG0/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/223&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/223&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b8FziW/hyTfCyJhbp/zkkAiCplkpakoQ98FybXq1/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/w3ngC/hyTgTeuvUr/HQxKfhAykeARrbcimBofkK/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/iJTHu/hyTgM7v7kW/HqkWxJr4itDalvF4526PG0/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280');&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;[D3.js] 강력한 성능의 시각화 라이브러리, D3.js에 대해 알아보자&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;D3.js 라는 이름을 들어 보셨나요? &quot;Data-Driven Documents&quot;의 줄임말인 D3.js는 웹 표준을 이용해 데이터를 시각화하는 데 사용되는 강력한 자바스크립트 라이브러리입니다. 주로 프론트엔드 고도화의&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;간단하게 D3만 써보기 위해 CRA(Create React App)을 이용하여 JS 리액트 프로젝트만 생성한 후&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;h2 data-ke-size=&quot;size26&quot;&gt;1단계 : 필요한 라이브러리 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 초기 세팅을 모두 마쳤으면 D3를 설치합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688913713929&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install d3&lt;/code&gt;&lt;/pre&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단계 : Mock Data 준비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드, 그리고 노드 사이의 링크를 나타내기 위해 Mock data를 이용하여 구성 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 그럴싸해 보이게 하기 위해 10개 정도씩 준비해 보았습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688913770800&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const nodes = [
    { id: &quot;node1&quot;, name: &quot;Node 1&quot; },
    { id: &quot;node2&quot;, name: &quot;Node 2&quot; },
    { id: &quot;node3&quot;, name: &quot;Node 3&quot; },
    { id: &quot;node4&quot;, name: &quot;Node 4&quot; },
    { id: &quot;node5&quot;, name: &quot;Node 5&quot; },
    { id: &quot;node6&quot;, name: &quot;Node 6&quot; },
    { id: &quot;node7&quot;, name: &quot;Node 7&quot; },
    { id: &quot;node8&quot;, name: &quot;Node 8&quot; },
    { id: &quot;node9&quot;, name: &quot;Node 9&quot; },
    { id: &quot;node10&quot;, name: &quot;Node 10&quot; },
  ];

  const links = [
    { source: &quot;node1&quot;, target: &quot;node2&quot;, name: &quot;Link 1-2&quot; },
    { source: &quot;node1&quot;, target: &quot;node3&quot;, name: &quot;Link 1-3&quot; },
    { source: &quot;node1&quot;, target: &quot;node4&quot;, name: &quot;Link 1-4&quot; },
    { source: &quot;node2&quot;, target: &quot;node3&quot;, name: &quot;Link 2-3&quot; },
    { source: &quot;node2&quot;, target: &quot;node4&quot;, name: &quot;Link 2-4&quot; },
    { source: &quot;node2&quot;, target: &quot;node5&quot;, name: &quot;Link 2-5&quot; },
    { source: &quot;node3&quot;, target: &quot;node4&quot;, name: &quot;Link 3-4&quot; },
    { source: &quot;node3&quot;, target: &quot;node5&quot;, name: &quot;Link 3-5&quot; },
    { source: &quot;node3&quot;, target: &quot;node6&quot;, name: &quot;Link 3-6&quot; },
    { source: &quot;node4&quot;, target: &quot;node5&quot;, name: &quot;Link 4-5&quot; },
    { source: &quot;node4&quot;, target: &quot;node6&quot;, name: &quot;Link 4-6&quot; },
    { source: &quot;node4&quot;, target: &quot;node7&quot;, name: &quot;Link 4-7&quot; },
    { source: &quot;node5&quot;, target: &quot;node6&quot;, name: &quot;Link 5-6&quot; },
  ];&lt;/code&gt;&lt;/pre&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;새로운 구성요소를 만들 것입니다. 구성요소가 마운트 될 때 useEffect 후크를 사용하여 그래프를 초기화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js는 force-directed 그래프를 생성하는 데 사용할 수 있는 'forceSimulation'기능을 제공합니다.&lt;br /&gt;&lt;br /&gt;여기에서는 useEffect Hooks에서 그래프를 생성하는 데 사용할 것입니다.&lt;br /&gt;forceSimulation&amp;nbsp;기능은&amp;nbsp;force에&amp;nbsp;따라&amp;nbsp;노드의&amp;nbsp;위치를&amp;nbsp;자동으로&amp;nbsp;업데이트하는&amp;nbsp;시뮬레이션을&amp;nbsp;실행합니다.&lt;br /&gt;&lt;br /&gt;이 시뮬레이션의 tick 이벤트를 수신하여 SVG에서 링크, 그리고 노드의 위치를 업데이트 할 수 있습니다.&lt;br /&gt;여기서&amp;nbsp;노드의&amp;nbsp;x,&amp;nbsp;y&amp;nbsp;속성과&amp;nbsp;링크의&amp;nbsp;x1,&amp;nbsp;y1,&amp;nbsp;x2,&amp;nbsp;y2&amp;nbsp;속성을&amp;nbsp;노드의&amp;nbsp;현재&amp;nbsp;위치를&amp;nbsp;기준으로&amp;nbsp;설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688913834576&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useRef, useEffect } from &quot;react&quot;;
import * as d3 from &quot;d3&quot;;

function Graph({ data }) {
	//...

  const svgRef = useRef();
  const width = 1600;
  const height = 1000;

  useEffect(() =&amp;gt; {
    const simulation = d3
      .forceSimulation(nodes)
      .force(
        &quot;link&quot;,
        d3
          .forceLink(links)
          .id((d) =&amp;gt; d.id)
          .distance(100)
      )
      .force(&quot;charge&quot;, d3.forceManyBody())
      .force(&quot;center&quot;, d3.forceCenter(width / 2, height / 2));

    const svg = d3.select(svgRef.current);

    const link = svg
      .append(&quot;g&quot;)
      .attr(&quot;stroke&quot;, &quot;#999&quot;)
      .attr(&quot;stroke-opacity&quot;, 0.6)
      .selectAll(&quot;line&quot;)
      .data(links)
      .join(&quot;line&quot;)
      .attr(&quot;stroke-width&quot;, (d) =&amp;gt; Math.sqrt(d.value));

    const node = svg
      .append(&quot;g&quot;)
      .attr(&quot;stroke&quot;, &quot;#fff&quot;)
      .attr(&quot;stroke-width&quot;, 1.5)
      .selectAll(&quot;circle&quot;)
      .data(nodes)
      .join(&quot;circle&quot;)
      .attr(&quot;r&quot;, 5)
      .attr(&quot;fill&quot;, &quot;#69b3a2&quot;);

    const labels = svg
      .append(&quot;g&quot;)
      .selectAll(&quot;text&quot;)
      .data(nodes)
      .join(&quot;text&quot;)
      .attr(&quot;text-anchor&quot;, &quot;middle&quot;)
      .attr(&quot;dominant-baseline&quot;, &quot;central&quot;)
      .text((d) =&amp;gt; d.name);

    function getConnectedNodes(node, depth = 0, nodeSet = new Set()) {
      if (depth &amp;gt; 2) return nodeSet;
      nodeSet.add(node.id);
      links.forEach((link) =&amp;gt; {
        if (link.source === node.id &amp;amp;&amp;amp; !nodeSet.has(link.target)) {
          getConnectedNodes(
            nodes.find((n) =&amp;gt; n.id === link.target),
            depth + 1,
            nodeSet
          );
        }
        if (link.target === node.id &amp;amp;&amp;amp; !nodeSet.has(link.source)) {
          getConnectedNodes(
            nodes.find((n) =&amp;gt; n.id === link.source),
            depth + 1,
            nodeSet
          );
        }
      });
      return nodeSet;
    }

    node.on(&quot;click&quot;, (event, d) =&amp;gt; {
      const connectedNodes = Array.from(getConnectedNodes(d));
      node.attr(&quot;fill&quot;, (n) =&amp;gt;
        connectedNodes.includes(n.id) ? &quot;#69b3a2&quot; : &quot;#ddd&quot;
      );
      link.style(&quot;stroke&quot;, (l) =&amp;gt;
        connectedNodes.includes(l.source.id) &amp;amp;&amp;amp;
        connectedNodes.includes(l.target.id)
          ? &quot;#999&quot;
          : &quot;#ddd&quot;
      );
    });

    simulation.on(&quot;tick&quot;, () =&amp;gt; {
      link
        .attr(&quot;x1&quot;, (d) =&amp;gt; d.source.x)
        .attr(&quot;y1&quot;, (d) =&amp;gt; d.source.y)
        .attr(&quot;x2&quot;, (d) =&amp;gt; d.target.x)
        .attr(&quot;y2&quot;, (d) =&amp;gt; d.target.y);

      node.attr(&quot;cx&quot;, (d) =&amp;gt; d.x).attr(&quot;cy&quot;, (d) =&amp;gt; d.y);

      labels.attr(&quot;x&quot;, (d) =&amp;gt; d.x).attr(&quot;y&quot;, (d) =&amp;gt; d.y);
    });
  }, []);

  return (
  	&amp;lt;svg ref={svgRef} width={width} height={height} /&amp;gt;
      &amp;lt;g className=&quot;nodes&quot; /&amp;gt;
      &amp;lt;g className=&quot;links&quot; /&amp;gt;
    &amp;lt;/svg&amp;gt;
  );
}

export default Graph;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SVG에서 링크와 노드를 그리면 아래와 같은 결과가 출력됩니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kMVfs/btsm7xWDkou/zRpxOmmezT5ap7FnGJGYZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kMVfs/btsm7xWDkou/zRpxOmmezT5ap7FnGJGYZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kMVfs/btsm7xWDkou/zRpxOmmezT5ap7FnGJGYZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkMVfs%2Fbtsm7xWDkou%2FzRpxOmmezT5ap7FnGJGYZ1%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;506&quot; height=&quot;395&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;395&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;이제, D3를 이용하여 여러분들의 상상의 나래를 펼쳐보아요!&lt;/p&gt;</description>
      <category>(3D)Dev Deep Dive/Frontend origin</category>
      <category>D3</category>
      <category>시각화</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/245</guid>
      <comments>https://time-map-installer.tistory.com/245#entry245comment</comments>
      <pubDate>Mon, 10 Jul 2023 19:29:07 +0900</pubDate>
    </item>
    <item>
      <title>[Team Project] WalkingPrint (Android Project)</title>
      <link>https://time-map-installer.tistory.com/244</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.40.30.png&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nq0tQ/btskDkrLeDY/zshsSTm0oEGE31NP5HfuSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nq0tQ/btskDkrLeDY/zshsSTm0oEGE31NP5HfuSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nq0tQ/btskDkrLeDY/zshsSTm0oEGE31NP5HfuSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnq0tQ%2FbtskDkrLeDY%2FzshsSTm0oEGE31NP5HfuSk%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;1064&quot; height=&quot;186&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.40.30.png&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;186&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.41.31.png&quot; data-origin-width=&quot;1394&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p7TP3/btskC4vN4mM/QrjmhRPEmvop6bbUz9CBS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p7TP3/btskC4vN4mM/QrjmhRPEmvop6bbUz9CBS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p7TP3/btskC4vN4mM/QrjmhRPEmvop6bbUz9CBS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp7TP3%2FbtskC4vN4mM%2FQrjmhRPEmvop6bbUz9CBS0%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;1394&quot; height=&quot;704&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.41.31.png&quot; data-origin-width=&quot;1394&quot; data-origin-height=&quot;704&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;주요 기능&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CPJb7/btskIb83SYT/L9NjVKhhAFhltktjtbxJ81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CPJb7/btskIb83SYT/L9NjVKhhAFhltktjtbxJ81/img.png&quot; width=&quot;234px;&quot; height=&quot;416px;&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.0478%; margin-right: 10px;&quot; data-widthpercent=&quot;31.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CPJb7/btskIb83SYT/L9NjVKhhAFhltktjtbxJ81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCPJb7%2FbtskIb83SYT%2FL9NjVKhhAFhltktjtbxJ81%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d0SIGV/btskDjGqkcD/G1sKR9nJFUhVDaBzEjQPGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d0SIGV/btskDjGqkcD/G1sKR9nJFUhVDaBzEjQPGk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;824&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.41.41.png&quot; style=&quot;width: 67.7894%;&quot; data-widthpercent=&quot;68.59&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d0SIGV/btskDjGqkcD/G1sKR9nJFUhVDaBzEjQPGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0SIGV%2FbtskDjGqkcD%2FG1sKR9nJFUhVDaBzEjQPGk%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;1012&quot; height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;/div&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;Fragments&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QI2cG/btskJULWbk6/otarYS1WIl4Jvg9r6rRJ20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QI2cG/btskJULWbk6/otarYS1WIl4Jvg9r6rRJ20/img.png&quot; width=&quot;244px;&quot; height=&quot;424px;&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;31.39&quot; style=&quot;width: 31.0205%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QI2cG/btskJULWbk6/otarYS1WIl4Jvg9r6rRJ20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQI2cG%2FbtskJULWbk6%2FotarYS1WIl4Jvg9r6rRJ20%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1MYd8/btskIZmWwO3/cBcw0QyXvN75KseVTyOpk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1MYd8/btskIZmWwO3/cBcw0QyXvN75KseVTyOpk1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;888&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.41.52.png&quot; style=&quot;width: 67.8167%;&quot; data-widthpercent=&quot;68.61&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1MYd8/btskIZmWwO3/cBcw0QyXvN75KseVTyOpk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1MYd8%2FbtskIZmWwO3%2FcBcw0QyXvN75KseVTyOpk1%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;1092&quot; height=&quot;888&quot;/&gt;&lt;/span&gt;&lt;/div&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;Map API&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yE7rP/btskJGG2bOP/gOqOBoe0TOBN6EmSVe5Sw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yE7rP/btskJGG2bOP/gOqOBoe0TOBN6EmSVe5Sw1/img.png&quot; width=&quot;244px;&quot; height=&quot;424px;&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;23.07&quot; style=&quot;width: 22.7987%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yE7rP/btskJGG2bOP/gOqOBoe0TOBN6EmSVe5Sw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyE7rP%2FbtskJGG2bOP%2FgOqOBoe0TOBN6EmSVe5Sw1%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1yDxW/btskC3XZ48q/UVk9GD3l8A5okLoJ8pIJx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1yDxW/btskC3XZ48q/UVk9GD3l8A5okLoJ8pIJx0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;710&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.42.05.png&quot; style=&quot;width: 76.0385%;&quot; data-widthpercent=&quot;76.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1yDxW/btskC3XZ48q/UVk9GD3l8A5okLoJ8pIJx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1yDxW%2FbtskC3XZ48q%2FUVk9GD3l8A5okLoJ8pIJx0%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;1332&quot; height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;/div&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;Results &amp;amp; DataBase&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd0WdJ/btskJizMs3c/GHl8hLmenmc5PrmpD09khk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd0WdJ/btskJizMs3c/GHl8hLmenmc5PrmpD09khk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1920&quot; data-filename=&quot;edited_KakaoTalk_Photo_2023-06-20-15-44-37.png&quot; data-widthpercent=&quot;37.54&quot; style=&quot;width: 37.0999%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd0WdJ/btskJizMs3c/GHl8hLmenmc5PrmpD09khk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd0WdJ%2FbtskJizMs3c%2FGHl8hLmenmc5PrmpD09khk%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;1080&quot; height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FTYZW/btskJjk8h3C/jaHqIpaC1GPMukePB0E4wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FTYZW/btskJjk8h3C/jaHqIpaC1GPMukePB0E4wK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;1376&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.42.14.png&quot; data-widthpercent=&quot;62.46&quot; style=&quot;width: 61.7373%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FTYZW/btskJjk8h3C/jaHqIpaC1GPMukePB0E4wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFTYZW%2FbtskJjk8h3C%2FjaHqIpaC1GPMukePB0E4wK%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;1288&quot; height=&quot;1376&quot;/&gt;&lt;/span&gt;&lt;/div&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;Used Queries&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lxz8Z/btskJt17sJT/i5UklkgKqukur3O221ySr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lxz8Z/btskJt17sJT/i5UklkgKqukur3O221ySr0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;560&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.42.25.png&quot; style=&quot;width: 60.0722%; margin-right: 10px;&quot; data-widthpercent=&quot;60.78&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lxz8Z/btskJt17sJT/i5UklkgKqukur3O221ySr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flxz8Z%2FbtskJt17sJT%2Fi5UklkgKqukur3O221ySr0%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;1054&quot; height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTbQZ1/btskEZ1YnNb/jOrT0hZZJjU68iiZr5E8j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTbQZ1/btskEZ1YnNb/jOrT0hZZJjU68iiZr5E8j0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;1044&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.42.35.png&quot; style=&quot;width: 38.765%;&quot; data-widthpercent=&quot;39.22&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTbQZ1/btskEZ1YnNb/jOrT0hZZJjU68iiZr5E8j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTbQZ1%2FbtskEZ1YnNb%2FjOrT0hZZJjU68iiZr5E8j0%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;1268&quot; height=&quot;1044&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/TeamProjects</category>
      <category>Android</category>
      <category>PROJECT</category>
      <category>TEAM</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/244</guid>
      <comments>https://time-map-installer.tistory.com/244#entry244comment</comments>
      <pubDate>Tue, 20 Jun 2023 15:48:40 +0900</pubDate>
    </item>
    <item>
      <title>[실리콘밸리 해커톤] 방방고(iOS)</title>
      <link>https://time-map-installer.tistory.com/243</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;진행정보&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기간 :&lt;/b&gt; 23/05/26 ~ 20/05/27 (1박 2일)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;규모 :&lt;/b&gt; 약 110명&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;&lt;b&gt;개발 유형 :&lt;/b&gt; iOS&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Demo&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtube.com/shorts/u38oQMk1ERw?feature=share&quot;&gt;https://youtube.com/shorts/u38oQMk1ERw?feature=share&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1687242357704&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;- YouTube&quot; data-og-description=&quot;&quot; data-og-host=&quot;www.youtube.com&quot; data-og-source-url=&quot;https://youtube.com/shorts/u38oQMk1ERw?feature=share&quot; data-og-url=&quot;https://www.youtube.com/watch?v=u38oQMk1ERw&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://youtube.com/shorts/u38oQMk1ERw?feature=share&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://youtube.com/shorts/u38oQMk1ERw?feature=share&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;- YouTube&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.youtube.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요(발표자료 활용)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.29.54.png&quot; data-origin-width=&quot;896&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xkQZX/btskJsWrr5b/NkJYYzTl02tj3HiW650XSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xkQZX/btskJsWrr5b/NkJYYzTl02tj3HiW650XSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xkQZX/btskJsWrr5b/NkJYYzTl02tj3HiW650XSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxkQZX%2FbtskJsWrr5b%2FNkJYYzTl02tj3HiW650XSk%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;173&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.29.54.png&quot; data-origin-width=&quot;896&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.30.35.png&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHj2Dq/btskDkkYSD2/7KYXRxSkkZNT5uZUDpzkm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHj2Dq/btskDkkYSD2/7KYXRxSkkZNT5uZUDpzkm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHj2Dq/btskDkkYSD2/7KYXRxSkkZNT5uZUDpzkm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHj2Dq%2FbtskDkkYSD2%2F7KYXRxSkkZNT5uZUDpzkm0%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;1410&quot; height=&quot;692&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.30.35.png&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.30.42.png&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhwQMQ/btskJjel2aT/LvRwweonKck8IgFNQgdajk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhwQMQ/btskJjel2aT/LvRwweonKck8IgFNQgdajk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhwQMQ/btskJjel2aT/LvRwweonKck8IgFNQgdajk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhwQMQ%2FbtskJjel2aT%2FLvRwweonKck8IgFNQgdajk%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;1124&quot; height=&quot;342&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.30.42.png&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.30.50.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cebo6O/btskCqFR8s0/mspzMNKdVbJKeqwPQkXap0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cebo6O/btskCqFR8s0/mspzMNKdVbJKeqwPQkXap0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cebo6O/btskCqFR8s0/mspzMNKdVbJKeqwPQkXap0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcebo6O%2FbtskCqFR8s0%2FmspzMNKdVbJKeqwPQkXap0%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;996&quot; height=&quot;198&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.30.50.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.04.png&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mV06P/btskDh9EY8j/p1MavK5uUDxR0PQ7kJ4Nb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mV06P/btskDh9EY8j/p1MavK5uUDxR0PQ7kJ4Nb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mV06P/btskDh9EY8j/p1MavK5uUDxR0PQ7kJ4Nb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmV06P%2FbtskDh9EY8j%2Fp1MavK5uUDxR0PQ7kJ4Nb0%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;1230&quot; height=&quot;578&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.04.png&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.12.png&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EJnEL/btskBLQUziH/KnigEEyZQFlqONgGk2Sqb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EJnEL/btskBLQUziH/KnigEEyZQFlqONgGk2Sqb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EJnEL/btskBLQUziH/KnigEEyZQFlqONgGk2Sqb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEJnEL%2FbtskBLQUziH%2FKnigEEyZQFlqONgGk2Sqb0%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;1236&quot; height=&quot;232&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.12.png&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&lt;b&gt;그 과정을 거친 후 앱에서 관광지에 대한 정보를 사용자에게 보여줍니다&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.22.png&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SUfxM/btskDhaHOWe/SS3KsQ73rgumi2Mx9eBkX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SUfxM/btskDhaHOWe/SS3KsQ73rgumi2Mx9eBkX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SUfxM/btskDhaHOWe/SS3KsQ73rgumi2Mx9eBkX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSUfxM%2FbtskDhaHOWe%2FSS3KsQ73rgumi2Mx9eBkX0%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;1326&quot; height=&quot;680&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.22.png&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프론트엔드로는 iOS를 위한 Swift를, 백엔드로는 서버를 위해 python 기반의 django를, DB의 경우 위치기반 서비스에 특화되어있는 PostgreSQL을 선택하였습니다. 또한 각 개발환경의 차이를 최소화하기 위한 방법으로 docker를 사용하였습니다.&lt;/b&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.30.png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdFuhg/btskJa9EDqs/go1WdioywUBx6NWtj7gRK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdFuhg/btskJa9EDqs/go1WdioywUBx6NWtj7gRK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdFuhg/btskJa9EDqs/go1WdioywUBx6NWtj7gRK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdFuhg%2FbtskJa9EDqs%2Fgo1WdioywUBx6NWtj7gRK1%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;804&quot; height=&quot;408&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.30.png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Swift에서 제공하는 MapKit, 그리고 CoreLocation이라는 라이브러리를 활용하였고, 이를 이용해 현재 위치 정보를 수집하는 데 사용했습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.38.png&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAuhbz/btskDjsO2c9/x4qq1dLkh0yik0uC9rtltK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAuhbz/btskDjsO2c9/x4qq1dLkh0yik0uC9rtltK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAuhbz/btskDjsO2c9/x4qq1dLkh0yik0uC9rtltK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAuhbz%2FbtskDjsO2c9%2Fx4qq1dLkh0yik0uC9rtltK%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;1162&quot; height=&quot;378&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.38.png&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;OpenAI의 GPT를 활용하였고, 위치 정보를 바탕으로 사용자가 원하는 거리 내에 존재하는 관광지를 추천해주는 역할을 하였습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.45.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvLrPK/btskIcmxBtO/TCVKHET0Tz60OWpuqi7ib0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvLrPK/btskIcmxBtO/TCVKHET0Tz60OWpuqi7ib0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvLrPK/btskIcmxBtO/TCVKHET0Tz60OWpuqi7ib0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvLrPK%2FbtskIcmxBtO%2FTCVKHET0Tz60OWpuqi7ib0%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;1364&quot; height=&quot;526&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.31.45.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;526&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Selenium을 활용하여 관광지 상세 정보 크롤링을 진행하였고, 상제 정보는 네이버 지도의 정보를 가져왔습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.32.03.png&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBZYW7/btskIdTe5Y5/CYXZNGSL9zxV0coTXHaOpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBZYW7/btskIdTe5Y5/CYXZNGSL9zxV0coTXHaOpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBZYW7/btskIdTe5Y5/CYXZNGSL9zxV0coTXHaOpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBZYW7%2FbtskIdTe5Y5%2FCYXZNGSL9zxV0coTXHaOpk%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;1254&quot; height=&quot;552&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.32.03.png&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.32.10.png&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRKnPR/btskEVZw0HJ/zorVlwKTQhfKh9DpwJftFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRKnPR/btskEVZw0HJ/zorVlwKTQhfKh9DpwJftFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRKnPR/btskEVZw0HJ/zorVlwKTQhfKh9DpwJftFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRKnPR%2FbtskEVZw0HJ%2FzorVlwKTQhfKh9DpwJftFk%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;678&quot; height=&quot;324&quot; data-filename=&quot;스크린샷 2023-06-20 오후 3.32.10.png&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/Techeer</category>
      <category>Demo</category>
      <category>아이디어톤</category>
      <category>해커톤</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/243</guid>
      <comments>https://time-map-installer.tistory.com/243#entry243comment</comments>
      <pubDate>Tue, 20 Jun 2023 15:38:42 +0900</pubDate>
    </item>
    <item>
      <title>[GradeBuddy] Term Project 결과물</title>
      <link>https://time-map-installer.tistory.com/242</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GradeBuddy_logo.png&quot; data-origin-width=&quot;150&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJb6wb/btskrbUXvkk/8puM40g3hBb9nmBmLORJr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJb6wb/btskrbUXvkk/8puM40g3hBb9nmBmLORJr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJb6wb/btskrbUXvkk/8puM40g3hBb9nmBmLORJr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJb6wb%2FbtskrbUXvkk%2F8puM40g3hBb9nmBmLORJr1%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;150&quot; height=&quot;70&quot; data-filename=&quot;GradeBuddy_logo.png&quot; data-origin-width=&quot;150&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Git : &lt;a href=&quot;https://github.com/TMInstaller/GradeBuddy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/TMInstaller/GradeBuddy&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687098543759&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - TMInstaller/GradeBuddy&quot; data-og-description=&quot;Contribute to TMInstaller/GradeBuddy development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/TMInstaller/GradeBuddy&quot; data-og-url=&quot;https://github.com/TMInstaller/GradeBuddy&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cNLPD6/hyS1edNnDM/tSzQ9X30IvK2AcDfX0KDpK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/TMInstaller/GradeBuddy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/TMInstaller/GradeBuddy&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cNLPD6/hyS1edNnDM/tSzQ9X30IvK2AcDfX0KDpK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - TMInstaller/GradeBuddy&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to TMInstaller/GradeBuddy development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IDE&lt;/b&gt;&amp;nbsp;:&amp;nbsp;IntelliJ&amp;nbsp;IDEA&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Language&lt;/b&gt; : Java 11&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Framework&lt;/b&gt; : Spring Boot 2.7.12&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ORM&lt;/b&gt; : MyBatis&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Database&lt;/b&gt; : MariaDB&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Template&lt;/b&gt; &lt;b&gt;Engine&lt;/b&gt; : JSP&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제작기간&lt;/b&gt; : 23/06/05 ~ 23/06/18&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Demo&lt;/h2&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=9hAxCoYLiW0&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/qsjfz/hyS1cUy4C6/KkfnJxMAvcuFgCm2uNh3Ak/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/9hAxCoYLiW0&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/개인</category>
      <category>Demo</category>
      <category>개인</category>
      <category>결과물</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/242</guid>
      <comments>https://time-map-installer.tistory.com/242#entry242comment</comments>
      <pubDate>Sun, 18 Jun 2023 23:27:59 +0900</pubDate>
    </item>
    <item>
      <title>[Project : HQRoutine] 14차 스프린트, Project 진행상황 중간정리</title>
      <link>https://time-map-installer.tistory.com/241</link>
      <description>&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;HQ.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UlWcJ/btsjkMCZlCN/nE3MYzNPdgNqMLYbgt5Zy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UlWcJ/btsjkMCZlCN/nE3MYzNPdgNqMLYbgt5Zy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UlWcJ/btsjkMCZlCN/nE3MYzNPdgNqMLYbgt5Zy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUlWcJ%2FbtsjkMCZlCN%2FnE3MYzNPdgNqMLYbgt5Zy0%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;215&quot; height=&quot;215&quot; data-filename=&quot;HQ.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;HQRoutineLogo.png&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UBdlz/btsjpE4QpSt/g6WMweFPAHlhoqkkr44VyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UBdlz/btsjpE4QpSt/g6WMweFPAHlhoqkkr44VyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UBdlz/btsjpE4QpSt/g6WMweFPAHlhoqkkr44VyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUBdlz%2FbtsjpE4QpSt%2Fg6WMweFPAHlhoqkkr44VyK%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;385&quot; height=&quot;144&quot; data-filename=&quot;HQRoutineLogo.png&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Frontend Spec&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Language : TypeScript&lt;/li&gt;
&lt;li&gt;Framework : Next.js&lt;/li&gt;
&lt;li&gt;Library : Axios, Moment.js, react-paginate, react-quill, next/Image, react-slick&lt;/li&gt;
&lt;li&gt;CSS : Sass&lt;/li&gt;
&lt;li&gt;IDE : Visual Studio Code&lt;/li&gt;
&lt;li&gt;Git Hooks : Husky, Prettier, ESLint&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이론 관련 문서화 내용들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JavaScript에서 화살표 함수와 function 함수의 차이점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/233&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://time-map-installer.tistory.com/233&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1686398626281&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, React] const = () =&amp;gt; {}? function () {}?  함수 표현식, 함수 선언문 중 무엇을 쓸까?&quot; data-og-description=&quot;자바스크립트에서 함수를 작성하는 방법은 여러 가지가 있습니다. 그 중에서도 함수 선언문, 함수 표현식, 그리고 화살표 함수에 대해 알아보겠습니다. 이들의 차이점은 무엇이고 어떤 상황에&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/233&quot; data-og-url=&quot;https://time-map-installer.tistory.com/233&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jBkud/hySWZlVuNp/NWss5GKNdOrQn0nBeK0FB1/img.png?width=626&amp;amp;height=150&amp;amp;face=0_0_626_150,https://scrap.kakaocdn.net/dn/d1ItNx/hySWqdIPeJ/Qin1Mjx3KAupqiDJmGzrt0/img.png?width=626&amp;amp;height=150&amp;amp;face=0_0_626_150,https://scrap.kakaocdn.net/dn/3B5GS/hySXb0WVEw/LL4UN07Bbe859Zw6AZazyk/img.png?width=2548&amp;amp;height=1426&amp;amp;face=0_0_2548_1426&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/233&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/233&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jBkud/hySWZlVuNp/NWss5GKNdOrQn0nBeK0FB1/img.png?width=626&amp;amp;height=150&amp;amp;face=0_0_626_150,https://scrap.kakaocdn.net/dn/d1ItNx/hySWqdIPeJ/Qin1Mjx3KAupqiDJmGzrt0/img.png?width=626&amp;amp;height=150&amp;amp;face=0_0_626_150,https://scrap.kakaocdn.net/dn/3B5GS/hySXb0WVEw/LL4UN07Bbe859Zw6AZazyk/img.png?width=2548&amp;amp;height=1426&amp;amp;face=0_0_2548_1426');&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, React] const = () =&amp;gt; {}? function () {}? 함수 표현식, 함수 선언문 중 무엇을 쓸까?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 함수를 작성하는 방법은 여러 가지가 있습니다. 그 중에서도 함수 선언문, 함수 표현식, 그리고 화살표 함수에 대해 알아보겠습니다. 이들의 차이점은 무엇이고 어떤 상황에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;b&gt;고도화 주제 탐색 중 SEO 향상 방법에 관한 탐색 진행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/225&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://time-map-installer.tistory.com/225&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1686398680140&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;[SEO] 서비스의 검색 엔진 순위를 높여보자, 헤더에 SEO 적용하기&quot; data-og-description=&quot;SEO(검색 엔진 최적화)는 웹사이트의 검색 엔진 순위를 높이기 위한 중요한 요소 입니다. 웹사이트를 아무리 잘 만들어 두어도 검색이 되지 않는다면 사용자는 쉽게 들어오지 않을 것이고, 결국 &quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/225&quot; data-og-url=&quot;https://time-map-installer.tistory.com/225&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cLTkuD/hySWYUQTqB/ULZ0BVO6OYmj7to2MJNQNk/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/CD7Lv/hySXb0WXgO/KtBOwdHfLpIDmTDV1xJtek/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/bjm26u/hySWm3t9Ls/hbeeVX8ol1IcB1N251Kaqk/img.png?width=1350&amp;amp;height=832&amp;amp;face=0_0_1350_832&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/225&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/225&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cLTkuD/hySWYUQTqB/ULZ0BVO6OYmj7to2MJNQNk/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/CD7Lv/hySXb0WXgO/KtBOwdHfLpIDmTDV1xJtek/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/bjm26u/hySWm3t9Ls/hbeeVX8ol1IcB1N251Kaqk/img.png?width=1350&amp;amp;height=832&amp;amp;face=0_0_1350_832');&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;[SEO] 서비스의 검색 엔진 순위를 높여보자, 헤더에 SEO 적용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SEO(검색 엔진 최적화)는 웹사이트의 검색 엔진 순위를 높이기 위한 중요한 요소 입니다. 웹사이트를 아무리 잘 만들어 두어도 검색이 되지 않는다면 사용자는 쉽게 들어오지 않을 것이고, 결국&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;b&gt;서비스가 복잡해짐에 따라 상태관리의 필요성을 느껴 알아본 관련 상태관리 툴들&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/224&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://time-map-installer.tistory.com/224&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1686398767086&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;[Recoil vs Redux] Frontend Engineer를 위한 React 상태관리 도구 알아보기&quot; data-og-description=&quot;프로젝트의 고도화가 진행되던 중, 이제는 전역 상태관리 도구를 이용하여 관련된 작업을 해야겠다 느꼈습니다. 리액트를 사용하고 있는 저는 리액트에서 쓸 수 있는 상태관리 툴들에 대해 알&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/224&quot; data-og-url=&quot;https://time-map-installer.tistory.com/224&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MgkuY/hySW84e2rk/D88K77RDJpwgycdrL1m1J0/img.png?width=720&amp;amp;height=652&amp;amp;face=0_0_720_652,https://scrap.kakaocdn.net/dn/rMn0l/hySWZ7ivXt/GhdkhRLeFt72OxrulrLY5k/img.png?width=720&amp;amp;height=652&amp;amp;face=0_0_720_652,https://scrap.kakaocdn.net/dn/jpC2y/hySW0rBvsa/b9u6vSY3KpeHIHPNGTYK50/img.png?width=720&amp;amp;height=652&amp;amp;face=0_0_720_652&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/224&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/224&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MgkuY/hySW84e2rk/D88K77RDJpwgycdrL1m1J0/img.png?width=720&amp;amp;height=652&amp;amp;face=0_0_720_652,https://scrap.kakaocdn.net/dn/rMn0l/hySWZ7ivXt/GhdkhRLeFt72OxrulrLY5k/img.png?width=720&amp;amp;height=652&amp;amp;face=0_0_720_652,https://scrap.kakaocdn.net/dn/jpC2y/hySW0rBvsa/b9u6vSY3KpeHIHPNGTYK50/img.png?width=720&amp;amp;height=652&amp;amp;face=0_0_720_652');&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;[Recoil vs Redux] Frontend Engineer를 위한 React 상태관리 도구 알아보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트의 고도화가 진행되던 중, 이제는 전역 상태관리 도구를 이용하여 관련된 작업을 해야겠다 느꼈습니다. 리액트를 사용하고 있는 저는 리액트에서 쓸 수 있는 상태관리 툴들에 대해 알&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;b&gt;일관된 코드 템플릿 적용을 위한 코드 품질 및 일관성 유지 도구 탐색&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/211&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://time-map-installer.tistory.com/211&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1686398792873&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;[ESLint, husky, Prettier] 당신의 코드 품질을 책임져주는 3가지 도구들&quot; data-og-description=&quot;ESLint? Husky? Prettier? ESLint, Husky, Prettier는 프로그래밍을 할 때 코드 품질과 일관성을 유지하기 위한 도구입니다. 그리고 이제 개발의 세계에서는 코드 품질과 일관성을 유지하는 것이 굉장히 중요&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/211&quot; data-og-url=&quot;https://time-map-installer.tistory.com/211&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/brlMeN/hySW5l9x9Z/q4SjA2ViIXiTvqRi9YbtZ1/img.png?width=740&amp;amp;height=740&amp;amp;face=0_0_740_740,https://scrap.kakaocdn.net/dn/bzxY7D/hySWoGZ89C/sQcGNUeVo57rF0rModf1GK/img.png?width=740&amp;amp;height=740&amp;amp;face=0_0_740_740,https://scrap.kakaocdn.net/dn/b3MK2v/hySW1jKbQJ/dqNzgXAAZleWjlCQ16zoF1/img.png?width=740&amp;amp;height=740&amp;amp;face=0_0_740_740&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/211&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/211&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/brlMeN/hySW5l9x9Z/q4SjA2ViIXiTvqRi9YbtZ1/img.png?width=740&amp;amp;height=740&amp;amp;face=0_0_740_740,https://scrap.kakaocdn.net/dn/bzxY7D/hySWoGZ89C/sQcGNUeVo57rF0rModf1GK/img.png?width=740&amp;amp;height=740&amp;amp;face=0_0_740_740,https://scrap.kakaocdn.net/dn/b3MK2v/hySW1jKbQJ/dqNzgXAAZleWjlCQ16zoF1/img.png?width=740&amp;amp;height=740&amp;amp;face=0_0_740_740');&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;[ESLint, husky, Prettier] 당신의 코드 품질을 책임져주는 3가지 도구들&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ESLint? Husky? Prettier? ESLint, Husky, Prettier는 프로그래밍을 할 때 코드 품질과 일관성을 유지하기 위한 도구입니다. 그리고 이제 개발의 세계에서는 코드 품질과 일관성을 유지하는 것이 굉장히 중요&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;b&gt;Type 관련 네이밍 통일을 위한 type과 interface의 차이점 탐색&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/197&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://time-map-installer.tistory.com/197&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1686398880621&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] type {함수명}? interface {함수명}?&quot; data-og-description=&quot;오늘도 평화롭게 사탕을 하나 먹으며 코드 리팩토링 요소를 찾아보던 날이었습니다. 입안 가득 퍼지는 청포도 향이 좋다고 생각하다가 전에 타입을 지정할 때 interface와 type을 둘 다 썼던 것만 &quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/197&quot; data-og-url=&quot;https://time-map-installer.tistory.com/197&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/IACwx/hySWX9tAMf/kzXvZpxStI252VCsk0ocoK/img.png?width=186&amp;amp;height=186&amp;amp;face=0_0_186_186,https://scrap.kakaocdn.net/dn/dbH215/hySW32Vhn7/mxS8lhzNaulZs3FZiVbCFk/img.png?width=186&amp;amp;height=186&amp;amp;face=0_0_186_186,https://scrap.kakaocdn.net/dn/lEOCM/hySW929FUa/jlOt3LrFk31JTBkDNUJS4k/img.png?width=2518&amp;amp;height=1080&amp;amp;face=0_0_2518_1080&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/197&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/197&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/IACwx/hySWX9tAMf/kzXvZpxStI252VCsk0ocoK/img.png?width=186&amp;amp;height=186&amp;amp;face=0_0_186_186,https://scrap.kakaocdn.net/dn/dbH215/hySW32Vhn7/mxS8lhzNaulZs3FZiVbCFk/img.png?width=186&amp;amp;height=186&amp;amp;face=0_0_186_186,https://scrap.kakaocdn.net/dn/lEOCM/hySW929FUa/jlOt3LrFk31JTBkDNUJS4k/img.png?width=2518&amp;amp;height=1080&amp;amp;face=0_0_2518_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;[TypeScript] type {함수명}? interface {함수명}?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;오늘도 평화롭게 사탕을 하나 먹으며 코드 리팩토링 요소를 찾아보던 날이었습니다. 입안 가득 퍼지는 청포도 향이 좋다고 생각하다가 전에 타입을 지정할 때 interface와 type을 둘 다 썼던 것만&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;b&gt;유지보수를 위한 폴더구조 설정 중 Hooks, Utils의 차이점 인식을 위한 탐색&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/196&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://time-map-installer.tistory.com/196&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1686398937315&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;[Folder Structure] Hooks? Utils? 차이점이 뭘까?&quot; data-og-description=&quot;프로젝트 개발을 하던 중에 함수를 따로 모듈화 하던 중 갑자기 궁금해 진 것이 있었습니다. &amp;quot;이렇게 특정 기능을 재사용하거나 관리하기 쉽게 따로 모듈화를 하는 건 좋은데, 어느 폴더에 넣어&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/196&quot; data-og-url=&quot;https://time-map-installer.tistory.com/196&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bFtOOP/hySWX9tB6j/udNKXYTtqvJb65kxPWEmS1/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/F9h3n/hySW3V8Reh/7TGNqkR5trGwxxLaxzC8t0/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/bietwx/hySW32VofV/ZF6Vai2RymckbEXWH3VTy0/img.png?width=1234&amp;amp;height=542&amp;amp;face=802_23_929_161&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/196&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/196&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bFtOOP/hySWX9tB6j/udNKXYTtqvJb65kxPWEmS1/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/F9h3n/hySW3V8Reh/7TGNqkR5trGwxxLaxzC8t0/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280,https://scrap.kakaocdn.net/dn/bietwx/hySW32VofV/ZF6Vai2RymckbEXWH3VTy0/img.png?width=1234&amp;amp;height=542&amp;amp;face=802_23_929_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;[Folder Structure] Hooks? Utils? 차이점이 뭘까?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 개발을 하던 중에 함수를 따로 모듈화 하던 중 갑자기 궁금해 진 것이 있었습니다. &quot;이렇게 특정 기능을 재사용하거나 관리하기 쉽게 따로 모듈화를 하는 건 좋은데, 어느 폴더에 넣어&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;h2 data-ke-size=&quot;size26&quot;&gt;담당 주 작업 페이지 : MainPage&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-10 오후 7.54.38.png&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;910&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ozjmd/btsji4LfcMh/mQ3KqZGU4lLz5DeAmZRZD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ozjmd/btsji4LfcMh/mQ3KqZGU4lLz5DeAmZRZD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ozjmd/btsji4LfcMh/mQ3KqZGU4lLz5DeAmZRZD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOzjmd%2Fbtsji4LfcMh%2FmQ3KqZGU4lLz5DeAmZRZD0%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;1372&quot; height=&quot;910&quot; data-filename=&quot;스크린샷 2023-06-10 오후 7.54.38.png&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;910&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;총 5개의 구역으로 이루어져 있으며 아래와 같이 나뉩니다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_스크린샷 2023-06-10 오후 7.54.38.png&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;910&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rvK0o/btsjnabUJAI/Jnth1Z5nUqHeq0zR1HhKy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rvK0o/btsjnabUJAI/Jnth1Z5nUqHeq0zR1HhKy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rvK0o/btsjnabUJAI/Jnth1Z5nUqHeq0zR1HhKy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrvK0o%2FbtsjnabUJAI%2FJnth1Z5nUqHeq0zR1HhKy1%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;1372&quot; height=&quot;910&quot; data-filename=&quot;edited_스크린샷 2023-06-10 오후 7.54.38.png&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;910&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1번 구역 - Calendar Section&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-10 오후 8.49.16.png&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbvG1b/btsjpDdQrEI/Owg6Cfm9ywS9aPsuHacep1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbvG1b/btsjpDdQrEI/Owg6Cfm9ywS9aPsuHacep1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbvG1b/btsjpDdQrEI/Owg6Cfm9ywS9aPsuHacep1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbvG1b%2FbtsjpDdQrEI%2FOwg6Cfm9ywS9aPsuHacep1%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;402&quot; height=&quot;542&quot; data-filename=&quot;스크린샷 2023-06-10 오후 8.49.16.png&quot; data-origin-width=&quot;402&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;&lt;b&gt;Calendar 컴포넌트가 들어가있는 곳&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Moment.js 를 활용하여 날짜 관련된 작업 수행&lt;/li&gt;
&lt;li&gt;현재 달에 해당하는 날 뿐만아니라 이전, 다음달에 해당하는 일부 일을 가지고 오는 작업 수행(사진 참고)&lt;/li&gt;
&lt;li&gt;개인 일정이 있는 날에는 하단에 붉은 점을, 배너를 통해 가져온 일정이 있으면 하단에 주황 점을 나타내도록 함&lt;/li&gt;
&lt;li&gt;특정 날짜 클릭 시 현재 선택된 날짜를 클릭된 날짜로 변경&lt;/li&gt;
&lt;li&gt;회색으로 되어있는 다른 달의 날을 클릭할 경우 현재 위치해있는 월을 해당 월로 변경
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(사진에서 7월 7일 누를 시 7월로 테이블 변경 이벤트 발생)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1686397849775&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// CalendarTable(날짜를 나타내는 부분 일부 코드 발췌)
let result: JSX.Element[] = [];
  let week = firstWeek;

  for (week; week &amp;lt;= lastWeek || result.length &amp;lt; 6; week++) {
    result = result.concat(
      &amp;lt;tr key={week}&amp;gt;
        {Array(7)
          .fill(0)
          .map((data, index) =&amp;gt; {
            let days = today
              .clone()
              .startOf('year')
              .week(week)
              .startOf('week')
              .add(index, 'day');

            const buttonColor =
              moment().format('YYYYMMDD') === days.format('YYYYMMDD')
                ? 'yellow'
                : recentlyClickedDay?.format('YYYYMMDD') ===
                  days.format('YYYYMMDD')
                ? 'green'
                : days.format('MM') !== today.format('MM')
                ? 'lightgray'
                : 'inherit';

            return (
              &amp;lt;td key={index}&amp;gt;
                &amp;lt;button
                  onClick={() =&amp;gt; handleClick(days)}
                  style={{ backgroundColor: buttonColor }}
                &amp;gt;
                  &amp;lt;span&amp;gt;{days.format('D')}&amp;lt;/span&amp;gt;
                  &amp;lt;br /&amp;gt;
                  &amp;lt;span
                    style={{
                      color: hasEvent(days, tasks) ? 'red' : 'transparent',
                    }}
                  &amp;gt;
                    &amp;bull;
                  &amp;lt;/span&amp;gt;
                  &amp;lt;span
                    style={{
                      color: hasBannerEvent(days, bannerTasks)
                        ? 'orange'
                        : 'transparent',
                    }}
                  &amp;gt;
                    &amp;bull;
                  &amp;lt;/span&amp;gt;
                &amp;lt;/button&amp;gt;
              &amp;lt;/td&amp;gt;
            );
          })}
      &amp;lt;/tr&amp;gt;,
    );
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2번 구역 - Routine Section&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-10 오후 8.05.44.png&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ7LjR/btsjnaprA8S/6vcDIOuEFtFkKx7K69uuK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ7LjR/btsjnaprA8S/6vcDIOuEFtFkKx7K69uuK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ7LjR/btsjnaprA8S/6vcDIOuEFtFkKx7K69uuK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ7LjR%2FbtsjnaprA8S%2F6vcDIOuEFtFkKx7K69uuK0%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;398&quot; height=&quot;544&quot; data-filename=&quot;스크린샷 2023-06-10 오후 8.05.44.png&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Routine 컴포넌트가 들어가있는 곳, 관련 CRUD가 진행되는 위치&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 일정 추가할 때 위 위치에서 폼이 생성됨 - 컴포넌트 간 소통 작업 진행&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React의 디자인 패턴 중 Lift up state 패턴을 사용하여 컴포넌트간 상호작용 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1686396908696&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// CalendarProps, 여기서 onAddTaskClick을 통해 폼 생성 요청 보냄
interface CalendarProps {
  onTasksChange: (tasks: TaskInfoType[]) =&amp;gt; void;
  onBannerTasksChange: (tasks: BannerTaskInfoType[]) =&amp;gt; void;
  onAddTaskClick: () =&amp;gt; void;
}

const Calendar: React.FC&amp;lt;CalendarProps&amp;gt; = ({
  onAddTaskClick,
  onTasksChange,
  onBannerTasksChange,
}) =&amp;gt; {
	...
	return(
		&amp;lt;button onClick={onAddTaskClick}&amp;gt;+ 일정 추가하기&amp;lt;/button&amp;gt;
    );
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1686396493233&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MainPage, 여길 통해 Routine Component로 인자 전달
const [showForm, setShowForm] = useState(false);

  const handleShowForm = () =&amp;gt; {
    setShowForm(!showForm);
  };
  ...
  return(
  	...
    {/* 달력 컴포넌트 */}
          &amp;lt;Calendar
            onTasksChange={setSelectedTasks}
            onBannerTasksChange={setSelectedBannerTasks}
            onAddTaskClick={handleShowForm}
            // onBannerTasksChange={handleBannerTasksChange}
          /&amp;gt;
    {/* Routine 컴포넌트 */}
    		&amp;lt;RoutineMain
              selectedTasks={selectedTasks}
              selectedBannerTasks={selectedBannerTasks}
              setSelectedTasks={setSelectedTasks}
              showForm={showForm}
              setShowForm={setShowForm}
              onUpdateSelectedBannerTask={updateBannerSelectedTask}
              onUpdateSelectedTask={updateSelectedTask}
            /&amp;gt;
    ...
  )&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1686396703165&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Routine 컴포넌트, MainPage를 통해 받아온 상태값을 전달
const RoutineMain: React.FC&amp;lt;RoutineMainProps&amp;gt; = ({
  selectedTasks,
  selectedBannerTasks,
  showForm,
  setShowForm,
  onUpdateSelectedTask,
  setSelectedTasks,
  onUpdateSelectedBannerTask,
}) =&amp;gt; {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3번 구역 - UserInfo Section&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;간단한 Info를 보여주는 위치&lt;/b&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;h4 data-ke-size=&quot;size20&quot;&gt;4번 구역 - SpecialRoutine Section&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Special Routine 관련 컴포넌트가 들어가있는 공간&lt;/b&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;status를 추가하여 현재 일정 진행중인지, 시작 전인 지 여부 구현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 시간과 지정된 시간 사이의 차이를 일정 시간마다 다시 확인해서 업데이트&lt;/p&gt;
&lt;pre id=&quot;code_1686398087625&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 관련 코드 중 일부
const [routineStatus, setRoutineStatus] = useState&amp;lt;string&amp;gt;('');
  const [nextRoutineData, setNextRoutineData] = useState&amp;lt;
    TaskInfoType | undefined
  &amp;gt;(undefined);

  useEffect(() =&amp;gt; {
    const fetchData = async () =&amp;gt; {
      const data = await getSingleRoutine(1);
      // console.log(data);
      if (data) {
        setNextRoutineData(data);

        const interval = setInterval(() =&amp;gt; {
          const currentTime = new Date().getTime();
          const startAtTime = new Date(data.startAt).getTime();
          const endAtTime = new Date(data.endAt).getTime();
          const timeDiff = endAtTime - currentTime;

          const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
          const hours = Math.floor(
            (timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60),
          );
          const minutes = Math.floor(
            (timeDiff % (1000 * 60 * 60)) / (1000 * 60),
          );

          setTimeLeft({ days, hours, minutes });

          if (currentTime &amp;lt; startAtTime &amp;amp;&amp;amp; !data.isFinished) {
            setRoutineStatus('시작 전');
          } else if (
            currentTime &amp;gt;= startAtTime &amp;amp;&amp;amp;
            currentTime &amp;lt; endAtTime &amp;amp;&amp;amp;
            !data.isFinished
          ) {
            setRoutineStatus('진행 중');
          } else {
            setRoutineStatus('종료');
            clearInterval(interval);
          }
        }, 1000);

        return () =&amp;gt; {
          clearInterval(interval);
        };
      }
    };

    fetchData();
  }, []);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5번 구역 - Banner Slider Section&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 사용자들이 추가한 Banner을 불러오는 기능 구현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 이미지를 넘길수도 있지만 일정 시간마다 다음 이미지로 넘어가게 두어 여러 프로모션 배너를 사용자들이 볼 수 있도록 함&lt;/p&gt;
&lt;pre id=&quot;code_1686397143240&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const settings = {
    dots: false,
    infinite: true,
    autoplay: true,
    autoplaySpeed: 3000,
    slidesToShow: 2,
    slidesToScroll: 1,
  };
  
  return(
        &amp;lt;Slider {...settings}&amp;gt;
        
        {/*여기에 Slider 구현*/}
        
        &amp;lt;/Slider&amp;gt;
  );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 정보를 수정하고 가독성 향상과 유지보수를 위해 분리, &lt;b&gt;next/Image&lt;/b&gt; 라이브러리를 이용하여 이미지 로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;react-slick&lt;/b&gt; 라이브러리를 활용하여 슬라이더 기능 구현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;+ Frontend Part 진행상황&lt;/h2&gt;
&lt;figure id=&quot;og_1686399294645&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;프로젝트명 : HQ Rutine 개발 페이지 : 배너 리스트 페이지 / 배너 상세 페이지 / 배너 글 작성 페이지 / 로그인 페이지&quot; data-og-host=&quot;unruly-stallion-b93.notion.site&quot; data-og-source-url=&quot;https://unruly-stallion-b93.notion.site/2c28ac54fd424ccd98631ad156831a2d&quot; data-og-url=&quot;https://unruly-stallion-b93.notion.site/2c28ac54fd424ccd98631ad156831a2d&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://unruly-stallion-b93.notion.site/2c28ac54fd424ccd98631ad156831a2d&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://unruly-stallion-b93.notion.site/2c28ac54fd424ccd98631ad156831a2d&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;프로젝트 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트명 : HQ Rutine 개발 페이지 : 배너 리스트 페이지 / 배너 상세 페이지 / 배너 글 작성 페이지 / 로그인 페이지&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;unruly-stallion-b93.notion.site&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;h2 data-ke-size=&quot;size26&quot;&gt;+ Backend Part 진행상황&lt;/h2&gt;
&lt;figure id=&quot;og_1686399249380&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;[Springboot] 달력에 필요한 직전 달 일정, 다음 달 일정 가져오기&quot; data-og-description=&quot;달력에 필요한 직전 달 일정, 다음 달 일정 가져오기&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@bjo6300/Springboot-%EB%8B%AC%EB%A0%A5%EC%97%90-%ED%95%84%EC%9A%94%ED%95%9C-%EC%A7%81%EC%A0%84-%EB%8B%AC-%EC%9D%BC%EC%A0%95-%EB%8B%A4%EC%9D%8C-%EB%8B%AC-%EC%9D%BC%EC%A0%95-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@bjo6300/Springboot-달력에-필요한-직전-달-일정-다음-달-일정-가져오기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lYYMY/hySXecjhb8/OPjX6mvULDnlYOA9UVyNuK/img.png?width=1510&amp;amp;height=1194&amp;amp;face=0_0_1510_1194,https://scrap.kakaocdn.net/dn/gowmw/hySWmPVLW1/VQ5IKDS7uOoXRlUktPwNW1/img.png?width=1510&amp;amp;height=1194&amp;amp;face=0_0_1510_1194&quot;&gt;&lt;a href=&quot;https://velog.io/@bjo6300/Springboot-%EB%8B%AC%EB%A0%A5%EC%97%90-%ED%95%84%EC%9A%94%ED%95%9C-%EC%A7%81%EC%A0%84-%EB%8B%AC-%EC%9D%BC%EC%A0%95-%EB%8B%A4%EC%9D%8C-%EB%8B%AC-%EC%9D%BC%EC%A0%95-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@bjo6300/Springboot-%EB%8B%AC%EB%A0%A5%EC%97%90-%ED%95%84%EC%9A%94%ED%95%9C-%EC%A7%81%EC%A0%84-%EB%8B%AC-%EC%9D%BC%EC%A0%95-%EB%8B%A4%EC%9D%8C-%EB%8B%AC-%EC%9D%BC%EC%A0%95-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lYYMY/hySXecjhb8/OPjX6mvULDnlYOA9UVyNuK/img.png?width=1510&amp;amp;height=1194&amp;amp;face=0_0_1510_1194,https://scrap.kakaocdn.net/dn/gowmw/hySWmPVLW1/VQ5IKDS7uOoXRlUktPwNW1/img.png?width=1510&amp;amp;height=1194&amp;amp;face=0_0_1510_1194');&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;[Springboot] 달력에 필요한 직전 달 일정, 다음 달 일정 가져오기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;달력에 필요한 직전 달 일정, 다음 달 일정 가져오기&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>활동내역.zip/Techeer</category>
      <category>Frontend</category>
      <category>HQRoutine</category>
      <category>next.js</category>
      <category>구현</category>
      <category>정리</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/241</guid>
      <comments>https://time-map-installer.tistory.com/241#entry241comment</comments>
      <pubDate>Sat, 10 Jun 2023 20:57:11 +0900</pubDate>
    </item>
    <item>
      <title>[Design] 피그마 그리드 템플릿</title>
      <link>https://time-map-installer.tistory.com/240</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;웹 디자인을 할 때 필요한 피그마 그리드 템플릿입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Columns&lt;/b&gt;과 &lt;b&gt;Rows&lt;/b&gt;로 나뉘며 스펙은 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-02 오후 3.24.26.png&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI1seu/btsiop21shg/9WUtgJX82CrXRUOvwDU5pK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI1seu/btsiop21shg/9WUtgJX82CrXRUOvwDU5pK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI1seu/btsiop21shg/9WUtgJX82CrXRUOvwDU5pK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI1seu%2Fbtsiop21shg%2F9WUtgJX82CrXRUOvwDU5pK%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;498&quot; height=&quot;268&quot; data-filename=&quot;스크린샷 2023-06-02 오후 3.24.26.png&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 그리드가 들어올텐데 Columns와 Rows는 Grid를 선택해서 고를 수 있습니다.&lt;/p&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKLpR5/btsisSQyd6B/1JVwDnKp8mJCk3LCKWmpP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKLpR5/btsisSQyd6B/1JVwDnKp8mJCk3LCKWmpP1/img.png&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;544&quot; data-filename=&quot;스크린샷 2023-06-02 오후 3.26.07.png&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.2081%; margin-right: 10px;&quot; data-widthpercent=&quot;52.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKLpR5/btsisSQyd6B/1JVwDnKp8mJCk3LCKWmpP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKLpR5%2FbtsisSQyd6B%2F1JVwDnKp8mJCk3LCKWmpP1%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;958&quot; height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wxiFi/btsir4dcjqZ/VPTjcCU7mbdSd92zXKBEyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wxiFi/btsir4dcjqZ/VPTjcCU7mbdSd92zXKBEyK/img.png&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;604&quot; data-filename=&quot;스크린샷 2023-06-02 오후 3.26.25.png&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.6292%;&quot; data-widthpercent=&quot;47.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wxiFi/btsir4dcjqZ/VPTjcCU7mbdSd92zXKBEyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwxiFi%2Fbtsir4dcjqZ%2FVPTjcCU7mbdSd92zXKBEyK%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;950&quot; height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Columns(세로줄)&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style10&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;&lt;b&gt;Count&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;&lt;b&gt;Color&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;&lt;b&gt;Type&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;&lt;b&gt;Width&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;&lt;b&gt;Offset&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;&lt;b&gt;Gutter&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;FF0000, 10%&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;Center&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;80&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center;&quot;&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Rows(가로줄)&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 34px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style11&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;Count&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;Color&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;Type&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;Height&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;Offset&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;Gutter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;FF0000, 10%&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;Center&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;16&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 16.6667%; text-align: center; height: 17px;&quot;&gt;16&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과물&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-05 오후 12.52.40.png&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cidhqC/btsiM9jZldw/xYjFvxsuvDYqCGlPq6uaW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cidhqC/btsiM9jZldw/xYjFvxsuvDYqCGlPq6uaW0/img.png&quot; data-alt=&quot;이제 여기에 웹을 디자인해보세요!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cidhqC/btsiM9jZldw/xYjFvxsuvDYqCGlPq6uaW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcidhqC%2FbtsiM9jZldw%2FxYjFvxsuvDYqCGlPq6uaW0%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;704&quot; height=&quot;469&quot; data-filename=&quot;스크린샷 2023-06-05 오후 12.52.40.png&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;469&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이제 여기에 웹을 디자인해보세요!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;End&lt;/p&gt;</description>
      <category>(3D)Dev Deep Dive/Templates &amp;amp; Guides</category>
      <category>Figma</category>
      <category>grid</category>
      <category>template</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/240</guid>
      <comments>https://time-map-installer.tistory.com/240#entry240comment</comments>
      <pubDate>Mon, 5 Jun 2023 12:53:16 +0900</pubDate>
    </item>
    <item>
      <title>[Dev Setting, yarn, husky] Git Commit이 무언가에 막혀서 안된다면?</title>
      <link>https://time-map-installer.tistory.com/239</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;상황 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Project:&lt;/b&gt; HQRoutine&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IDE:&lt;/b&gt; Visual Studio Code&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Environment: &lt;/b&gt;Mac M1(Apple Sillicon)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상황: &lt;/b&gt;맥북 초기화 이후 Project 리팩토링 중 &lt;b&gt;작업 branch&lt;/b&gt;에서 &lt;b&gt;git pull origin develop&lt;/b&gt;을 할 때 아래와 같은 문제 발생&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.04.02.png&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vRs4J/btsitTJRg6d/xzwKSOBpHB5vSVKJQShksk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vRs4J/btsitTJRg6d/xzwKSOBpHB5vSVKJQShksk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vRs4J/btsitTJRg6d/xzwKSOBpHB5vSVKJQShksk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvRs4J%2FbtsitTJRg6d%2FxzwKSOBpHB5vSVKJQShksk%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;300&quot; height=&quot;305&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.04.02.png&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;596&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;사실 이건 Mac을 산 지 얼마 되지 않았을 때에도 발생했던 문제라서 이렇게 기록을 남겨두면 좋겠다 싶었습니다.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1st Trying - yarn 다시 설치해 보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Git log 상태&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685801133140&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2023-06-03 23:05:13.974 [info] &amp;gt; git -c user.useConfigOnly=true commit --quiet --allow-empty-message --file - [32ms]
2023-06-03 23:05:13.974 [info] .husky/pre-commit: line 4: yarn: command not found
husky - pre-commit hook exited with code 127 (error)
husky - command not found in PATH=/Library/Developer/CommandLineTools/usr/libexec/git-core:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin
2023-06-03 23:05:13.986 [info] &amp;gt; git config --get-all user.name [11ms]
2023-06-03 23:05:13.996 [info] &amp;gt; git config --get-all user.email [9ms]
2023-06-03 23:05:14.007 [info] &amp;gt; git for-each-ref --format=%(refname)%00%(upstream:short)%00%(objectname)%00%(upstream:track)%00%(upstream:remotename)%00%(upstream:remoteref) refs/heads/chore/89 refs/remotes/chore/89 [10ms]
2023-06-03 23:05:14.019 [info] &amp;gt; git status -z -uall [12ms]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;git-error&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685801174170&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; git -c user.useConfigOnly=true commit --quiet --allow-empty-message --file -
.husky/pre-commit: line 4: yarn: command not found
husky - pre-commit hook exited with code 127 (error)
husky - command not found in PATH=/Library/Developer/CommandLineTools/usr/libexec/git-core:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin&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;위의 오류 메시지들은 `&lt;b&gt;husky&lt;/b&gt;`의 &lt;b&gt;pre-commit hook&lt;/b&gt;이 `yarn` 명령을 찾지 못해서 발생한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 PATH 설정에 &lt;b&gt;yarn&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;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;1. Yarn 설치 확인&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 yarn이 제대로 설치되어 있는지 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 `&lt;u&gt;&lt;b&gt;yarn --version&lt;/b&gt;&lt;/u&gt;`을 실행하여 yarn이 설치되어 있는지 확인합니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;2. PATH 설정 확인 &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`yarn`이 설치되어 있음에도 불구하고 해당 오류가 발생한다면, 시스템의 `PATH`에 `yarn`이 제대로 설정되어 있는지 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`echo $PATH`를 실행하여 현재 PATH를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령의 결과에 yarn의 경로가 포함되어 있는지 확인하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 yarn의 경로가 없다면, 적절한 위치에 yarn의 경로를 추가해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서는 셸의 설정 파일(`. bashrc`, `. zshrc`, `. bash_profile` 등)을 수정해야 할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;3. husky 설치 확인 및 재설치&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`&lt;b&gt;husky&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;husky&lt;/b&gt;`를 제대로 설치하려면 다음 명령을 실행합니다&lt;/p&gt;
&lt;pre id=&quot;code_1685801512655&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn add husky --dev
yarn husky install&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;VSCode에서 터미널을 열고 싶다면 (Mac) control + shift + `를 누르면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, pre-commit hook을 다시 설정합니다&lt;/p&gt;
&lt;pre id=&quot;code_1685801533373&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn husky add .husky/pre-commit 'yarn test'&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;u&gt;&lt;b&gt;실행 결과&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.17.41.png&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beTai1/btsitZ37tNf/h5FrK1P8fwjQkLm7gGSVG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beTai1/btsitZ37tNf/h5FrK1P8fwjQkLm7gGSVG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beTai1/btsitZ37tNf/h5FrK1P8fwjQkLm7gGSVG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeTai1%2FbtsitZ37tNf%2Fh5FrK1P8fwjQkLm7gGSVG1%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;1718&quot; height=&quot;762&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.17.41.png&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;762&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2nd Trying - VS Code 재시작&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 단계를 따라도 문제가 해결되지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 &lt;b&gt;Git&lt;/b&gt; &lt;b&gt;hook&lt;/b&gt;이 실행될 때 &lt;b&gt;PATH&lt;/b&gt; 설정에 &lt;b&gt;yarn&lt;/b&gt;이 포함되어 있지 않아서 발생하는 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 VS Code에서 이를 실행할 때 발생하는 문제일 수 있는데, 이는 &lt;b&gt;VSCode&lt;/b&gt;의 터미널이 별도의 셸 세션을 사용하며, 때로는 기본 셸과 다른 PATH 설정을 가질 수 있기 때문입니다.&lt;/p&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;u&gt;&lt;b&gt;VS Code 재시작&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가끔 VS Code는 새로 설치된 도구를 즉시 인식하지 못하는 경우가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS Code를 완전히 닫았다가 다시 열어보았습니다.&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;스크린샷 2023-06-03 오후 11.23.30.png&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yira3/btsis6CZFug/KoILJ7S12CJEneJiAmuAP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yira3/btsis6CZFug/KoILJ7S12CJEneJiAmuAP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yira3/btsis6CZFug/KoILJ7S12CJEneJiAmuAP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyira3%2Fbtsis6CZFug%2FKoILJ7S12CJEneJiAmuAP0%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;200&quot; height=&quot;228&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.23.30.png&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;544&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;혹시나 하는 마음에 이를 모두 스테이지하고 커밋을 눌러보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.24.19.png&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w2APS/btsitAjuwuY/KYQwv6zeR9S4Y26em22uek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w2APS/btsitAjuwuY/KYQwv6zeR9S4Y26em22uek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w2APS/btsitAjuwuY/KYQwv6zeR9S4Y26em22uek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw2APS%2FbtsitAjuwuY%2FKYQwv6zeR9S4Y26em22uek%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;300&quot; height=&quot;303&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.24.19.png&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;582&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3rd Trying - 확장 프로그램 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;git log&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685802322582&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2023-06-03 23:26:01.263 [info] &amp;gt; git -c user.useConfigOnly=true commit --quiet --allow-empty-message --file - [159ms]
2023-06-03 23:26:01.263 [info] yarn run v1.22.19
error Command &quot;formatyarn&quot; not found.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
husky - pre-commit hook exited with code 1 (error)
2023-06-03 23:26:01.273 [info] &amp;gt; git config --get-all user.name [9ms]
2023-06-03 23:26:01.282 [info] &amp;gt; git config --get-all user.email [9ms]
2023-06-03 23:26:01.293 [info] &amp;gt; git for-each-ref --format=%(refname)%00%(upstream:short)%00%(objectname)%00%(upstream:track)%00%(upstream:remotename)%00%(upstream:remoteref) refs/heads/chore/89 refs/remotes/chore/89 [10ms]
2023-06-03 23:26:01.306 [info] &amp;gt; git status -z -uall [12ms]&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;u&gt;&lt;b&gt;git-error&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685802395460&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; git -c user.useConfigOnly=true commit --quiet --allow-empty-message --file -
yarn run v1.22.19
error Command &quot;formatyarn&quot; not found.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
husky - pre-commit hook exited with code 1 (error)&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;위의 오류 메시지에 따르면 &lt;b&gt;formatyarn&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;이것은 husky의 pre-commit hook이 실행하는 스크립트 중에 formatyarn이라는 명령이 포함되어 있고, 이 명령이 프로젝트에 존재하지 않아서 발생하는 문제로 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;formatyarn이 프로젝트의 스크립트 중 하나인지, 아니면 잘못된 명령인지 확인해 보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json 파일의 scripts 섹션을 확인하면 프로젝트에서 사용하는 스크립트를 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 formatyarn이 이 목록에 없다면, 이는 잘못된 명령일 가능성이 높습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;formatyarn이 프로젝트의 스크립트 중 하나라면, 이를 실행하기 위한 모든 종속성이 설치되어 있는지 확인해 보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 이 스크립트가 prettier나 eslint 같은 도구를 사용한다면, 해당 도구들이 모두 설치되어 있어야 합니다.&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.28.56.png&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EgFz2/btsivfL9aAM/X5hadGoT769RIKQBAzZJd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EgFz2/btsivfL9aAM/X5hadGoT769RIKQBAzZJd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EgFz2/btsivfL9aAM/X5hadGoT769RIKQBAzZJd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEgFz2%2FbtsivfL9aAM%2FX5hadGoT769RIKQBAzZJd1%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;522&quot; height=&quot;388&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.28.56.png&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해 보니 ESLint가 설치되어 있지 않았군요. 설치해 보고 다시 실행해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 아직 오류가 해결되지 않았습니다. 계속 확인해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 &lt;b&gt;formatyarn&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;format&lt;/b&gt;과 &lt;b&gt;yarn&lt;/b&gt;이 분리된 명령어라면,. husky/pre-commit 파일에서 이를 분리해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.31.14.png&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceXs6S/btsiBNnL1bR/jKB9U6PqEGI385hAdrM7dK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceXs6S/btsiBNnL1bR/jKB9U6PqEGI385hAdrM7dK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceXs6S/btsiBNnL1bR/jKB9U6PqEGI385hAdrM7dK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceXs6S%2FbtsiBNnL1bR%2FjKB9U6PqEGI385hAdrM7dK%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;672&quot; height=&quot;370&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.31.14.png&quot; data-origin-width=&quot;672&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;위 파일의 4번째 줄을 줄 바꿈을 통해 분리하면 format과 test 스크립트가 순차적으로 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이는 &lt;b&gt;format&lt;/b&gt;과 &lt;b&gt;test&lt;/b&gt; 스크립트가 &lt;b&gt;package.json&lt;/b&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;b&gt;yarn&lt;/b&gt; &lt;b&gt;install&lt;/b&gt;을 다시 실행하여 모든 변경 사항을 적용하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 &lt;b&gt;husky&lt;/b&gt;의 &lt;b&gt;pre-commit hook&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;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.32.09.png&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czVcHQ/btsitzrpnCm/hBEsOtI70iMkdFouYMXk9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czVcHQ/btsitzrpnCm/hBEsOtI70iMkdFouYMXk9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czVcHQ/btsitzrpnCm/hBEsOtI70iMkdFouYMXk9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczVcHQ%2FbtsitzrpnCm%2FhBEsOtI70iMkdFouYMXk9k%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;300&quot; height=&quot;295&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.32.09.png&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;572&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4th Trying - pre-commit 파일 수정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;pre-commit&lt;/b&gt; 파일에 있는 명령이 올바르다면, 이는 &lt;b&gt;yarn&lt;/b&gt; &lt;b&gt;format&lt;/b&gt;과 &lt;b&gt;yarn&lt;/b&gt; &lt;b&gt;test&lt;/b&gt;가 &lt;b&gt;package.json&lt;/b&gt; 파일의 &lt;b&gt;scripts&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;package.json&lt;/b&gt; 파일을 열어보면 &lt;b&gt;scripts&lt;/b&gt; 섹션이 다음과 같이 표시되어야 합니다&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
    &quot;format&quot;: &quot;명령어&quot;,
    &quot;test&quot;: &quot;명령어&quot;,
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;b&gt;format&lt;/b&gt;&quot;과 &quot;&lt;b&gt;test&lt;/b&gt;&quot; 스크립트는 프로젝트에 맞게 설정되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &lt;b&gt;format&lt;/b&gt; 스크립트는 프로젝트의 코드 포맷팅을 담당하고, &lt;b&gt;test&lt;/b&gt; 스크립트는 프로젝트의 테스트를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 스크립트들이 없다면, &lt;b&gt;yarn&lt;/b&gt; &lt;b&gt;format&lt;/b&gt;과 &lt;b&gt;yarn&lt;/b&gt; &lt;b&gt;test&lt;/b&gt; 명령은 작동하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blcSsw/btsivREjqps/Kc6nGEQnT7wnWO5LrkquEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blcSsw/btsivREjqps/Kc6nGEQnT7wnWO5LrkquEk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;298&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.41.45.png&quot; style=&quot;width: 35.2183%; margin-right: 10px;&quot; data-widthpercent=&quot;35.63&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blcSsw/btsivREjqps/Kc6nGEQnT7wnWO5LrkquEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblcSsw%2FbtsivREjqps%2FKc6nGEQnT7wnWO5LrkquEk%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;672&quot; height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oxs0d/btsisReKzt8/KJYZYgmpyAO68vWbzdHd3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oxs0d/btsisReKzt8/KJYZYgmpyAO68vWbzdHd3k/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;136&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.42.35.png&quot; style=&quot;width: 63.6189%;&quot; data-widthpercent=&quot;64.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oxs0d/btsisReKzt8/KJYZYgmpyAO68vWbzdHd3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foxs0d%2FbtsisReKzt8%2FKJYZYgmpyAO68vWbzdHd3k%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;554&quot; height=&quot;136&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;package.json&lt;/b&gt; 파일로 가보았는데 말 그대로 &lt;b&gt;test&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;package.json&lt;/b&gt;에 없다면, 이를 추가하거나, 이미 존재하는 스크립트 이름으로 &lt;b&gt;pre-commit&lt;/b&gt; 파일을 업데이트해야 합니다. 그런 다음 &lt;b&gt;yarn install&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;만약 format이나 test 스크립트가 필요하지 않다면, 이를 pre-commit 파일에서 제거할 수도 있습니다. &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;u&gt;그래서 &lt;b&gt;yarn test&lt;/b&gt; 부분을 제거하고 다시 커밋을 진행해 보았습니다.&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mk2Pi/btsizeeRPgw/3wPUZ97dbeHlFFQybkHuMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mk2Pi/btsizeeRPgw/3wPUZ97dbeHlFFQybkHuMk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;488&quot; data-origin-height=&quot;174&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.40.02.png&quot; style=&quot;width: 34.0625%; margin-right: 10px;&quot; data-widthpercent=&quot;34.46&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mk2Pi/btsizeeRPgw/3wPUZ97dbeHlFFQybkHuMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmk2Pi%2FbtsizeeRPgw%2F3wPUZ97dbeHlFFQybkHuMk%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;488&quot; height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqQyrC/btsivgYyV6A/f9cvO5OKNkS9XSo0UNRPzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqQyrC/btsivgYyV6A/f9cvO5OKNkS9XSo0UNRPzK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;120&quot; data-filename=&quot;스크린샷 2023-06-03 오후 11.45.07.png&quot; style=&quot;width: 64.7747%;&quot; data-widthpercent=&quot;65.54&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqQyrC/btsivgYyV6A/f9cvO5OKNkS9XSo0UNRPzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqQyrC%2FbtsivgYyV6A%2Ff9cvO5OKNkS9XSo0UNRPzK%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;640&quot; height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;성공인가요? 성공이에요!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에도 오래 걸리면 어쩌지 싶었던 부분이 드디어 끝났습니다!&lt;br /&gt;&lt;br /&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;이런 습관은 계속 유지해야겠습니다.&lt;/p&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;TroubleShooting을 종료합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>(3D)Dev Deep Dive/TroubleShootings</category>
      <category>Husky</category>
      <category>Troubleshooting</category>
      <category>Yarn</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/239</guid>
      <comments>https://time-map-installer.tistory.com/239#entry239comment</comments>
      <pubDate>Sun, 4 Jun 2023 00:44:53 +0900</pubDate>
    </item>
    <item>
      <title>[Dev Setting, Mariadb/Mysql] local DB 설치과정 중 NIMBY 현상 발생</title>
      <link>https://time-map-installer.tistory.com/238</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3200&quot; data-origin-height=&quot;2128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NXAgB/btsivgD8cEB/DPrMQj2i9kmnIQHmO3RRc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NXAgB/btsivgD8cEB/DPrMQj2i9kmnIQHmO3RRc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NXAgB/btsivgD8cEB/DPrMQj2i9kmnIQHmO3RRc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNXAgB%2FbtsivgD8cEB%2FDPrMQj2i9kmnIQHmO3RRc0%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;399&quot; data-origin-width=&quot;3200&quot; data-origin-height=&quot;2128&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아마 이 글에서 NIMBY는 이런 뜻일 겁니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;N&lt;/b&gt;&lt;/u&gt;ow&lt;br /&gt;&lt;u&gt;&lt;b&gt;I&lt;/b&gt;&lt;/u&gt; am going to tell you about how I fixed error about setting&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;M&lt;/b&gt;&lt;/u&gt;ysql and mariadb in local environment.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;B&lt;/b&gt;&lt;/u&gt;ut I can't solve this problem. so reset&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;Y&lt;/b&gt;&lt;/u&gt;our computer&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2023 Series&lt;/h2&gt;
&lt;figure id=&quot;og_1685782299594&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;[ 2023 ] CheckPoint, 2023년&quot; data-og-description=&quot;2023년 동안 작성했던 의미있다고 생각하는 포스팅들을 모아 둔 게시글입니다 이 글은 2023년이 끝날 때까지 계속해서 업데이트 해 나갈 예정입니다 [ January ] - 변화의 시작, 1월 더보기 [2022WinterBoo&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/163&quot; data-og-url=&quot;https://time-map-installer.tistory.com/163&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bLyb8r/hySQCFLoqD/rHQLWO67ENpOfxb1GgXp70/img.jpg?width=640&amp;amp;height=376&amp;amp;face=0_0_640_376,https://scrap.kakaocdn.net/dn/BMmUS/hySRVRfmFo/zBkXM8CN7LlGSyd3jmNqI0/img.jpg?width=640&amp;amp;height=376&amp;amp;face=0_0_640_376&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/163&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/163&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bLyb8r/hySQCFLoqD/rHQLWO67ENpOfxb1GgXp70/img.jpg?width=640&amp;amp;height=376&amp;amp;face=0_0_640_376,https://scrap.kakaocdn.net/dn/BMmUS/hySRVRfmFo/zBkXM8CN7LlGSyd3jmNqI0/img.jpg?width=640&amp;amp;height=376&amp;amp;face=0_0_640_376');&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;[ 2023 ] CheckPoint, 2023년&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2023년 동안 작성했던 의미있다고 생각하는 포스팅들을 모아 둔 게시글입니다 이 글은 2023년이 끝날 때까지 계속해서 업데이트 해 나갈 예정입니다 [ January ] - 변화의 시작, 1월 더보기 [2022WinterBoo&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 정의 - 왜 내 local 환경은 DB를 거부하는가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;local 환경:&lt;/b&gt; M1, MacBook-PRO&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설치 환경:&lt;/b&gt; homebrew&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Terminal:&lt;/b&gt; oh-my-zsh, iTerm2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제:&lt;/b&gt; mysql이나 mariadb 설치오류&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전까지 약 한 달 가량 문제 지속, 프론트엔드 엔지니어링에 DB가 많이 필요한 건 아니라서 문제가 없었지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학교 프로젝트에서 로컬 DB가 필수적이므로 문제 해결을 위한 TroubleShooting을 작성합니다.&lt;/p&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;u&gt;&lt;b&gt;작성 시점 이전까지 해 본 시도들은 다음과 같습니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;remove, install로 db 재설치, &lt;/b&gt;&lt;code&gt;&lt;b&gt;실패&lt;/b&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;brew 삭제 후 cleanup 한 후 재설치, &lt;code&gt;실패&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;/opt/homebrew/var/mysql&lt;/code&gt; 디렉터리 삭제 후 재설치, &lt;code&gt;실패&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;/opt/homebrew/var/mysql&lt;/code&gt; 삭제한 후 재실행, &lt;code&gt;실패&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;sudo 이용한 설치, &lt;code&gt;실패&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;mariadb나 mysql 중 하나만 깔아두고 실행해보기, &lt;code&gt;실패&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;httpd 설치 후 mysql 실행, &lt;code&gt;실패&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;chmod 이용한 mysql 권한 변경 -&amp;gt; 적용된 권한이 보이지 않음, &lt;code&gt;실패&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;/opt/homebrew/var/mysql&lt;/code&gt; 삭제 후, &lt;code&gt;brew postinstall&lt;/code&gt; 을 통해 설치해보기, &lt;code&gt;실패&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;homebrew 제거, 캐시 제거 후 &lt;code&gt;/opt/hombrew/var/&lt;/code&gt; 내부 디렉터리 전체 제거 이후 homebrew 재설치 이후 db 설치, &lt;code&gt;실패&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&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;로컬 db를 설치해야하는데 설치 자체가 안되는 건지 workbench를 사용해도 접속이 안되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날잡고 36시간동안 고군분투하고 결국에는 성공한 이 TroubleShooting을 문서화하여 공유합니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;GPT Plus의 GPT4와 여러 글들, 그리고 지인분들께 방법에 대한 조언을 듣고 얻은 모든 방법을 동원해 보았습니다.&lt;br /&gt;결국은 포맷이었지만, 포맷은 너무나도 오래 걸리고 다시 세팅하기까지 너무나도 귀찮기에 다른 방법들을 먼저 활용해보는 것도 괜찮을 것&amp;nbsp; 같습니다.&lt;br /&gt;&lt;br /&gt;+ 인터넷 화면 크기를 줄이고 사이드 shortcut 기능을 이용하면 더 빠르게 원하는 곳으로 이동하실 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&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;&lt;code&gt;주요 상황은 다음과 같습니다.&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;중요: brew service start mariadb/mysql 을 통해 서버를 실행함과 동시에 stopped로 바뀌는 현상&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 부분을 해결했다면 정상적으로 실행할 수 있을 듯 한데 거의 모든 과정의 마지막에서 이 현상이 공통적으로 나타났습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;커맨드를 통해 파일 mysql 관련 파일을 확인해보면 출력되지만 finder에서는 나타나지 않는 현상&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;권한 설정을 해 두어도 실제 권한 목록에 mysql이 나타나지 않는 현상mysql을 삭제하고 재설치해도 관련 디렉터리들이 나타나지 않는 현상(brew 재설치 시에도 같은 현상 나타남)&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;그리고 주요 에러 메세지들은 다음과 같았습니다.&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;subunit&quot;&gt;&lt;code&gt;Error: Failure while executing; `/bin/launchctl bootstrap gui/501 /Users/tminstaller/Library/LaunchAgents/homebrew.mxcl.mariadb.plist` exited with 5.

// 혹은

ERROR 2002 (HY000): Can't connect to local server through socket '/tmp/mysql.sock' (2)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1st Trying - DB 재설치, 소켓 파일 위치 설정 변경, 버전 확인&lt;code&gt;&lt;/code&gt;&lt;/h2&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;MySQL, MariaDB 완전히 제거 후 재설치:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재까지 시도한 방법 외에도 완전한 삭제가 필요할 수 있습니다.&lt;/li&gt;
&lt;li&gt;이를 위해선 MySQL 혹은 MariaDB와 관련된 모든 파일 및 디렉토리를 수동으로 삭제해야 할 수도 있습니다.
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt; brew remove mysql
 brew cleanup
 sudo rm /usr/local/mysql
 sudo rm -rf /usr/local/var/mysql
 sudo rm -rf /Library/StartupItems/MySQLCOM
 sudo rm -rf /Library/PreferencePanes/My*
 sudo rm -rf ~/Library/PreferencePanes/My*
 sudo rm -rf /Library/Receipts/mysql*
 sudo rm -rf /Library/Receipts/MySQL*
 sudo rm -rf /var/db/receipts/com.mysql.*&lt;/code&gt;&lt;/pre&gt;
그 후 재설치를 시도해 보세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아니면 아래의 명령들을 차례대로 실행해보세요.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;소켓 파일 위치 설정 변경&lt;/b&gt;
&lt;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;code&gt;my.cnf&lt;/code&gt; 파일에 다음과 같은 설정을 추가해 보세요.&lt;/li&gt;
&lt;li&gt;그 후 MySQL 혹은 MariaDB를 재시작해 보세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt; [mysqld]
 socket=/tmp/mysql.sock&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MySQL 설정 파일 문제&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MySQL 설정 파일이 잘못된 경우에도 비슷한 문제가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/my.cnf&lt;/code&gt;, &lt;code&gt;/etc/mysql/my.cnf&lt;/code&gt; 또는 &lt;code&gt;~/.my.cnf&lt;/code&gt;에 있는 설정 파일이 올바른지 확인해 보세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Homebrew 버전&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;또한 Homebrew의 최신 버전을 사용하고 있는지 확인해 보세요.&lt;/li&gt;
&lt;li&gt;이전 버전의 Homebrew를 사용하고 있다면 문제가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;아래의 명령어로 Homebrew를 업데이트 해보세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt; brew update
 brew upgrade&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도 시스템 로그를 확인해 보는 것도 좋을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;/var/log/system.log&lt;/code&gt;에 접근해 MySQL 혹은 MariaDB와 관련된 로그를 확인해 보세요.&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;모든 것이 실패한다면, Docker와 같은 가상화 도구를 사용해 MySQL 혹은 MariaDB를 설치하는 것도 고려해 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 시스템의 다른 부분과 격리되어 실행되므로 문제를 해결할 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 부분에서 따로 해결되는 부분은 없없습니다. 그래서 두 번째 시도로 넘어갔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;2nd Trying - 로그 파일 확인해보기, Mac 컴퓨터 이름 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Homebrew를 통해 설치한 MariaDB의 경우, 로그 파일은 보통 &lt;code&gt;/usr/local/var/mysql/&lt;/code&gt; 디렉토리에 위치해 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경로는 MariaDB가 설치된 위치에 따라 다를 수 있으니, 설치 경로를 확인해 보시는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB의 에러 로그를 보기 위해서는 Terminal을 열고 아래와 같은 명령어를 입력하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;tail -f /usr/local/var/mysql/your_computer_name.local.err&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;code&gt;your_computer_name.local.err&lt;/code&gt;는 MariaDB의 에러 로그 파일명입니다.&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;tail -f&lt;/code&gt; 명령은 위에서 설명한 것과 같이 파일의 마지막 부분을 실시간으로 출력해주는 명령어입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 로그 파일에는 MariaDB를 실행하거나 종료할 때 발생하는 에러 메시지 또는 MariaDB의 동작에 관한 정보가 저장되어 있으므로, 문제의 원인을 찾는 데 도움이 될 것입니다.&lt;/p&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;pre class=&quot;stata&quot;&gt;&lt;code&gt;tail: /usr/local/var/mysql/TMInstaller.local.err: No such file or directory&lt;/code&gt;&lt;/pre&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;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;Mac에서 컴퓨터 이름을 확인하는 방법은 다음과 같습니다.&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;System Preferences (시스템 환경설정)을 통해 확인하는 방법:&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Apple 메뉴(화면 왼쪽 상단에 있는  아이콘)을 클릭합니다.&lt;/li&gt;
&lt;li&gt;&quot;&lt;b&gt;System Preferences&lt;/b&gt;&quot;를 선택합니다.&lt;/li&gt;
&lt;li&gt;&quot;&lt;b&gt;Sharing&lt;/b&gt;&quot; 아이콘을 클릭합니다.&lt;/li&gt;
&lt;li&gt;&quot;&lt;b&gt;Computer Name&lt;/b&gt;&quot; 필드에 표시된 이름이 현재 Mac 컴퓨터의 이름입니다.&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;또는,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Terminal (터미널)을 통해 확인하는 방법:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Terminal 애플리케이션을 실행하고 다음과 같은 명령어를 입력합니다:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;hostname&lt;/code&gt;&lt;/pre&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;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;$(yourhostname).local&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 제 hostname이 TimeMap이라면 결과는 TimeMap.local이라고 나오게 되는 것이죠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;추가적인 문제 발생&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;/usr/local/var directory not found&lt;/code&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;&lt;code&gt;/usr/local/var&lt;/code&gt; 디렉터리가 없는 경우, Homebrew의 설치가 올바르게 이루어지지 않았을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 Homebrew는 &lt;code&gt;/usr/local/var&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;디렉터리를 직접 만드는 방법도 있지만, 그 전에 Homebrew 설치 상태를 확인하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Homebrew 설치 상태 확인을 위해서는 터미널에서 다음 명령어를 실행해보세요&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;brew doctor&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어는 Homebrew의 설치 상태를 점검하고 문제가 있는 경우, 그 해결 방법을 안내해줍니다.&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;2nd&amp;nbsp;Trying&amp;nbsp;-&amp;nbsp;Result,&amp;nbsp;환경변수에&amp;nbsp;디렉터리&amp;nbsp;추가하기&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Warning: Homebrew's &quot;sbin&quot; was not found in your PATH but you have installed
formulae that put executables in /opt/homebrew/sbin.
Consider setting your PATH for example like so:
echo 'export PATH=&quot;/opt/homebrew/sbin:$PATH&quot;' &amp;gt;&amp;gt; ~/.zshrc&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;Homebrew가 설치한 일부 프로그램이 실행되려면, 해당 프로그램의 실행 파일이 위치한 디렉토리를 &lt;code&gt;PATH&lt;/code&gt; 환경 변수에 추가해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;PATH&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;&lt;b&gt;위 경고 메세지는 Homebrew가 설치한 일부 프로그램의 실행 파일이 &lt;code&gt;/opt/homebrew/sbin&lt;/code&gt;에 위치해 있으므로, 이 디렉토리를 &lt;code&gt;PATH&lt;/code&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;PATH&lt;/code&gt; 환경 변수에 &lt;code&gt;/opt/homebrew/sbin&lt;/code&gt; 디렉토리를 추가하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널을 열고 아래의 명령어를 실행해보았습니다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;echo 'export PATH=&quot;/opt/homebrew/sbin:$PATH&quot;' &amp;gt;&amp;gt; ~/.zshrc&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;이 명령어는 현재 사용자의 Zsh 셸 설정 파일인 &lt;code&gt;~/.zshrc&lt;/code&gt;에 &lt;code&gt;PATH&lt;/code&gt; 환경 변수 설정을 추가하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Bash 셸을 사용하고 있다면, 대신 &lt;code&gt;~/.bash_profile&lt;/code&gt; 파일에 같은 설정을 추가해야 합니다&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;echo 'export PATH=&quot;/opt/homebrew/sbin:$PATH&quot;' &amp;gt;&amp;gt; ~/.bash_profile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하고 나서 새로운 터미널 창을 열거나, 현재 터미널 세션에서 다음 명령어를 실행하여 변경한 설정을 적용하세요:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;source ~/.zshrc     # Zsh를 사용하는 경우
source ~/.bash_profile  # Bash를 사용하는 경우&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;code&gt;/opt/homebrew/sbin&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;&lt;b&gt;위 방법대로 하였을 때 더 이상 아까의 에러메세지가 나타나지 않았습니다. 우선 여기는 성공했군요.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위의 명령어가 특별한 문제점을 찾지 못했거나, 여전히 &lt;code&gt;/usr/local/var&lt;/code&gt; 디렉터리가 없는 경우, 직접 디렉터리를 생성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어를 터미널에 입력하세요:&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;sudo mkdir -p /usr/local/var&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;➜  ~ brew reinstall mariadb

➜  ~ brew services
Name    Status User File
mariadb none
➜  ~ brew services start mariadb
==&amp;gt; Successfully started `mariadb` (label: homebrew.mxcl.mariadb)
➜  ~ mariadb -u root
ERROR 2002 (HY000): Can't connect to local server through socket '/tmp/mysql.sock' (2)&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;hr data-ke-style=&quot;style1&quot; /&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;3rd Trying - my.cnf 파일 수정해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ERROR 2002 (HY000): Can't connect to local server through socket '/tmp/mysql.sock' (2)&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;에러는 MySQL 혹은 MariaDB 서버가 실행되지 않았을 때 발생하는 에러입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선적으로 MariaDB 혹은 MySQL 서버가 실행되고 있는지 확인해 보세요. Terminal에서 아래 명령어를 입력하여 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;brew services list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 예상대로 멈춰있는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Name    Status  User        File
mariadb stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist&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;이 명령어는 Homebrew를 통해 설치한 서비스들의 현재 상태를 보여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB 혹은 MySQL 서비스가 실행되지 않았으므로, 아래 명령어를 통해 실행해 보았습니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;brew services start mariadb
# 혹은
brew services start mysql&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;위 명령어를 실행한 후 다시 MariaDB 혹은 MySQL 서버에 접속해 보았습니다.&lt;/p&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;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;➜  ~ brew services start mariadb
Bootstrap failed: 5: Input/output error
Try re-running the command as root for richer errors.
Error: Failure while executing; `/bin/launchctl bootstrap gui/501 /Users/tminstaller/Library/LaunchAgents/homebrew.mxcl.mariadb.plist` exited with 5.&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 data-ke-size=&quot;size16&quot;&gt;만약 위 방법으로도 해결되지 않는다면, &lt;code&gt;my.cnf&lt;/code&gt; 설정 파일에서 &lt;code&gt;socket&lt;/code&gt; 설정이 올바른지 확인해볼 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB 혹은 MySQL에서 사용하는 소켓 파일 경로가 실제로 존재하는지, 그리고 해당 소켓 파일에 대한 권한이 적절한지 확인해 보세요.&lt;/p&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;my.cnf&lt;/code&gt; 파일은 &lt;code&gt;/etc/&lt;/code&gt; 혹은 &lt;code&gt;/usr/local/etc/&lt;/code&gt; 디렉터리에 위치해 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 &lt;code&gt;my.cnf&lt;/code&gt; 파일의 &lt;code&gt;socket&lt;/code&gt; 설정을 확인하거나 수정할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;[client]
#socket=/var/mysql/mysql.sock
socket=/tmp/mysql.sock

[mysqld]
#socket=/var/mysql/mysql.sock
socket=/tmp/mysql.sock&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 설정을 확인한 후에도 여전히 문제가 발생한다면, MariaDB 혹은 MySQL을 완전히 삭제한 후 재설치하는 것을 고려해볼 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;TIP - Finder에서 폴더 검색하기, M1은 다른 경로에 있어요!&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;finder에서 폴더를 검색하려면 &lt;code&gt;command + shift + g&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;&lt;b&gt;놀랍게도 /usr/local/etc/ 디렉터리가 존재하지 않았습니다.&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;Homebrew가 설치되는 위치는 Mac의 M1 칩셋에서 변경되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;M1 칩셋을 사용하는 경우 Homebrew는 &lt;code&gt;/opt/homebrew/&lt;/code&gt; 디렉터리에 설치되며, 설정 파일들은 &lt;code&gt;/opt/homebrew/etc/&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;따라서, 위의 파일 경로 대신 &lt;code&gt;/opt/homebrew/etc/&lt;/code&gt; 디렉터리에 &lt;code&gt;my.cnf&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;아래 명령어를 사용하면 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;ls /opt/homebrew/etc/my.cnf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;➜  ~ ls /opt/homebrew/ets/my.cnf
ls: /opt/homebrew/ets/my.cnf: No such file or directory&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;하지만 finder에서 직접 찾아가보았더니 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 이상하네요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;! 앗 오타가 났었습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;➜  ~ ls /opt/homebrew/etc/my.cnf
/opt/homebrew/etc/my.cnf&lt;/code&gt;&lt;/pre&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;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# Default Homebrew MySQL server config
[mysqld]
# Only allow connections from localhost
bind-address = 127.0.0.1
mysqlx-bind-address = 127.0.0.1&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;pre class=&quot;ini&quot;&gt;&lt;code&gt;[client]
socket=/tmp/mysql.sock

[mysqld]
socket=/tmp/mysql.sock&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 설정을 &lt;code&gt;/opt/homebrew/etc/my.cnf&lt;/code&gt; 파일에 적용하고, MariaDB 혹은 MySQL을 다시 시작해보라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에도 문제가 발생한다면, MariaDB 혹은 MySQL의 로그 파일을 확인하여 문제의 원인을 찾아볼 수 있습니다.&lt;/p&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;Result&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 매우 편리한 문서 편집기로 VSCode를 선택했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode로 해당 파일을 열고, 편집하고, 저장하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;# Default Homebrew MySQL server config
[client]
socket=/tmp/mysql.sock
[mysqld]
socket=/tmp/mysql.sock
# Only allow connections from localhost
bind-address = 127.0.0.1
mysqlx-bind-address = 127.0.0.1&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;터미널을 재실행 하고 실행시켜보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;➜  ~ brew services list
Name    Status  User        File
mariadb stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist
➜  ~ brew services start mariadb
Service `mariadb` already started, use `brew services restart mariadb` to restart.
➜  ~ brew services restart mariadb
Stopping `mariadb`... (might take a while)
==&amp;gt; Successfully stopped `mariadb` (label: homebrew.mxcl.mariadb)
==&amp;gt; Successfully started `mariadb` (label: homebrew.mxcl.mariadb)
➜  ~ brew services list
Name    Status  User        File
mariadb stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시나 실행은 잘 되지만 바로 직후에 status가 stopped로 바뀌는 현상은 해결되지 않았습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;4th Trying - 로그 파일 확인, my.cnf 수정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;M1 칩이라는 것을 질문할 때 명시해야할 것 같습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 종류에 따라 경로가 다르네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;M1 칩셋의 Mac에서 MariaDB가 바로 종료되는 문제는 Homebrew와 MariaDB의 호환성 문제일 수 있다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우 MariaDB가 왜 종료되었는지 이유를 파악하기 위해 로그 파일을 확인해볼 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB의 로그 파일 위치는 설정에 따라 다르지만, 기본적으로 &lt;code&gt;/opt/homebrew/var/mysql/컴퓨터이름.local.err&lt;/code&gt;에 위치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어를 통해 로그 파일을 확인해 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;cat /opt/homebrew/var/mysql/$(hostname).err&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그 파일 내용을 그대로 분석 의뢰했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;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;로그 분석 결과&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB 서버를 시작하는 동안에 두 가지 주요한 오류가 발생하였습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;&quot;Table 'mysql.plugin' doesn't exist&quot;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;이 메시지는 'mysql.plugin' 테이블이 존재하지 않는다는 것을 의미합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 테이블은 MariaDB에서 사용되는 플러그인 정보를 저장합니다.&lt;/li&gt;
&lt;li&gt;이 테이블이 없으면 일부 플러그인이 로드되지 않을 수 있습니다.&lt;/li&gt;
&lt;li&gt;이 문제를 해결하려면 MariaDB 설치를 확인하거나 다시 설치할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&quot;unknown variable 'mysqlx-bind-address=127.0.0.1'&quot;&lt;/li&gt;
&lt;li&gt;이 메시지는 'mysqlx-bind-address'라는 알 수 없는 변수가 있음을 나타냅니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'mysqlx-bind-address'는 MySQL 8.0 이상에서 MySQL X 프로토콜을 위한 설정 변수입니다.&lt;/li&gt;
&lt;li&gt;그러나 이 변수는 MariaDB에서 지원되지 않습니다.&lt;/li&gt;
&lt;li&gt;MariaDB에서는 'bind-address'라는 설정만 지원됩니다. 따라서&lt;/li&gt;
&lt;li&gt;'mysqlx-bind-address' 설정을 제거해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 문제를 해결하려면 MariaDB 설치를 다시 확인하거나 필요한 경우 재설치하십시오.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 'mysqlx-bind-address' 설정을 my.cnf 또는 MariaDB의 설정 파일에서 제거하십시오.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 가지 변경 후에 MariaDB를 다시 시작하십시오.&lt;/p&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;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;# Default Homebrew MySQL server config
[client]
socket=/tmp/mysql.sock
[mysqld]
socket=/tmp/mysql.sock
# Only allow connections from localhost
bind-address = 127.0.0.1
# mysqlx-bind-address = 127.0.0.1 &amp;lt;- 이 부분 제거&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;pre class=&quot;routeros&quot;&gt;&lt;code&gt;➜  ~ brew services list
Name    Status  User        File
mariadb stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist
➜  ~ brew services restart mariadb
Stopping `mariadb`... (might take a while)
==&amp;gt; Successfully stopped `mariadb` (label: homebrew.mxcl.mariadb)
==&amp;gt; Successfully started `mariadb` (label: homebrew.mxcl.mariadb)
➜  ~ brew services list
Name    Status  User        File
mariadb stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist&lt;/code&gt;&lt;/pre&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&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;5th Trying - DB 재 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해보니 MariaDB 서비스가 제대로 시작되지 않고 있는 것으로 보였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 방법을 사용하여 문제를 해결을 시도해보았습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;설치 재검토&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MariaDB가 제대로 설치되지 않았을 수 있습니다.&lt;/li&gt;
&lt;li&gt;Homebrew를 사용하여 MariaDB를 재설치해보세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그러나 이 작업을 수행하기 전에 데이터 백업을 해두는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Homebrew를 사용하여 MariaDB를 재설치하는 방법은 다음과 같습니다&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;  brew remove mariadb
&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;  brew cleanup
&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;  brew install mariadb&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;➜  ~ brew remove mariadb
Uninstalling /opt/homebrew/Cellar/mariadb/10.11.3... (928 files, 210.7MB)
➜  ~ brew cleanup
Pruned 0 symbolic links and 2 directories from /opt/homebrew
➜  ~ brew install mariadb
# successed
➜  ~ brew services start mariadb
Bootstrap failed: 5: Input/output error
Try re-running the command as root for richer errors.

Error: Failure while executing; `/bin/launchctl bootstrap gui/501 /Users/tminstaller/Library/LaunchAgents/homebrew.mxcl.mariadb.plist` exited with 5.

➜  ~ brew services restart mariadb
Stopping `mariadb`... (might take a while)
==&amp;gt; Successfully stopped `mariadb` (label: homebrew.mxcl.mariadb)
==&amp;gt; Successfully started `mariadb` (label: homebrew.mxcl.mariadb)
➜  ~ brew services list
Name    Status  User        File
mariadb stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;subunit&quot;&gt;&lt;code&gt;Error: Failure while executing; `/bin/launchctl bootstrap gui/501 /Users/tminstaller/Library/LaunchAgents/homebrew.mxcl.mariadb.plist` exited with 5.&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;hr data-ke-style=&quot;style1&quot; /&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;6th Trying - Homebrew를 이용하지 않고 수동으로 시작해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제는 Homebrew에서 MariaDB 서비스를 관리하려고 할 때 발생하는 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Homebrew 대신 MariaDB 서버를 수동으로 시작해 보시는 것은 어떨까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 다음의 명령어를 사용하였습니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;mysql.server start&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;➜  ~ mysql.server start
Starting MariaDB
230602 14:47:18 mysqld_safe Logging to '/opt/homebrew/var/mysql/{your-computer-name}.local.err'.
230602 14:47:18 mysqld_safe Starting mariadbd daemon with databases from /opt/homebrew/var/mysql
./opt/homebrew/bin/mysql.server: line 264: kill: (18771) - No such process
 ERROR!&lt;/code&gt;&lt;/pre&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;혹시몰라 다음과 같이 MariaDB 서버의 상태를 확인해 보았습니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;mysqladmin version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령은 MariaDB 서버가 현재 실행 중인지, 그리고 어떤 설정이 적용되어 있는지 등의 정보를 제공합니다.&lt;/p&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;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;➜  ~ mysqladmin version
mysqladmin: connect to server at 'localhost' failed
error: 'Can't connect to local server through socket '/tmp/mysql.sock' (2)'
Check that mariadbd is running and that the socket: '/tmp/mysql.sock' exists!&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;hr data-ke-style=&quot;style1&quot; /&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;7th Trying - DB 재 설치, 서버 수동 시작&lt;/h2&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;아래의 명령을 사용하여 MariaDB를 재설치해 보았습니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;brew uninstall mariadb
brew cleanup
brew install mariadb&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 class=&quot;pgsql&quot;&gt;&lt;code&gt;mysql.server start&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 단계를 완료한 후에는 다시 &lt;code&gt;mysqladmin version&lt;/code&gt; 명령을 실행하여 MariaDB 서버의 상태를 확인해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB 서버가 제대로 시작되었는지 확인할 수 있습니다.&lt;/p&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;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;➜  ~ mysql.server start
Starting MariaDB
230602 14:53:18 mysqld_safe Logging to '/opt/homebrew/var/mysql/{your-computer-name}.local.err'.
230602 14:53:18 mysqld_safe Starting mariadbd daemon with databases from /opt/homebrew/var/mysql
./opt/homebrew/bin/mysql.server: line 264: kill: (22816) - No such process
 ERROR!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 제대로 시작되었다면 8번째 시도는 없었겠죠?&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8th Trying - MariaDB 대신 MySQL 사용해보기&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;만약 여전히 문제가 발생한다면, MariaDB 설치와 관련된 문제를 해결하기 위해 다른 방법을 시도해 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중 하나는 MariaDB 대신 MySQL을 사용하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB는 MySQL의 포크이므로, 두 시스템은 매우 유사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL은 다음의 명령으로 설치할 수 있습니다&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;brew install mysql&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;그리고 다음의 명령으로 MySQL 서비스를 시작할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;brew services start mysql&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;subunit&quot;&gt;&lt;code&gt;Error: Cannot install mysql because conflicting formulae are installed.
  mariadb: because mysql, mariadb, and percona install the same binaries

Please `brew unlink mariadb` before continuing.

Unlinking removes a formula's symlinks from /opt/homebrew. You can
link the formula again after the install finishes. You can --force this
install, but the build may fail or cause obscure side effects in the
resulting software.&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;여러 DB가 충돌이 날 때에는? 하나를 Unlink 해보자!&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB가 이미 설치되어 있어서 MySQL 설치가 충돌하는 상황이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하려면 먼저 MariaDB를 언링크해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB를 언링크해 보세요&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;brew unlink mariadb&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;그리고 나서 MySQL을 설치하실 수 있습니다&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;brew install mysql&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;그 후, MySQL 서비스를 시작합니다&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;brew services start mysql&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;이 단계들을 거치면 MySQL을 사용하실 수 있을 것입니다.&lt;/p&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;하지만 MariaDB를 사용하려 한다면, MariaDB를 다시 링크해야 합니다&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;brew link mariadb&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;그런 다음 MariaDB 서비스를 다시 시작합니다&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;brew services start mariadb&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;동시에 두 시스템을 실행하면 충돌이 발생할 수 있으므로, 한 번에 하나의 시스템만 사용하는 것이 좋습니다.&lt;/p&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;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;➜  ~ brew services start mysql
==&amp;gt; Successfully started `mysql` (label: homebrew.mxcl.mysql)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;와 이 때 설마했습니다&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;➜  ~ mysql -u root
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
➜  ~ brew services list
Name    Status  User        File
mariadb stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist
mysql   stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist
➜  ~ brew services start mysql
Bootstrap failed: 5: Input/output error
Try re-running the command as root for richer errors.
Error: Failure while executing; `/bin/launchctl bootstrap gui/501 /Users/tminstaller/Library/LaunchAgents/homebrew.mxcl.mysql.plist` exited with 5.
➜  ~ brew services restart mysql
Stopping `mysql`... (might take a while)
==&amp;gt; Successfully stopped `mysql` (label: homebrew.mxcl.mysql)
==&amp;gt; Successfully started `mysql` (label: homebrew.mxcl.mysql)
➜  ~ brew services list
Name    Status  User        File
mariadb stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist
mysql   stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist&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;h4 data-ke-size=&quot;size20&quot;&gt;사실 로컬 DB 말고도 다른 방법이 있긴 합니다만..&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 MariaDB와 MySQL 둘 다 사용하고 싶지 않다면, Docker를 사용하여 MariaDB 컨테이너를 실행하는 것을 고려해 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker를 사용하면 운영 체제와 독립적으로 데이터베이스를 실행할 수 있으므로, 이런 문제를 피할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 저는 학교에서 진행하는 프로젝트를 만들기 위해 local로 설치해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;9th Trying - 보안 기능 때문에 발생한 문제?&lt;/h2&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;위의 문제는 macOS의 보안 기능 때문에 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Big Sur 이후 버전에서는 서비스가 사용자 공간(user space) 대신 시스템 공간(system space)에서 시작해야 할 수 있습니다.&lt;/p&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;u&gt;&lt;b&gt;1. Homebrew로 MySQL을 정지합니다&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;brew services stop mysql&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;u&gt;&lt;b&gt;2. 아래 명령어를 사용하여 MySQL 서버를 직접 시작합니다&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;sudo mysql.server start&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;u&gt;&lt;b&gt;3. MySQL에 로그인합니다&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;mysql -u root -p&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;또한, MariaDB가 실행되지 않도록 확인하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 실행되는 MySQL과 MariaDB는 서로 충돌할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL이 잘 실행되는지 확인한 후에는 다시 MariaDB를 실행하거나 필요에 따라 삭제할 수 있습니다.&lt;/p&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;Result&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시몰라서 mysql과 mariadb를 모두 중지시키고 진행했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 2번에서 막혔군요&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;➜  ~ brew services stop mysql
Stopping `mysql`... (might take a while)
==&amp;gt; Successfully stopped `mysql` (label: homebrew.mxcl.mysql)
➜  ~ brew services stop mariadb
Stopping `mariadb`... (might take a while)
==&amp;gt; Successfully stopped `mariadb` (label: homebrew.mxcl.mariadb)
➜  ~ sudo mysql.server start
Password:
Starting MySQL
. ERROR! The server quit without updating PID file (/opt/homebrew/var/mysql/{your-computer-name}.local.pid).&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;10th Trying - 안전모드 실행해보기, my.cnf 파일 수정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10번 찍어 안넘어가는 나무 없다고 했습니다. 9번 휘둘렀으니 얼추 넘어가겠지요.&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;보통 이 오류는 MySQL 서버의 설정 파일에 문제가 있을 때 발생합니다.&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;MySQL의 설정 파일은 &lt;code&gt;/etc/my.cnf&lt;/code&gt;, &lt;code&gt;/etc/mysql/my.cnf&lt;/code&gt;, 또는 &lt;code&gt;/usr/local/mysql/etc/my.cnf&lt;/code&gt; 위치에 있을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일들을 확인하고, &lt;code&gt;[mysqld]&lt;/code&gt; 섹션에 있는 &lt;code&gt;pid-file&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;또한, 이 경로에 파일이 없는 경우 MySQL 서버는 시작되지 않을 수 있습니다.&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;만약 설정 파일에 문제가 없다면, 다음으로 MySQL 서버의 로그 파일을 확인해 보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 로그 파일은 보통 &lt;code&gt;/var/log/mysql/error.log&lt;/code&gt; 위치에 있습니다. -&amp;gt; 하지만 저는 M1이기에 해당하는 위치로 가야겠죠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일에는 MySQL 서버가 시작되지 않는 이유에 대한 자세한 정보가 포함되어 있을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 다음 명령을 실행해보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 명령은 mysql 서버를 안전 모드로 시작합니다&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;sudo mysqld_safe&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안전 모드에서 MySQL 서버를 시작한 후 문제가 발생하지 않는다면, 이는 설정 파일에 문제가 있음을 나타낼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 설정 파일을 수정하거나 새로 생성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;➜  ~ sudo mysql.server start
Password:
Starting MySQL
. ERROR! The server quit without updating PID file (/opt/homebrew/var/mysql/{your-computer-name}.local.pid).
➜  ~ sudo mysqld_safe
Password:
2023-06-02T06:40:08.6NZ mysqld_safe Logging to '/opt/homebrew/var/mysql/{your-computer-name}.local.err'.
2023-06-02T06:40:08.6NZ mysqld_safe Starting mysqld daemon with databases from /opt/homebrew/var/mysql
2023-06-02T06:40:08.6NZ mysqld_safe mysqld from pid file /opt/homebrew/var/mysql/{your-computer-name}.local.pid ended&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아마 pid-file 경로의 문제일 수 있으니 그 부분을 해결해보도록 하겠습니다.&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;pid-file&lt;/code&gt;이 설정되어 있지 않다면, MySQL 설정 파일인 &lt;code&gt;my.cnf&lt;/code&gt;에 이 값을 추가해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일은 보통 &lt;code&gt;/etc/my.cnf&lt;/code&gt;, &lt;code&gt;/etc/mysql/my.cnf&lt;/code&gt;, 또는 &lt;code&gt;/usr/local/mysql/etc/my.cnf&lt;/code&gt;에 위치해 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;my.cnf&lt;/code&gt; 파일의 위치는 MySQL 설치 환경에 따라 다를 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 MySQL의 &lt;code&gt;my.cnf&lt;/code&gt; 파일을 찾을 수 없다면, 아래의 명령어를 통해 파일을 찾을 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;find / -name my.cnf&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;&lt;code&gt;my.cnf&lt;/code&gt; 파일을 찾은 후에는, &lt;code&gt;[mysqld]&lt;/code&gt; 섹션에 &lt;code&gt;pid-file&lt;/code&gt; 설정을 추가하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 &lt;code&gt;pid-file&lt;/code&gt; 설정을 추가하는 예입니다:&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;[mysqld]
pid-file = /var/run/mysqld/mysqld.pid&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;code&gt;/var/run/mysqld/mysqld.pid&lt;/code&gt;는 pid 파일의 경로입니다.&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;변경 사항을 저장한 후 MySQL 서버를 재시작하면, 새로운 &lt;code&gt;pid-file&lt;/code&gt; 설정이 적용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이 과정에서 어려움을 겪거나, &lt;code&gt;my.cnf&lt;/code&gt; 파일을 찾을 수 없다면, MySQL 설치를 완전히 제거하고 다시 설치하는 것을 고려해 보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 시스템에 MySQL 설정이 꼬여 있을 경우 종종 효과적입니다.&lt;/p&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;Result&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 M1 칩을 사용하기에 M1에 해당하는 &lt;code&gt;opt/homebrew/이후 동일&lt;/code&gt; 경로를 통해 my.cnf 파일을 수정했습니다.&lt;/p&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;my.cnf&lt;/code&gt; 파일은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;# Default Homebrew MySQL server config
[client]
socket=/tmp/mysql.sock
[mysqld]
socket=/tmp/mysql.sock
pid-file = /var/run/mysqld/mysqld.pid
# Only allow connections from localhost
bind-address = 127.0.0.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과연 재시작하면 성공적으로 실행될까요?&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;➜  ~ brew services restart mysql
==&amp;gt; Successfully started `mysql` (label: homebrew.mxcl.mysql)
➜  ~ brew services list
Name    Status  User        File
mariadb none
mysql   stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist
➜  ~ mysql -u root -p
Enter password:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시나 였지만 역시나 였습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11th Trying - MySQL 다시 설치&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;위의 오류 메시지는 MySQL 서버가 실행되지 않고 있거나, &lt;code&gt;mysql.sock&lt;/code&gt; 파일을 찾을 수 없을 때 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 오류를 해결하기 위해 다음의 방법들을 시도해볼 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;MySQL 서비스를 재시작해 보세요.&lt;/b&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt; brew services restart mysql&lt;/code&gt;&lt;/pre&gt;
그리고 다음의 명령어로 MySQL 서비스가 정상적으로 동작하는지 확인해 보세요:만약 MySQL 서비스가 여전히 실행되지 않는다면, 다른 방법을 시도해 보세요. &amp;rarr; &lt;code&gt;다음 단계&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt; brew services list&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;아래의 명령어로 MySQL 서비스를 재시작해 보세요:&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;mysql.sock&lt;/code&gt; 파일의 위치를 확인해 보세요.&lt;/b&gt;MySQL 설정 파일인 &lt;code&gt;my.cnf&lt;/code&gt;에서 &lt;code&gt;mysql.sock&lt;/code&gt; 파일의 위치를 확인하거나 변경할 수 있습니다. &lt;code&gt;my.cnf&lt;/code&gt; 파일은 보통 &lt;code&gt;/etc/my.cnf&lt;/code&gt;, &lt;code&gt;/etc/mysql/my.cnf&lt;/code&gt;, 또는 &lt;code&gt;/usr/local/mysql/etc/my.cnf&lt;/code&gt;에 위치해 있습니다.만약 &lt;code&gt;mysql.sock&lt;/code&gt; 파일이 해당 위치에 존재하지 않는다면, MySQL 서비스를 재시작해야 합니다. MySQL 서비스가 시작되면, &lt;code&gt;mysql.sock&lt;/code&gt; 파일이 해당 위치에 자동으로 생성됩니다.&lt;/li&gt;
&lt;li&gt;이 파일에서 &lt;code&gt;[mysqld]&lt;/code&gt; 섹션에 있는 &lt;code&gt;socket&lt;/code&gt; 설정을 확인해 보세요. 이 설정이 &lt;code&gt;mysql.sock&lt;/code&gt; 파일의 실제 위치와 일치해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mysql.sock&lt;/code&gt; 파일은 일반적으로 &lt;code&gt;/tmp/mysql.sock&lt;/code&gt;에 위치해 있지만, 설치 환경에 따라 위치가 다를 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MySQL을 재설치해 보세요.&lt;/b&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt; brew remove mysql
 brew cleanup
 brew install mysql&lt;/code&gt;&lt;/pre&gt;
그리고 MySQL 서비스를 시작해 보세요:이제 MySQL이 정상적으로 동작하는지 확인해 보세요.&lt;/li&gt;
&lt;li&gt;&lt;code&gt; brew services start mysql&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;위의 방법들이 모두 실패한다면, MySQL을 완전히 제거한 후 다시 설치해 보세요&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mac - 백그라운드 항목이 추가됨 알림?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result&lt;/b&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;그러다가 뭔가 계속해서 Mac OS의 자체적인 알림이 있었습니다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;백그라운드 항목이 추가됨
'mysqld_safe'은(는) 백그라운드에서 실행될 수 있는 항목입니다. 로그인 항목 설정에서 이 항목을 관리할 수 있습니다.
이런 메세지가 실행될때마다 뜨는데 맥OS 자체의 문제일까?&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;blockquote data-ke-style=&quot;style3&quot;&gt;이 메시지는 Mac의 보안 및 개인 정보 보호 기능에 관련된 것입니다.&lt;br /&gt;&quot;mysqld_safe&quot;는 MySQL 서버를 안전하게 시작하는 스크립트인데, 백그라운드에서 실행되면서 macOS가 사용자에게 알리고 있습니다.&lt;br /&gt;&lt;br /&gt;macOS에서는 사용자가 어떤 애플리케이션이 자신의 컴퓨터에서 백그라운드에서 실행되고 있는지 알 수 있도록 이러한 메시지를 보여줍니다. &lt;br /&gt;이는 보안적인 측면에서 유용하며, 사용자가 의도치 않게 백그라운드에서 돌아가는 프로세스를 제어할 수 있게 해줍니다.&lt;br /&gt;&lt;br /&gt;하지만, MySQL과 같이 의도적으로 백그라운드에서 실행되도록 설계된 애플리케이션의 경우에는 이 메시지를 무시하셔도 됩니다.&lt;br /&gt;&lt;br /&gt;해당 메시지가 거슬린다면, 시스템 환경설정 &amp;gt; 사용자 및 그룹 &amp;gt; 로그인 항목 에서 &quot;mysqld_safe&quot;를 찾아서 삭제하실 수 있지만, 이 경우 MySQL 서버가 자동으로 시작되지 않을 수 있으니 주의하시기 바랍니다.&lt;br /&gt;&lt;br /&gt;만약 MySQL이 정상적으로 작동한다면, 이 메시지는 문제가 되지 않습니다. &lt;br /&gt;MySQL 작동에 문제가 있다면, 해당 메시지가 아니라 MySQL 자체의 로그나 에러 메시지를 확인하는 것이 더 도움이 될 것입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 사실상 메세지는 문제가 되지 않는 것이므로 다시 처음으로 돌아가서 해봤던 작업을 해보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;12th Trying&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 그 방법은 바로 이것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;/opt/homebrew/var/mysql&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;&lt;b&gt;Result&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;brew reinstall mysql&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;➜  ~ brew services restart mysql
Stopping `mysql`... (might take a while)
==&amp;gt; Successfully stopped `mysql` (label: homebrew.mxcl.mysql)
==&amp;gt; Successfully started `mysql` (label: homebrew.mxcl.mysql)
➜  ~ brew services list
Name    Status  User        File
mariadb none
mysql   stopped tminstaller ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;One Hours Later..&lt;/b&gt;&lt;/blockquote&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;hr data-ke-style=&quot;style1&quot; /&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;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Final Trying - Reset, Mac 초기화하기&lt;/h2&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;맥북 초기화 방법은 다음과 같습니다. M1 기준으로 진행하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://crossjin.tistory.com/entry/MAC-M1-%EC%B4%88%EA%B8%B0%ED%99%94-%EB%B0%A9%EB%B2%95%EA%B3%B5%EC%9E%A5%EC%B4%88%EA%B8%B0%ED%99%94&quot;&gt;MAC M1 초기화 방법(공장초기화)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1685779547874&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;MAC M1 초기화 방법(공장초기화)&quot; data-og-description=&quot;M1 칩을 장착한 맥이 점점 늘어나면서 초기화해야 될 일이 생기고 있는데 큰 문제를 일으켜서 하는 건 아니고 다른 사용자가 사용했었던 맥이라 클린 한 세팅을 위해 초기화하는 것이다. 그렇다&quot; data-og-host=&quot;crossjin.tistory.com&quot; data-og-source-url=&quot;https://crossjin.tistory.com/entry/MAC-M1-%EC%B4%88%EA%B8%B0%ED%99%94-%EB%B0%A9%EB%B2%95%EA%B3%B5%EC%9E%A5%EC%B4%88%EA%B8%B0%ED%99%94&quot; data-og-url=&quot;https://crossjin.tistory.com/entry/MAC-M1-%EC%B4%88%EA%B8%B0%ED%99%94-%EB%B0%A9%EB%B2%95%EA%B3%B5%EC%9E%A5%EC%B4%88%EA%B8%B0%ED%99%94&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b6MJRS/hySRWiiz1i/JRGk5z63cjQgJq2rWsIddK/img.jpg?width=800&amp;amp;height=584&amp;amp;face=0_0_800_584,https://scrap.kakaocdn.net/dn/RDW4t/hySR0kGN4l/Nh1R06ZSHQ7BlRPSWK70G1/img.jpg?width=800&amp;amp;height=584&amp;amp;face=0_0_800_584,https://scrap.kakaocdn.net/dn/bTYuBA/hySQKcIaKg/gyb3vDP8QacG0amiRIyJv1/img.jpg?width=1384&amp;amp;height=954&amp;amp;face=0_0_1384_954&quot;&gt;&lt;a href=&quot;https://crossjin.tistory.com/entry/MAC-M1-%EC%B4%88%EA%B8%B0%ED%99%94-%EB%B0%A9%EB%B2%95%EA%B3%B5%EC%9E%A5%EC%B4%88%EA%B8%B0%ED%99%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://crossjin.tistory.com/entry/MAC-M1-%EC%B4%88%EA%B8%B0%ED%99%94-%EB%B0%A9%EB%B2%95%EA%B3%B5%EC%9E%A5%EC%B4%88%EA%B8%B0%ED%99%94&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b6MJRS/hySRWiiz1i/JRGk5z63cjQgJq2rWsIddK/img.jpg?width=800&amp;amp;height=584&amp;amp;face=0_0_800_584,https://scrap.kakaocdn.net/dn/RDW4t/hySR0kGN4l/Nh1R06ZSHQ7BlRPSWK70G1/img.jpg?width=800&amp;amp;height=584&amp;amp;face=0_0_800_584,https://scrap.kakaocdn.net/dn/bTYuBA/hySQKcIaKg/gyb3vDP8QacG0amiRIyJv1/img.jpg?width=1384&amp;amp;height=954&amp;amp;face=0_0_1384_954');&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;MAC M1 초기화 방법(공장초기화)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;M1 칩을 장착한 맥이 점점 늘어나면서 초기화해야 될 일이 생기고 있는데 큰 문제를 일으켜서 하는 건 아니고 다른 사용자가 사용했었던 맥이라 클린 한 세팅을 위해 초기화하는 것이다. 그렇다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;crossjin.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I15kd/btsiyWSGa2u/1nqVEDADGtL3VF4k1pVGlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I15kd/btsiyWSGa2u/1nqVEDADGtL3VF4k1pVGlK/img.png&quot; data-alt=&quot;인터넷이 느려 슬픈 저는 자고 일어났습니다...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I15kd/btsiyWSGa2u/1nqVEDADGtL3VF4k1pVGlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI15kd%2FbtsiyWSGa2u%2F1nqVEDADGtL3VF4k1pVGlK%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;537&quot; height=&quot;432&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;432&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그렇게 다음날 아침 MacOS 설치가 완료되었습니다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화가 모두 끝났다면 최우선적으로 brew를 설치하고 iTerm2를 설치합니다.&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;Mac에서 Homebrew를 완전히 삭제하고 재 설치하고 싶다면?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Homebrew를 Mac에서 완전히 삭제하고 재설치하는 과정은 아래와 같습니다.&lt;/b&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;1. Homebrew 제거&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어는 Homebrew의 공식 uninstall 스크립트를 실행합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 스크립트는 Homebrew를 설치한 디렉토리 (일반적으로 /usr/local)에서 Homebrew 파일을 제거합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1685779691333&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/bin/bash -c &quot;$(curl -fsSL &amp;lt;https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh&amp;gt;)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;2. 모든 Homebrew 패키지와 캐시 제거&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt; rm -rf /usr/local/Cellar /usr/local/Homebrew /usr/local/Caskroom /usr/local/Frameworks /usr/local/opt /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/local/var&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어는 Homebrew가 설치한 모든 패키지와 Homebrew의 git 저장소를 제거합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Homebrew가 설치한 모든 패키지와 캐시를 수동으로 제거하기 위해 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;3. Homebrew 설치&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어는 Homebrew를 다시 설치하는 공식 스크립트를 실행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685779747977&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 모든 명령어는 터미널에서 실행되며, 명령어 실행 시 관리자 권한이 필요할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 실행 후에는 Homebrew가 올바르게 설치되었는지 확인하려면 &lt;code&gt;brew doctor&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;그러나 Homebrew를 재설치하기 전에 Homebrew와 관련된 문제를 해결하는 데 도움이 될 수 있는 &lt;code&gt;brew doctor&lt;/code&gt; 명령어를 먼저 실행해 보는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;iTerm 설치, zsh 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://green1229.tistory.com/77&quot;&gt;iTerm2 설치 및 사용&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1685779787696&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;iTerm2 설치 및 사용&quot; data-og-description=&quot;안녕하세요. 그린입니다  이번 포스팅에서는 iTerm2를 설치 및 커스터마이징하여 사용 적용해보겠습니다 &amp;zwj;  예전부터 MacOS에서 기본 터미널을 사용하고 있었는데 다른 분들중에 iTerms2&quot; data-og-host=&quot;green1229.tistory.com&quot; data-og-source-url=&quot;https://green1229.tistory.com/77&quot; data-og-url=&quot;https://green1229.tistory.com/77&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/y34Pq/hySR57mOGm/nX4MdH8vKdkkg7lTLTJ361/img.png?width=800&amp;amp;height=417&amp;amp;face=0_0_800_417,https://scrap.kakaocdn.net/dn/cxAO6Z/hySQGIaBQr/wP5Ij2GXGIOxg7YSgmzYK0/img.png?width=800&amp;amp;height=417&amp;amp;face=0_0_800_417,https://scrap.kakaocdn.net/dn/8NZfm/hySRS1f0F6/8pkX1CasVykckdGdl7zgt0/img.png?width=1782&amp;amp;height=998&amp;amp;face=0_0_1782_998&quot;&gt;&lt;a href=&quot;https://green1229.tistory.com/77&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://green1229.tistory.com/77&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/y34Pq/hySR57mOGm/nX4MdH8vKdkkg7lTLTJ361/img.png?width=800&amp;amp;height=417&amp;amp;face=0_0_800_417,https://scrap.kakaocdn.net/dn/cxAO6Z/hySQGIaBQr/wP5Ij2GXGIOxg7YSgmzYK0/img.png?width=800&amp;amp;height=417&amp;amp;face=0_0_800_417,https://scrap.kakaocdn.net/dn/8NZfm/hySRS1f0F6/8pkX1CasVykckdGdl7zgt0/img.png?width=1782&amp;amp;height=998&amp;amp;face=0_0_1782_998');&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;iTerm2 설치 및 사용&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요. 그린입니다  이번 포스팅에서는 iTerm2를 설치 및 커스터마이징하여 사용 적용해보겠습니다 &amp;zwj;  예전부터 MacOS에서 기본 터미널을 사용하고 있었는데 다른 분들중에 iTerms2&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;green1229.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;+ zsh 환경변수를 세팅할 때 본인이 가지고 있는 CPU가 무엇인 지 확인하고 가는 것을 추천합니다.&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;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Success, But - 첫 설치 시 주의할 점, 초기 비밀번호 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;brew install mariadb&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;brew services start mariadb&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 통해 mariadb를 성공적으로 설치를 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 DB가 업데이트 되면서 root 계정에는 반드시 패스워드가 필요하다고 하였고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 작업을 진행하고 TroubleShooting을 마쳤습니다.&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;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;~ &amp;raquo; sudo mysql -u root  
Password:
Sorry, try again.
Password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 7
Server version: 10.11.3-MariaDB Homebrew

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]&amp;gt; ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password;
Query OK, 0 rows affected (0.010 sec)

MariaDB [(none)]&amp;gt; SET PASSWORD FOR 'root'@'localhost' = PASSWORD('your-password');
Query OK, 0 rows affected (0.022 sec)

MariaDB [(none)]&amp;gt; FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.001 sec)

MariaDB [(none)]&amp;gt; exit
Bye
--------------------------------------------------------------------------------------
~ &amp;raquo; mysql -u root -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.11.3-MariaDB Homebrew

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]&amp;gt; exit
Bye&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떤 작업을 한 것인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서 수행된 주요 작업들을 단계별로 나열해 보겠습니다.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Homebrew 설치&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;brew install mariadb&lt;/code&gt; 명령을 통해 MariaDB를 설치하려 하였지만, Homebrew가 설치되어 있지 않아 명령어를 인식하지 못하는 문제가 발생했습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zsh: command not found: brew&lt;/code&gt; 메시지는 brew라는 명령어를 인식하지 못했다는 것을 나타냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Homebrew 설치 후 MariaDB 설치&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Homebrew를 설치한 이후 &lt;code&gt;brew install mariadb&lt;/code&gt; 명령을 통해 MariaDB를 설치하였습니다.&lt;/li&gt;
&lt;li&gt;MariaDB는 MySQL의 포크 버전으로, 데이터베이스 관리 시스템입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MariaDB 서비스 시작&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;brew services start mariadb&lt;/code&gt; 명령을 통해 MariaDB 서비스를 시작하셨습니다.&lt;/li&gt;
&lt;li&gt;이 작업은 MariaDB를 백그라운드에서 실행하도록 설정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MariaDB에 로그인 시도&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;mysql -u root -p&lt;/code&gt; 명령으로 root 사용자로 MariaDB에 로그인하려 하셨지만, 비밀번호가 설정되어 있지 않아 접근이 거부되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;mysql_secure_installation&lt;/code&gt; 실패&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;mysql_secure_installation&lt;/code&gt; 스크립트를 실행하려 하셨지만, root 비밀번호가 없어 접근이 거부되어 실행에 실패하였습니다.&lt;/li&gt;
&lt;li&gt;이 스크립트는 MariaDB를 보안적으로 강화하기 위해 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;sudo를 이용한 MariaDB 접근&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;sudo mysql -u root&lt;/code&gt; 명령을 통해 MariaDB에 root 권한으로 접근하셨습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;root 비밀번호 설정 - &lt;/b&gt;MariaDB에 로그인한 후, 다음의 SQL 명령어를 사용해 root 비밀번호를 설정하셨습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SET PASSWORD FOR 'root'@'localhost' = PASSWORD('your-password');&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FLUSH PRIVILEGES;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이러한 명령어를 통해 root 계정의 비밀번호를 'your-password'로 설정하였습니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비밀번호 설정 후 MariaDB 로그인 성공&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비밀번호가 설정된 이후 &lt;code&gt;mysql -u root -p&lt;/code&gt; 명령을 통해 MariaDB에 로그인하는데 성공하셨습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼, 처음에는 MariaDB에 접근하는 데 문제가 있었지만, 비밀번호를 설정한 후에는 성공적으로 로그인하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정은 MariaDB의 기본 설정에서는 root 계정의 비밀번호가 비어있기에 추가 설정을 해주어야 하는 것을 나타냅니다&lt;/p&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;따라하실 때 5번을 건너뛰어도 크게 문제 없을 듯 합니다.&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;예를들어보자면 원래의 Mac 이름이 TimeMaplaptop-MacBookAir.local이었다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바뀐 컴퓨터 이름은 TimeMapui-MacBookAir.local 과 같이 변경되는 것을 확인할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTfCp3/btsisSdH1dr/ULntKPrD1gjjkzdo0cz7j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTfCp3/btsisSdH1dr/ULntKPrD1gjjkzdo0cz7j1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;244&quot; data-origin-height=&quot;199&quot; data-filename=&quot;스크린샷 2023-06-03 오후 5.19.23.png&quot; data-widthpercent=&quot;40.16&quot; style=&quot;width: 39.6938%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTfCp3/btsisSdH1dr/ULntKPrD1gjjkzdo0cz7j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTfCp3%2FbtsisSdH1dr%2FULntKPrD1gjjkzdo0cz7j1%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;244&quot; height=&quot;199&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tbYmQ/btsisn5VuQI/b5y4aZK9cbBG8qFKduriSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tbYmQ/btsisn5VuQI/b5y4aZK9cbBG8qFKduriSk/img.png&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;208&quot; data-is-animation=&quot;false&quot; style=&quot;width: 59.1434%;&quot; data-widthpercent=&quot;59.84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tbYmQ/btsisn5VuQI/b5y4aZK9cbBG8qFKduriSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtbYmQ%2Fbtsisn5VuQI%2Fb5y4aZK9cbBG8qFKduriSk%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;380&quot; height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;행복하군요. 무려 11286개의 단어를 작성하고 해결했습니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어... 한 달 이상 봉인되어있던 로컬 DB에 대한 한을 풀었습니다..!&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;진짜 모르겠을 때에는 초기화를 하는 것도 방법일지도 모르겠네요&lt;/p&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;12가지의 방법 안에서 해결할 수 있었으면 좋겠습니다.&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>(3D)Dev Deep Dive/TroubleShootings</category>
      <category>iTerm</category>
      <category>mariaDB</category>
      <category>MySQL</category>
      <category>reset</category>
      <category>setting</category>
      <category>sudo</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/238</guid>
      <comments>https://time-map-installer.tistory.com/238#entry238comment</comments>
      <pubDate>Sat, 3 Jun 2023 15:59:41 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] About HTTP 요청 관련 어노테이션, @도움!</title>
      <link>https://time-map-installer.tistory.com/237</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNcewm/btsijP1SvD2/KYQr3E7a0FtaN4ubQPttM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNcewm/btsijP1SvD2/KYQr3E7a0FtaN4ubQPttM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNcewm/btsijP1SvD2/KYQr3E7a0FtaN4ubQPttM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNcewm%2FbtsijP1SvD2%2FKYQr3E7a0FtaN4ubQPttM1%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;384&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot를 사용하기 위해 학습을 하던 중, 문득 HTTP 요청에 따른 어노테이션들이 무엇이 있고, 어떤 일을 하는 지 궁금했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 저는 이에 대해 알아보기로 하였고, 나중에 이 글을 읽고 도움이 되었으면 하는 마음에 새 글을 작성하기로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;@GetMapping&lt;/code&gt; 은 무엇인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@GetMapping&lt;/code&gt;은 Spring Framework에서 제공하는 annotation이며, 주로 &lt;b&gt;HTTP GET 요청&lt;/b&gt;을 처리하는 메소드에 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 &lt;code&gt;@RequestMapping&lt;/code&gt;의 특수한 형태라고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로, &lt;code&gt;@GetMapping&lt;/code&gt;은 컨트롤러의 메소드 위에 위치하여, 특정 URL에 대한 &lt;b&gt;GET 요청&lt;/b&gt;이 오면 그 요청을 처리할 메소드를 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;간단한 예를 보자면 다음과 같습니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@GetMapping(&quot;/hello&quot;)
public String hello() {
    return &quot;Hello, World!&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우에는 &lt;code&gt;/hello&lt;/code&gt; URL로의 GET 요청이 이 메소드에 의해 처리됩니다. 그 결과로서 &quot;Hello, World!&quot;라는 문자열이 반환됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &lt;code&gt;@GetMapping&lt;/code&gt;은 &lt;code&gt;@RequestMapping&lt;/code&gt;에서 제공하는 속성들을 그대로 제공합니다. 예를 들어, &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;params&lt;/code&gt;, &lt;code&gt;headers&lt;/code&gt; 등의 속성을 사용할 수 있습니다. 이들 속성은 요청이 특정 조건을 충족해야 메소드가 처리하도록 하는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@GetMapping&lt;/code&gt;은 주로 RESTful API를 개발할 때 사용되며, 이는 클라이언트에서 서버로 데이터를 요청하는 경우에 주로 사용되는 HTTP 메소드인 GET을 사용함으로써, 서버에서 클라이언트로의 데이터 전송을 명시적으로 표현하는 방법입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;엇, 이거 분명 페이지 이동에도 사용되지 않았나요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇습니다, &lt;code&gt;@GetMapping&lt;/code&gt;은 페이지 이동에도 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서는 Thymeleaf, FreeMarker, JSP 등의 템플릿 엔진을 사용해 뷰를 만들 수 있습니다.&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;code&gt;@GetMapping&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예를 들어, 아래와 같이 사용할 수 있습니다&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@GetMapping(&quot;/greeting&quot;)
public String greeting(
    @RequestParam(name=&quot;name&quot;, required=false, defaultValue=&quot;World&quot;) 
    String name, Model model
) {
    model.addAttribute(&quot;name&quot;, name);
    return &quot;greeting&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 &quot;&lt;b&gt;/greeting&lt;/b&gt;&quot; 경로로 GET 요청이 오면 &lt;code&gt;greeting&lt;/code&gt; 메소드가 호출됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메소드는 &lt;code&gt;greeting&lt;/code&gt;이라는 이름의 템플릿을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 템플릿 엔진(예를 들어, Thymeleaf)은 이 &lt;code&gt;greeting&lt;/code&gt; 이름을 가진 템플릿을 찾아서 렌더링하고, 최종적으로 생성된 HTML 페이지를 클라이언트에게 반환하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 메소드 인자로 받는 &lt;code&gt;Model&lt;/code&gt; 객체는 뷰에 전달할 데이터를 담는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제에서는 &lt;code&gt;name&lt;/code&gt;이라는 속성에 값을 담아 &lt;code&gt;greeting&lt;/code&gt; 템플릿에 전달하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 전달된 데이터는 템플릿 내에서 사용되어 동적인 뷰를 생성하는데 활용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;greeting이 반환된다는건 잘 알겠어요. 하지만 어디에 위치해있는 지 잘 모르겠네요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot의 기본 설정에 따르면, &lt;code&gt;greeting&lt;/code&gt;이라는 뷰 템플릿 파일은 일반적으로 &lt;code&gt;src/main/resources/templates&lt;/code&gt; 디렉토리 아래에 위치하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예를 들어 Thymeleaf를 사용한다면, &lt;code&gt;greeting&lt;/code&gt; 템플릿은 &lt;code&gt;src/main/resources/templates/greeting.html&lt;/code&gt;이 됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 뷰 템플릿 파일의 위치는 &lt;code&gt;application.properties&lt;/code&gt; 또는 &lt;code&gt;application.yml&lt;/code&gt; 설정 파일을 통해 변경할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿 엔진(예: Thymeleaf)은 &lt;code&gt;@GetMapping&lt;/code&gt;에서 반환한 문자열을 통해 해당 위치의 파일을 찾아 렌더링 작업을 수행하게 됩니다. 이렇게 렌더링된 결과는 최종적으로 HTML 형태로 클라이언트에게 전송되며, 이것이 바로 사용자가 웹 브라우저를 통해 보게 되는 웹 페이지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그리고 JSP를 사용하는 경우 일반적으로 웹 애플리케이션의 &lt;code&gt;WEB-INF&lt;/code&gt; 디렉토리 아래에 JSP 파일들이 위치합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;greeting.jsp&lt;/code&gt;라는 JSP 파일이 있다면, 그 위치는 보통 &lt;code&gt;src/main/webapp/WEB-INF/jsp/greeting.jsp&lt;/code&gt;가 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, &lt;code&gt;src/main/webapp&lt;/code&gt;는 웹 애플리케이션의 루트 디렉토리에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Spring Boot에서는 기본적으로 내장 톰캣을 사용하고, &lt;code&gt;src/main/webapp&lt;/code&gt;와 &lt;code&gt;WEB-INF&lt;/code&gt; 디렉토리는 기본적으로 사용하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 만약 Spring Boot에서 JSP를 사용하려면 별도의 설정이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 &lt;code&gt;spring.mvc.view.prefix&lt;/code&gt;와 &lt;code&gt;spring.mvc.view.suffix&lt;/code&gt; 속성을 &lt;code&gt;application.properties&lt;/code&gt; 또는 &lt;code&gt;application.yml&lt;/code&gt;에 설정하여 JSP 파일의 위치를 지정할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;spring.mvc.view.prefix= /WEB-INF/jsp/
spring.mvc.view.suffix= .jsp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정은 컨트롤러가 뷰 이름으로 &quot;greeting&quot;을 반환하면, Spring MVC가 이를 &lt;code&gt;/WEB-INF/jsp/greeting.jsp&lt;/code&gt;로 변환하여 해당 JSP 파일을 찾게 됩니다. 그리고 이 JSP 파일이 렌더링되어 최종적인 HTML 페이지가 생성됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HTTP 요청 메소드를 처리하는 다른 어노테이션들도 알려주세요!&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서는 HTTP 요청 메소드를 처리하는 여러 가지 어노테이션을 제공하고 있습니다. &lt;code&gt;@GetMapping&lt;/code&gt;과 마찬가지로 이들 어노테이션들도 &lt;code&gt;@RequestMapping&lt;/code&gt;의 특수한 형태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아래에는 추가적인 어노테이션들을 다뤄놓았습니다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@PostMapping&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HTTP POST&lt;/code&gt; 요청을 처리하는 메소드를 표시합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST&lt;/code&gt;는 주로 클라이언트에서 서버로 데이터를 전송하거나, 리소스를 생성하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@PostMapping(&quot;/users&quot;)
public User createUser(@RequestBody User user) {
    return userService.createUser(user);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@PutMapping&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HTTP PUT&lt;/code&gt; 요청을 처리하는 메소드를 표시합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PUT&lt;/code&gt;은 주로 리소스를 수정하거나, 대체하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@PutMapping(&quot;/users/{id}&quot;)
public User updateUser(@PathVariable Long id, @RequestBody User user) {
    return userService.updateUser(id, user);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@DeleteMapping&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HTTP DELETE&lt;/code&gt; 요청을 처리하는 메소드를 표시합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE&lt;/code&gt;는 주로 리소스를 삭제하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@DeleteMapping(&quot;/users/{id}&quot;)
public void deleteUser(@PathVariable Long id) {
    userService.deleteUser(id);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@PatchMapping&lt;/code&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;HTTP PATCH&lt;/code&gt; 요청을 처리하는 메소드를 표시합니다.&lt;/li&gt;
&lt;li&gt;PATCH는 주로 리소스의 일부를 수정하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@PatchMapping(&quot;/users/{id}&quot;)
public User updateUserPartially(@PathVariable Long id, @RequestBody User user) {
    return userService.updateUserPartially(id, user);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들 어노테이션을 사용하면 각 HTTP 요청 메소드에 대응하는 컨트롤러 메소드를 명확하게 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 각 메소드가 어떤 HTTP 요청을 처리하는지를 더 쉽게 이해할 수 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 코드의 가독성을 향상시키는 데 도움이 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;아직 이해가 잘 되지 않아요. 더 설명해주세요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 어노테이션에 대한 몇 가지 상황을 더 설명하겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@GetMapping&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상품 목록을 가져오는 API를 만들 때, &lt;code&gt;GET 메소드&lt;/code&gt;는 서버의 리소스를 조회하는 데 가장 일반적으로 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@GetMapping(&quot;/products&quot;)
public List&amp;lt;Product&amp;gt; getProducts() {
    return productService.getAllProducts();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 특정 사용자의 정보를 조회하는 API를 만들 때,&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@GetMapping(&quot;/users/{userId}&quot;)
public User getUser(@PathVariable String userId) {
    return userService.getUser(userId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@PostMapping&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 사용자를 등록하는 API를 만들 때, &lt;code&gt;POST 메소드&lt;/code&gt;는 서버에 새로운 리소스를 생성하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@PostMapping(&quot;/users&quot;)
public User createUser(@RequestBody User newUser) {
    return userService.createUser(newUser);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@PutMapping&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자의 전체 정보를 수정하는 API를 만들 때, &lt;code&gt;PUT 메소드&lt;/code&gt;는 서버의 특정 리소스를 전체적으로 업데이트하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@PutMapping(&quot;/users/{userId}&quot;)
public User updateUser(@PathVariable String userId, @RequestBody User updatedUser) {
    return userService.updateUser(userId, updatedUser);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@DeleteMapping&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 사용자를 삭제하는 API를 만들 때, &lt;code&gt;DELETE 메소드&lt;/code&gt;는 서버의 특정 리소스를 삭제하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@DeleteMapping(&quot;/users/{userId}&quot;)
public ResponseEntity&amp;lt;?&amp;gt; deleteUser(@PathVariable String userId) {
    userService.deleteUser(userId);
    return ResponseEntity.ok().build();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@PatchMapping&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자의 일부 정보(예: 이메일 변경)만을 수정하는 API를 만들 때, &lt;code&gt;PATCH 메소드&lt;/code&gt;는 서버의 특정 리소스의 일부분만을 업데이트하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@PatchMapping(&quot;/users/{userId}&quot;)
public User updateUserEmail(@PathVariable String userId, @RequestBody User updatedUser) {
    return userService.updateUserEmail(userId, updatedUser);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 각 HTTP 메소드는 그 용도에 따라 적절한 상황에서 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 클라이언트와 서버 간의 커뮤니케이션을 더욱 명확하고 이해하기 쉽게 만들 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Backend</category>
      <category>@annotation</category>
      <category>Annotation</category>
      <category>deletemapping</category>
      <category>GetMapping</category>
      <category>Mapping</category>
      <category>PostMapping</category>
      <category>putmapping</category>
      <category>spring boot</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/237</guid>
      <comments>https://time-map-installer.tistory.com/237#entry237comment</comments>
      <pubDate>Fri, 2 Jun 2023 10:17:52 +0900</pubDate>
    </item>
    <item>
      <title>[JWT] Jin짜 Way렇게 어렵T? 기초 알아보기</title>
      <link>https://time-map-installer.tistory.com/236</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/llHOA/btsijY5CbtO/AF0ZKxTZdn6LCGWiU2MlR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/llHOA/btsijY5CbtO/AF0ZKxTZdn6LCGWiU2MlR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/llHOA/btsijY5CbtO/AF0ZKxTZdn6LCGWiU2MlR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FllHOA%2FbtsijY5CbtO%2FAF0ZKxTZdn6LCGWiU2MlR1%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;256&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;JWT를 프론트엔드와 백엔드 관점에서 살펴보자&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;JWT(Json Web Token)&lt;/b&gt;&lt;/u&gt; 로그인 방법은 &lt;b&gt;클라이언트(프론트엔드)&lt;/b&gt;와 &lt;b&gt;서버(백엔드)&lt;/b&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;이에 대한 설명을 프론트엔드와 백엔드의 관점에서 분할해 설명하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uePcU/btsimD65C9i/vfkaXJ6AqoXuZChMdsyYr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uePcU/btsimD65C9i/vfkaXJ6AqoXuZChMdsyYr1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;1192&quot; data-filename=&quot;스크린샷 2023-06-02 오전 9.38.49.png&quot; style=&quot;width: 50.4329%; margin-right: 10px;&quot; data-widthpercent=&quot;51.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uePcU/btsimD65C9i/vfkaXJ6AqoXuZChMdsyYr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuePcU%2FbtsimD65C9i%2FvfkaXJ6AqoXuZChMdsyYr1%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;1270&quot; height=&quot;1192&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D6Qcu/btsikCnFZue/ZQkqoiLA68dyPImCk6YeZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D6Qcu/btsikCnFZue/ZQkqoiLA68dyPImCk6YeZ1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;1240&quot; data-filename=&quot;스크린샷 2023-06-02 오전 9.42.14.png&quot; style=&quot;width: 48.4043%;&quot; data-widthpercent=&quot;48.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D6Qcu/btsikCnFZue/ZQkqoiLA68dyPImCk6YeZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD6Qcu%2FbtsikCnFZue%2FZQkqoiLA68dyPImCk6YeZ1%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;1268&quot; height=&quot;1240&quot;/&gt;&lt;/span&gt;&lt;/div&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프론트엔드&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 프론트엔드에서 로그인 요청을 할 때, 일반적으로 아이디와 비밀번호 등의 정보를 함께 보냅니다.&lt;/li&gt;
&lt;li&gt;이는 일반적으로 &lt;u&gt;&lt;b&gt;HTTP POST&lt;/b&gt;&lt;/u&gt; 요청을 통해 이루어집니다.&lt;/li&gt;
&lt;li&gt;로그인 요청이 성공하면 서버는 &lt;b&gt;JWT를 생성&lt;/b&gt;하고 이를 &lt;b&gt;HTTP 응답&lt;/b&gt;으로 클라이언트에게 보냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라이언트(프론트엔드)&lt;/b&gt;는 이 토큰을 저장해야 합니다.&lt;/li&gt;
&lt;li&gt;이를 위해 일반적으로 &lt;b&gt;쿠키&lt;/b&gt; 또는 &lt;b&gt;localStorage 같은&lt;/b&gt; &lt;b&gt;웹 스토리지&lt;/b&gt;를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드에서는 로그인 이후에 필요한 &lt;b&gt;모든 API 요청&lt;/b&gt;에 이 JWT를 포함해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 이는 &lt;b&gt;HTTP 요청&lt;/b&gt;의 &lt;b&gt;Authorization 헤더&lt;/b&gt;에 &lt;b&gt;&quot;Bearer&quot;&lt;/b&gt;라는 접두어와 함께 토큰이 포함되어 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;백엔드&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백엔드 서버에서는 먼저 로그인 요청을 받아 사용자의 자격 증명을 검증합니다.&lt;/li&gt;
&lt;li&gt;이는 일반적으로 데이터베이스에 저장된 사용자 정보를 참조하여 이루어집니다.&lt;/li&gt;
&lt;li&gt;자격 증명이 올바르다면, 백엔드는 JWT를 생성합니다.&lt;/li&gt;
&lt;li&gt;JWT는 일반적으로 사용자의 아이디와 같은 식별 정보, 토큰의 만료 날짜, 그리고 서버의 비밀 키를 사용해 서명된 데이터를 포함합니다.&lt;/li&gt;
&lt;li&gt;백엔드는 생성한 JWT를 HTTP 응답으로 클라이언트에게 보냅니다.&lt;/li&gt;
&lt;li&gt;이후로 클라이언트가 보내는 모든 요청은 이 JWT를 포함해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 서버에서는 이후로 들어오는 모든 요청에 대해 Authorization 헤더를 확인하고, JWT가 존재하고 유효한지 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 JWT가 만료되었거나 변조되었는지를 검사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT가 유효하다면 해당 요청을 수행하고, 그렇지 않다면 권한이 없다는 HTTP 메세지를 보냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;토큰을 관리하는 방법과 들어가는 정보는 무엇이 있을까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JWT(Json Web Tokens)은 로그인된 사용자를 인증하는데 사용되며, &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;일반적으로 토큰에는 다음과 같은 정보(Payload, 페이로드)가 포함됩니다.&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;b&gt;등록된 클레임(Registered Claims)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이는 토큰에 대한 정보를 제공하는 일련의 표준 필드입니다.&lt;/li&gt;
&lt;li&gt;예를 들어, 'iss' (발급자), 'exp' (만료 시간), 'sub' (주제), 'aud' (대상), 등입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공개 클레임(Public Claims)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이는 사용자와 관련된 추가 정보를 포함할 수 있는 필드입니다.&lt;/li&gt;
&lt;li&gt;예를 들어 사용자의 아이디, 이름, 권한 등이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비공개 클레임(Private Claims)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이들은 클라이언트와 서버 사이에 합의하여 사용하는 클레임입니다.&lt;/li&gt;
&lt;li&gt;이러한 클레임은 등록된 클레임이나 공개 클레임과 충돌하지 않는 이름을 사용해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;음, 조금 어려운 것 같네요. 여권에 비유해볼까요?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;1. 등록된 클레임(Registered Claims)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;여권에는 이름, 성별, 출생일 등의 기본적인 정보가 들어가는데, 이는 &quot;등록된 클레임&quot;에 비유할 수 있습니다. &lt;/b&gt;&lt;/li&gt;
&lt;li&gt;여권이 이를 통해 누가 소유하고 있는지, 언제 만료되는지 등의 기본적인 정보를 알 수 있는 것처럼, JWT에서 'iss' (발급자), 'exp' (만료 시간), 'sub' (주제), 'aud' (대상) 등의 필드가 바로 이 역할을 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;2. 공개 클레임(Public Claims)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여권에 추가로 특정 사람에게 부여되는 비자나 스탬프와 같은 정보가 추가될 수 있습니다.&lt;/li&gt;
&lt;li&gt;이는 해당 사람이 어느 국가로 여행할 수 있는지 또는 어떤 활동을 할 수 있는지에 대한 추가 정보를 제공합니다.&lt;/li&gt;
&lt;li&gt;이를 &quot;공개 클레임&quot;에 비유할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JWT에서 이러한 정보는 사용자의 아이디, 이름, 권한 등을 포함할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;3. 비공개 클레임(Private Claims)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마지막으로, 여권에는 개인이나 특정 조직이 필요로 하는 추가 정보가 들어갈 수 있습니다.&lt;/li&gt;
&lt;li&gt;예를 들어, 특정 여행사가 고객의 여행 선호도나 추가 서비스 등의 정보를 여권에 담아두는 것처럼, JWT에서는 클라이언트와 서버가 합의하여 사용하는 추가 정보가 바로 &quot;비공개 클레임&quot;이 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단, 이러한 정보는 기존의 등록된 클레임이나 공개 클레임과는 다른 이름을 사용해야 충돌이 없습니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;그리고 토큰 관리 방법은 여러 가지가 있습니다. 아래 몇 가지 기본적인 방법을 나열해 보겠습니다&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;스토리지&lt;/b&gt;
&lt;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;일반적으로 이를 위해 HTTP Only 쿠키나 웹 스토리지(Local Storage, Session Storage)를 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;어떤 방식을 사용하느냐는 **XSS(Cross-Site Scripting)**나 **CSRF(Cross-Site Request Forgery)**와 같은 보안 위협을 어떻게 관리하느냐에 따라 달라집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;만료 관리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JWT는 일반적으로 'exp' 클레임을 포함하여 만료 시간을 정의합니다.&lt;/li&gt;
&lt;li&gt;서버는 이 만료 시간을 확인하고, 만료된 토큰을 거부해야 합니다.&lt;/li&gt;
&lt;li&gt;클라이언트는 만료된 토큰을 감지하고 사용자에게 다시 로그인하거나 토큰을 재발급받을 수 있게 해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재발급&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토큰이 만료되거나 무효화되었을 때, 사용자는 새로운 토큰을 재발급받아야 합니다.&lt;/li&gt;
&lt;li&gt;이는 일반적으로 &quot;refresh token&quot;을 사용하여 이루어집니다.&lt;/li&gt;
&lt;li&gt;Refresh token은 클라이언트가 보안적으로 유효한 상태에서 새로운 access token을 받을 수 있게 해주는 긴 수명을 가진 토큰입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;토큰 무효화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 상황에서는 토큰을 무효화해야 할 필요가 있을 수 있습니다.&lt;/li&gt;
&lt;li&gt;예를 들어 사용자가 로그아웃하거나, 토큰이 유출되었을 때 등입니다.&lt;/li&gt;
&lt;li&gt;이를 위해 몇 가지 방법을 사용할 수 있는데, 예를 들면 토큰을 &lt;b&gt;블랙리스트에 등록&lt;/b&gt;하는 것이 있습니다.&lt;/li&gt;
&lt;li&gt;이 방식을 사용하면 무효화된 토큰이 더 이상 사용되지 않도록 보장할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;b&gt;비밀번호는 프론트엔드에서 관리하기 힘들지 않을까?&lt;/b&gt;&lt;/h2&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;보통 사용자가 로그인할 때, 비밀번호는 프론트엔드에서 입력 받아 서버로 전송하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 HTTPS와 같은 보안된 연결을 사용하여 데이터가 전송되는 도중에 노출되는 것을 방지합니다.&lt;/p&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;백엔드에서 비밀번호는 해시와 솔트(salt)를 사용해 보안 처리를 하게 됩니다.&lt;/b&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프론트엔드에서 JWT와 같은 인증 토큰을 저장하는 방법은 여러 가지가 있습니다&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;쿠키(Cookies)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿠키는 웹사이트가 사용자의 브라우저에 저장하는 작은 데이터 조각입니다.&lt;/li&gt;
&lt;li&gt;HTTP Only 쿠키는 JavaScript를 통해 접근할 수 없으므로 XSS(Cross-Site Scripting) 공격으로부터 안전하다는 장점이 있습니다.&lt;/li&gt;
&lt;li&gt;하지만 CSRF(Cross-Site Request Forgery) 공격에 대해 취약할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;웹 스토리지(Web Storage)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 스토리지에는 두 가지 형태, 즉 로컬 스토리지(Local Storage)와 세션 스토리지(Session Storage)가 있습니다.&lt;/li&gt;
&lt;li&gt;이 둘은 쿠키와 비슷하지만, 더 많은 데이터를 저장할 수 있으며 HTTP 요청과 함께 자동으로 서버로 전송되지 않습니다.&lt;/li&gt;
&lt;li&gt;웹 스토리지는 XSS 공격에 취약할 수 있지만 적절한 콘텐츠 보안 정책(Content Security Policy)을 통해 이를 완화할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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;hr data-ke-style=&quot;style1&quot; /&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;b&gt;토큰들은 어떤 스펙을 보유하고 있을까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT (JSON Web Tokens)는 Access 토큰과 Refresh 토큰으로 주로 사용되며, 각각 다른 목적과 수명을 가지고 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Access 토큰&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Access 토큰은 사용자가 특정 자원에 접근할 권한을 가지고 있는지를 나타냅니다.&lt;/li&gt;
&lt;li&gt;일반적으로 이 토큰은 사용자 세션의 유효성을 나타내며, 짧은 만료 시간을 가집니다 (보통 수 분에서 수 시간).&lt;/li&gt;
&lt;li&gt;Access 토큰에는 다음과 같은 정보가 포함될 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Subject (sub):&lt;/b&gt; 토큰이 대상으로 하는 사용자나 서비스의 ID&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Issuer (iss):&lt;/b&gt; 토큰을 발급한 서비스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Expiration (exp):&lt;/b&gt; 토큰이 만료되는 시간&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Not Before (nbf):&lt;/b&gt; 이 시간 이전에는 토큰이 처리되지 않아야 함을 나타냅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Issued At (iat):&lt;/b&gt; 토큰이 발급된 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Refresh 토큰&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Refresh 토큰은 Access 토큰이 만료되었을 때 새로운 Access 토큰을 발급받는 데 사용됩니다.&lt;/li&gt;
&lt;li&gt;이 토큰은 보통 긴 수명을 가지고 있으며, 경우에 따라 만료되지 않을 수도 있습니다.&lt;/li&gt;
&lt;li&gt;Refresh 토큰은 일반적으로 사용자가 로그인 상태를 유지하면서 새로운 Access 토큰을 안전하게 받을 수 있도록 도와줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Refresh 토큰은 주로 저장소(예: 데이터베이스)에 저장되며, Access 토큰을 발급할 때 이 토큰이 유효한지를 확인합니다.&lt;br /&gt;이렇게&amp;nbsp;하면,&amp;nbsp;Refresh&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;저는 이해가 잘 안되는 것 같아요. 이것도 다른 방법으로 설명해주실 수 있나요?&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론입니다.&amp;nbsp;이를&amp;nbsp;설명하기&amp;nbsp;위해&amp;nbsp;&quot;회원&amp;nbsp;카드와&amp;nbsp;재발급&amp;nbsp;쿠폰&quot;이라는&amp;nbsp;개념을&amp;nbsp;이용하겠습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;1. Access 토큰&lt;/b&gt;&lt;/u&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;이 카드를 통해 자신이 상점의 회원임을 증명하고, 회원 권한을 가진 사람이 사용할 수 있는 특정 상품이나 서비스에 접근할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 회원 카드는 일정 기간 후에 만료가 되는데, 이는 보통의 Access 토큰이 짧은 만료 시간을 가지고 있는 것과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Access 토큰에는 발급자, 대상, 만료 시간 등의 정보가 포함되어 있어, 이를 검증하는 측이 해당 토큰이 유효한지 판단할 수 있게 해줍니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;2. Refresh 토큰&lt;/b&gt;&lt;/u&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;&lt;b&gt;이 쿠폰은 일반적으로 회원 카드보다 훨씬 긴 수명을 가지고 있으며, 이를 통해 새로운 회원 카드를 안전하게 발급받을 수 있습니다. &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로, Refresh 토큰은 Access 토큰이 만료되었을 때 새로운 Access 토큰을 안전하게 발급받을 수 있도록 도와줍니다.&lt;br /&gt;&lt;br /&gt;따라서&amp;nbsp;사용자는&amp;nbsp;Access&amp;nbsp;토큰을&amp;nbsp;이용해서&amp;nbsp;자신의&amp;nbsp;권한을&amp;nbsp;증명하고&amp;nbsp;필요한&amp;nbsp;서비스에&amp;nbsp;접근할&amp;nbsp;수&amp;nbsp;있으며,&amp;nbsp;만약&amp;nbsp;Access&amp;nbsp;토큰이&amp;nbsp;만료되었다면&amp;nbsp;Refresh&amp;nbsp;토큰을&amp;nbsp;이용해&amp;nbsp;새로운&amp;nbsp;Access&amp;nbsp;토큰을&amp;nbsp;발급받을&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런&amp;nbsp;방식으로&amp;nbsp;JWT&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;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;b&gt;추가적으로 JWT에 대해 더 알면 좋을 내용들&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JWT에 대해 더 얘기해볼 만한 주제들이 몇 가지 있습니다. 보안, JWT 구조, 그리고 JWT의 장단점을 포함해서요.&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;JWT의 구조&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JWT는 세 부분으로 이루어져 있습니다: &lt;b&gt;헤더(Header)&lt;/b&gt;, &lt;b&gt;페이로드(Payload)&lt;/b&gt;, 그리고 &lt;b&gt;서명(Signature)&lt;/b&gt;.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;헤더(Header)&lt;/b&gt;는 토큰의 타입과 사용된 알고리즘을 명시합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, { &quot;alg&quot;: &quot;HS256&quot;, &quot;typ&quot;: &quot;JWT&quot; }.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;페이로드(Payload)&lt;/b&gt;에는 &lt;b&gt;클레임(claim)&lt;/b&gt;이라 불리는 선언들이 들어 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이는 사용자에 대한 데이터나 다른 토큰 관련 정보들을 담고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서명(Signature)&lt;/b&gt;은 서버가 헤더와 페이로드를 검증할 수 있게 하는 부분입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서명은 헤더, 페이로드, 그리고 비밀키를 이용해 생성되며, 이 비밀키는 서버에서만 알고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;b&gt;이 세 부분은 각각 Base64Url로 인코딩되어 .으로 연결된 문자열을 형성합니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JWT의 장점과 단점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JWT는 Stateless한 성격으로 인해 수평 확장성이 뛰어나고, 서버의 리소스를 적게 사용하는 장점이 있습니다.&lt;/li&gt;
&lt;li&gt;하지만 토큰 자체가 정보를 가지고 있기 때문에, 한 번 발급된 토큰은 만료 시간이 지나기 전까지는 무효화하기 어렵다는 단점도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JWT는 보안적으로 매우 중요한 역할을 하기 때문에, 올바르게 관리하고 사용하는 것이 중요합니다.&lt;/li&gt;
&lt;li&gt;특히 HS256 (HMAC with SHA-256)와 RS256 (RSA with SHA-256) 같은 강력한 암호화 알고리즘이 사용되어야 하며, 토큰을 보호하기 위해 HTTPS와 같은 보안 프로토콜 위에서 전송되어야 합니다.&lt;/li&gt;
&lt;li&gt;또한, 토큰은 안전한 저장소에 저장되어야 하며, 가능한 XSS와 CSRF 같은 공격으로부터 보호되어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JWT에 대한 공격 시나리오와 대응 방안&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;비밀키 노출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JWT는 비밀키를 통해 서명되므로, 이 키가 노출되면 공격자가 임의의 토큰을 만들 수 있습니다.&lt;/li&gt;
&lt;li&gt;이를 방지하려면 비밀키를 안전하게 보관하고, 정기적으로 키를 교체해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Replay Attacks&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 토큰이 노출되면, 공격자는 그 토큰을 사용해 서버에게 자신을 해당 사용자로 가장할 수 있습니다.&lt;/li&gt;
&lt;li&gt;이를 방지하려면, 토큰을 HTTPS 같은 보안 프로토콜을 통해 전송하고, 가능한 쿠키에 저장하면 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JWT와 OAuth&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JWT는 OAuth와 같은 인증 프로토콜에서 토큰으로 사용될 수 있습니다.&lt;/li&gt;
&lt;li&gt;OAuth는 사용자가 인증 정보를 직접 제공하지 않고도, 인증 제공자(ex. Google, Facebook 등)를 통해 서드 파티 애플리케이션에 인증할 수 있게 하는 프로토콜입니다.&lt;/li&gt;
&lt;li&gt;이때 인증 제공자는 JWT를 생성하여 애플리케이션에 제공하고, 애플리케이션이 이 JWT를 사용하여 사용자를 인증하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JWT 디버깅 및 테스팅 도구&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;jwt.io와 같은 웹사이트는 JWT를 생성하고 디코딩하는 데 도움이 됩니다.&lt;/li&gt;
&lt;li&gt;이는 JWT를 이해하거나 디버깅하는데 유용한 도구입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jwt.io/&quot;&gt;JWT.IO&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1685665793278&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;JWT.IO&quot; data-og-description=&quot;JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.&quot; data-og-host=&quot;jwt.io&quot; data-og-source-url=&quot;https://jwt.io/&quot; data-og-url=&quot;http://jwt.io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Bs47Y/hySQEP0pCo/HMc38kq2kJhyKpAlDB6JZk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/3G75B/hySQFajoY1/jH2G13tu6J6kIuAHK2HYp1/img.png?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512&quot;&gt;&lt;a href=&quot;https://jwt.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jwt.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Bs47Y/hySQEP0pCo/HMc38kq2kJhyKpAlDB6JZk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/3G75B/hySQFajoY1/jH2G13tu6J6kIuAHK2HYp1/img.png?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512');&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;JWT.IO&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jwt.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 주제 외에도 JWT의 사용 사례, 다른 인증 방법과의 비교, 다양한 프로그래밍 언어에서의 JWT 라이브러리 사용 방법 등이 있습니다.&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Backend</category>
      <category>json web token</category>
      <category>jwt</category>
      <category>단점</category>
      <category>디버깅</category>
      <category>백엔드</category>
      <category>장점</category>
      <category>프론트엔드</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/236</guid>
      <comments>https://time-map-installer.tistory.com/236#entry236comment</comments>
      <pubDate>Fri, 2 Jun 2023 09:34:26 +0900</pubDate>
    </item>
    <item>
      <title>[CSS] BEM 방법론에 대해 알아보자(Block, Element, Modifier)</title>
      <link>https://time-map-installer.tistory.com/235</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Group 28.png&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8YSQu/btsiej3yUSB/UkY9COqfjatH36WVgbUGg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8YSQu/btsiej3yUSB/UkY9COqfjatH36WVgbUGg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8YSQu/btsiej3yUSB/UkY9COqfjatH36WVgbUGg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8YSQu%2Fbtsiej3yUSB%2FUkY9COqfjatH36WVgbUGg1%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;469&quot; height=&quot;300&quot; data-filename=&quot;Group 28.png&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;BEM (Block, Element, Modifier)은 CSS 클래스를 명명하는 방법론 중 하나로, HTML과 CSS의 구조를 더 이해하기 쉽고 유지보수하기 쉽게 만들어주는 효과가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BEM의 이름은 아래 세 가지 컴포넌트에 따라 붙여집니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Block (블록)&lt;/li&gt;
&lt;li&gt;Element (요소)&lt;/li&gt;
&lt;li&gt;Modifier (수정자)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Block (블록)&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Element (요소)&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;3. Modifier (수정자)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정자는 블록이나 요소의 상태와 행동을 정의합니다. 예를 들면, 비활성화 상태, 체크 상태, 다른 테마 등이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BEM 네이밍 규칙은 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;블록과 요소는 &lt;code&gt;__&lt;/code&gt; (두 개의 언더스코어)로 연결합니다. 예: &lt;code&gt;block__element&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;블록 또는 요소와 수정자는 &lt;code&gt;-&lt;/code&gt; (두 개의 하이픈)으로 연결합니다. 예: &lt;code&gt;block--modifier&lt;/code&gt;, &lt;code&gt;block__element--modifier&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML과 CSS 예시를 통해 BEM을 이해해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;menu&quot;&amp;gt;
  &amp;lt;button class=&quot;menu__item&quot;&amp;gt;Item 1&amp;lt;/button&amp;gt;
  &amp;lt;button class=&quot;menu__item menu__item--disabled&quot;&amp;gt;Item 2&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.menu { /* block */ }

.menu__item { /* element */ }

.menu__item--disabled { /* modifier */ }
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서, &lt;code&gt;menu&lt;/code&gt;는 블록, &lt;code&gt;menu__item&lt;/code&gt;은 요소, &lt;code&gt;menu__item--disabled&lt;/code&gt;는 수정자입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정자 &lt;code&gt;--disabled&lt;/code&gt;는 &lt;code&gt;menu__item&lt;/code&gt; 요소의 상태를 변경합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BEM (Block, Element, Modifier)에 대해 간단한 비유를 들어보겠습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BEM을 자동차로 비유해보면 이해하기 더 쉬울 것입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Block (블록)&lt;/b&gt;: 이것은 자동차 자체를 의미합니다. 각 부품 없이는 자동차를 운행할 수 없으며, 그 자체로 완전한 기능을 가진 독립적인 단위입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Element (요소)&lt;/b&gt;: 이것은 자동차의 부품, 예를 들면 바퀴, 핸들, 브레이크 등을 의미합니다. 이들은 자동차(Block) 내에서 의미를 가집니다. 자동차 없이 바퀴나 핸들은 그 자체로 기능을 가지지 못합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Modifier (수정자)&lt;/b&gt;: 이것은 자동차의 특정 상태나 행동을 의미합니다. 예를 들어, &quot;달리는 자동차&quot;, &quot;파란색 자동차&quot; 등과 같이 블록이나 요소의 상태나 행동을 변경합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 적용해보면 다음과 같습니다:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;car&quot;&amp;gt;
  &amp;lt;div class=&quot;car__wheel&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;car__wheel&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;car__wheel&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;car__wheel&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;car__handle&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;car__brake&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class=&quot;car car--running&quot;&amp;gt;
  &amp;lt;div class=&quot;car__wheel&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;car__wheel&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;car__wheel&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;car__wheel&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;car__handle&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;car__brake&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;car&lt;/code&gt;는 블록, &lt;code&gt;car__wheel&lt;/code&gt;, &lt;code&gt;car__handle&lt;/code&gt;, &lt;code&gt;car__brake&lt;/code&gt;는 요소, &lt;code&gt;car--running&lt;/code&gt;은 수정자에 해당합니다. 이처럼 BEM 방법론은 웹 사이트의 다양한 요소들을 구조적으로 이해하고 관리하는 데 도움을 줍니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 BEM을 사용하면, 코드를 훨씬 더 쉽게 이해하고 유지보수할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BEM 방법론은 CSS 클래스의 이름을 일관성 있게 유지하고, 중복을 최소화하며, 스타일을 예측 가능하게 만드는 등의 이점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 대형 프로젝트에서 CSS의 복잡성을 관리하는데 효과적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 BEM을 사용할 때는 클래스 이름이 길어질 수 있으므로 상황에 맞게 알맞은 방법을 찾아보아야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Next.js에서 BEM 방법론 적용하려면?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js를 이용하여 개발하는 사람들에게도 BEM 방법론이 도움이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 React 기반의 프레임워크로, JSX에서는 CSS 클래스 이름에 하이픈(-)을 사용할 수 있지만, JavaScript의 객체 키에 하이픈을 사용하는 것은 문법 오류를 유발합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, CSS in JS 방식을 사용할 때 JavaScript 객체로 스타일을 정의하게 되며, 이때 'Block--Modifier' 형태의 BEM 명명법을 그대로 사용할 수 없게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해, 'Block--Modifier' 대신 'BlockModifier' 형태의 CamelCase 명명법을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 불구하고, 'Block__Element'는 '__' 구분자에 문제가 없으므로 그대로 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 적용한 예시를 살펴보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// CSS in JS
const styles = {
  menu: { /* block styles */ },
  menu__item: { /* element styles */ },
  menu__item__disabledContents: { /* modifier styles */ }
};

// JSX
&amp;lt;div style={styles.menu}&amp;gt;
  &amp;lt;button style={styles.menu__item}&amp;gt;Item 1&amp;lt;/button&amp;gt;
  &amp;lt;button style={styles.menu__item, styles.menu__item__disabledContents}&amp;gt;Item 2&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 'menu', 'menu__item', 'menu__item__disabledContents'는 각각 블록, 요소, 수정자에 해당하며, camelCase 명명법을 사용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 camelCase를 사용하면 JavaScript 환경에서의 문법 문제를 피하면서도 BEM 방식의 일관성 있는 클래스 이름 규칙을 유지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>BEM</category>
      <category>BLOCK</category>
      <category>CSS</category>
      <category>element</category>
      <category>Modifier</category>
      <category>방법론</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/235</guid>
      <comments>https://time-map-installer.tistory.com/235#entry235comment</comments>
      <pubDate>Thu, 1 Jun 2023 18:37:02 +0900</pubDate>
    </item>
    <item>
      <title>[Backend] 로그인 양대 산맥, JWT와 Session 중 무엇을 선택해야할까?</title>
      <link>https://time-map-installer.tistory.com/234</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbZOQ8/btsidlTCKfW/AVQmiMbYrXWNHVzyf2cuvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbZOQ8/btsidlTCKfW/AVQmiMbYrXWNHVzyf2cuvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbZOQ8/btsidlTCKfW/AVQmiMbYrXWNHVzyf2cuvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbZOQ8%2FbtsidlTCKfW%2FAVQmiMbYrXWNHVzyf2cuvK%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;938&quot; height=&quot;600&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말해보자면 JWT를 많이 사용합니다.&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;세션과 JWT, 이 두 방법은 어떤 차이가 있는지 알기쉽게  설명 해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;세션(Session),  고속도로 일반 결제 창구&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;세션 방식&lt;/b&gt;을 사용하는 것은 &lt;b&gt;고속도로의 일반 결제창구&lt;/b&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;이 기록은 서버의 메모리에 저장되며, 차량이 고속도로를 빠져나갈 때까지 유지됩니다.&lt;/p&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sfMny/btsijnXhWTS/1p8KX1dOCFg5XUq5gfxkjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sfMny/btsijnXhWTS/1p8KX1dOCFg5XUq5gfxkjK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1388&quot; data-origin-height=&quot;1160&quot; data-filename=&quot;스크린샷 2023-06-01 오후 1.14.26.png&quot; style=&quot;width: 48.449%; margin-right: 10px;&quot; data-widthpercent=&quot;49.02&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sfMny/btsijnXhWTS/1p8KX1dOCFg5XUq5gfxkjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsfMny%2FbtsijnXhWTS%2F1p8KX1dOCFg5XUq5gfxkjK%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;1388&quot; height=&quot;1160&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MxqhZ/btsiboKizrV/B9T5JcPQKKhmcBG2j9ZKo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MxqhZ/btsiboKizrV/B9T5JcPQKKhmcBG2j9ZKo1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;1170&quot; data-filename=&quot;스크린샷 2023-06-01 오후 1.16.48.png&quot; style=&quot;width: 50.3882%;&quot; data-widthpercent=&quot;50.98&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MxqhZ/btsiboKizrV/B9T5JcPQKKhmcBG2j9ZKo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMxqhZ%2FbtsiboKizrV%2FB9T5JcPQKKhmcBG2j9ZKo1%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;1456&quot; height=&quot;1170&quot;/&gt;&lt;/span&gt;&lt;/div&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JWT(JSON Web Token), 하이패스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에, &lt;b&gt;JWT&lt;/b&gt;는 고속도로의 &lt;b&gt;하이패스 시스템&lt;/b&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;이 하이패스 장치는 운전자의 정보와 통행료 정보가 미리 저장되어 있어, 창구를 통과하는 순간에 빠르게 처리됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 효율적이며, 서버의 메모리 부하도 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하지만, 하이패스 장치가 도난당하거나 잘못된 손에 넘어가면 문제가 발생할 수 있습니다. &lt;/b&gt;&lt;/p&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;새로운 토큰으로 갱신하는 방법(Refresh 토큰)을 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwDQFK/btsiej8ZrML/OKS3L1ifuE8NKEp5lG7bS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwDQFK/btsiej8ZrML/OKS3L1ifuE8NKEp5lG7bS1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;1178&quot; data-filename=&quot;스크린샷 2023-06-01 오후 1.14.59.png&quot; style=&quot;width: 49.6307%; margin-right: 10px;&quot; data-widthpercent=&quot;50.21&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwDQFK/btsiej8ZrML/OKS3L1ifuE8NKEp5lG7bS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwDQFK%2Fbtsiej8ZrML%2FOKS3L1ifuE8NKEp5lG7bS1%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;1446&quot; height=&quot;1178&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lDGBk/btsibbKTHOE/5JoyY8U1NDjdyI2WxCZTk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lDGBk/btsibbKTHOE/5JoyY8U1NDjdyI2WxCZTk1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;1152&quot; data-filename=&quot;스크린샷 2023-06-01 오후 1.17.20.png&quot; style=&quot;width: 49.2065%;&quot; data-widthpercent=&quot;49.79&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lDGBk/btsibbKTHOE/5JoyY8U1NDjdyI2WxCZTk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlDGBk%2FbtsibbKTHOE%2F5JoyY8U1NDjdyI2WxCZTk1%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;1402&quot; height=&quot;1152&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt; JWT 기반 인증 방식(좌), 하이패스 방식(우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;+ 쿠키(Cookie), 화물 차량용 하이패스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로,&amp;nbsp;쿠키는&amp;nbsp;&lt;b&gt;웹&amp;nbsp;브라우저에서만&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;기능&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 고속도로의&amp;nbsp;&lt;b&gt;특정 차량만이 사용할 수 있는 특별한 하이패스 장치&lt;/b&gt;로 생각할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 JWT는 모든 차량이 사용할 수 있는 범용적인 하이패스 장치와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;따라서 모바일 애플리케이션 같은 다양한 환경에서는 JWT를 사용한 인증 방식이 더 효과적입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;세션 기반 인증&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;사용자가 로그인하면 서버는 사용자의 세션 정보를 저장하고 사용자에게 세션 ID를 부여합니다.&lt;/li&gt;
&lt;li&gt;이 세션 ID는 쿠키에 저장되어 사용자의 브라우저와 서버 사이에서 주고받아집니다.&lt;/li&gt;
&lt;li&gt;사용자가 다음 페이지를 요청할 때마다, 쿠키에 저장된 세션 ID가 서버에 전달되어 사용자의 인증 정보를 확인합니다.&lt;/li&gt;
&lt;li&gt;이 방식은 마치 고속도로에서 차량이 통행료를 납부하고 통행을 위한 입출구 정보를 받는 것과 유사합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JWT 기반 인증&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 로그인하면 서버는 사용자의 정보를 바탕으로 JWT를 생성하고 이를 사용자에게 전달합니다.&lt;/li&gt;
&lt;li&gt;사용자는 이 토큰을 저장하고, 서버에 요청을 보낼 때마다 토큰을 함께 전달하여 자신을 인증합니다.&lt;/li&gt;
&lt;li&gt;이 방식은 마치 고속도로에서 차량이 하이패스 장치를 통해 자동으로 통행료를 결제하고 통행하는 것과 유사합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 기반 인증은 서버의 메모리 부담이 있지만, JWT 기반 인증은 토큰의 보안 문제가 있을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 보안 문제를 완화하기 위해 JWT의 유효기간을 짧게 설정하고, 새로운 토큰으로 갱신하는 방법(Refresh 토큰)을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 쿠키는 웹 브라우저에서만 사용할 수 있으므로, 모바일 앱 같은 다른 환경에서는 JWT를 사용한 인증 방식이 더 적합합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Backend</category>
      <category>Backend</category>
      <category>jwt</category>
      <category>로그인</category>
      <category>백엔드</category>
      <category>세션</category>
      <category>인증</category>
      <category>확장성</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/234</guid>
      <comments>https://time-map-installer.tistory.com/234#entry234comment</comments>
      <pubDate>Thu, 1 Jun 2023 13:29:43 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript, React] const = () =&amp;gt; {}? function () {}?  함수 표현식, 함수 선언문 중 무엇을 쓸까?</title>
      <link>https://time-map-installer.tistory.com/233</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0i7FC/btshCf1z22w/6vBzIq17cnhkcz4lEKFd01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0i7FC/btshCf1z22w/6vBzIq17cnhkcz4lEKFd01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0i7FC/btshCf1z22w/6vBzIq17cnhkcz4lEKFd01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0i7FC%2FbtshCf1z22w%2F6vBzIq17cnhkcz4lEKFd01%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;626&quot; height=&quot;150&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;자바스크립트에서 함수를 작성하는 방법은 여러 가지가 있습니다.&lt;br&gt;그 중에서도 함수 선언문, 함수 표현식, 그리고 화살표 함수에 대해 알아보겠습니다.&lt;br&gt;이들의 차이점은 무엇이고 어떤 상황에서 어떤 것을 사용하면 좋을까요?&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 리액트에서의 함수 표현식과 선언문&lt;/b&gt;&lt;/h2&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2548&quot; data-origin-height=&quot;1426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7m0yu/btshE9zUheJ/azAxnbT4xa2C1Pd2g04pp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7m0yu/btshE9zUheJ/azAxnbT4xa2C1Pd2g04pp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7m0yu/btshE9zUheJ/azAxnbT4xa2C1Pd2g04pp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7m0yu%2FbtshE9zUheJ%2FazAxnbT4xa2C1Pd2g04pp1%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;2548&quot; height=&quot;1426&quot; data-origin-width=&quot;2548&quot; data-origin-height=&quot;1426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;리액트에서 컴포넌트 함수를 작성할 때, 함수 선언문과 함수 표현식 중 어떤 것을 사용해야 할까요?&lt;br&gt;여기에는 정답이 없지만, 두 방식 간의 차이점을 이해하고 상황에 맞게 적절하게 선택하는 것이 중요합니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;함수 선언문(&lt;u&gt;function () {}&lt;/u&gt;)&lt;/b&gt;을 사용하면 메인 로직을 한눈에 보여주고, export default와 선언을 동시에 할 수 있습니다.&lt;br&gt;그런데 이 경우, 일반적인 자바스크립트에서 함수 표현식을 사용할 때의 장점을 일부 포기하게 됩니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;반면, &lt;b&gt;함수 표현식(&lt;u&gt;const = () =&amp;gt; {}&lt;/u&gt;)&lt;/b&gt;을 사용하면 일반적인 자바스크립트에서의 장점을 살릴 수 있습니다.&lt;br&gt;그러나 메인 로직에서만 함수 선언문을 사용하면 코드가 일관성이 떨어질 수 있습니다. 이는 함수 선언문과 함수 표현식이 혼용되어 사용되면서 발생하는 문제입니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;그럼 함수 선언문과 표현식의 차이점은 무엇인가요?&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;JavaScript에서 함수 선언문과 함수 표현식은 비슷한 기능을 제공하지만, 중요한 차이점들이 있습니다.&lt;br&gt;이러한 차이점들은 각각의 사용에 영향을 미치며, 이에 따라 특정 상황에 더 적합하게 됩니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;&lt;u&gt;1. 함수 선언문 (Function Declarations):&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function myFunction() {
    // function body
}

&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;함수 선언문은 호이스팅 (hoisting)이 일어납니다.&lt;br&gt;이는 함수가 선언되기 전에 호출할 수 있음을 의미합니다.&lt;br&gt;이러한 특성은 큰 프로그램에서 유용할 수 있지만, 예상치 못한 동작을 유발할 수도 있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;&lt;u&gt;2. 함수 표현식 (Function Expressions):&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;let myFunction = function() {
    // function body
}

&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;함수 표현식은 호이스팅되지 않습니다.&lt;br&gt;이는 함수가 선언되기 전에 호출할 수 없다는 것을 의미합니다.&lt;br&gt;또한 함수 표현식을 사용하면 함수를 변수에 할당할 수 있고, 이를 다른 함수에 인자로 전달하거나, 객체의 메소드로 사용하거나, 배열의 원소로 저장하는 등 더 많은 유연성을 제공합니다.&lt;br&gt;또한, 자바스크립트에서 함수 표현식은 익명 함수(이름 없는 함수)를 사용할 수 있게 해주는데, 이는 클로저, 콜백, 즉시 실행 함수 등에서 유용하게 사용됩니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;함수 표현식을 사용하는 경우, 그 특정한 장점들을 활용할 수 있습니다.&lt;/b&gt;&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt; 
 &lt;li&gt;&lt;b&gt;익명 함수와 클로저&lt;/b&gt; 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;&amp;nbsp;함수 표현식은 익명 함수를 생성하거나 클로저를 사용하는데 유용합니다.&lt;/li&gt; 
   &lt;li&gt;클로저는 함수와 그 함수가 선언될 때의 렉시컬 환경과의 조합입니다.&lt;/li&gt; 
   &lt;li&gt;이는 함수가 자신이 선언된 스코프에서 액세스할 수 있었던 모든 변수를 '기억'하는 능력을 가집니다.&lt;/li&gt; 
   &lt;li&gt;이는 함수가 선언된 곳에서의 변수를 활용하는 데 매우 유용하며, 이러한 패턴은 모듈 패턴, 콜백, 이벤트 핸들러 등에서 자주 볼 수 있습니다.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;변수로의 할당&lt;/b&gt; 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;&amp;nbsp;함수 표현식은 변수에 할당될 수 있기 때문에, 다른 함수의 매개변수로 전달하거나 다른 함수에서 반환하는 등의 동작이 가능합니다.&lt;/li&gt; 
   &lt;li&gt;이는 함수형 프로그래밍에서 매우 중요한 특성입니다.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;&lt;b&gt;Arrow 함수&lt;/b&gt; 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;&amp;nbsp;Arrow 함수는 함수 표현식의 특별한 형태로, this 바인딩을 갖지 않는 것이 특징입니다.&lt;/li&gt; 
   &lt;li&gt;즉, Arrow 함수 내부에서 this를 참조하면, 해당 함수를 둘러싼 코드의 this를 가리킵니다.&lt;/li&gt; 
   &lt;li&gt;이러한 특성은 콜백 함수에서 매우 유용합니다.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;따라서, 둘 중 어떤 것을 선택할지는 코드의 목적과 개인의 코딩 스타일에 따라 달라집니다.&lt;br&gt;일관성 있는 코드를 작성하려면 하나의 방식을 선택하여&amp;nbsp;일관되게 사용하는 것이 좋습니다.&lt;/blockquote&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&gt;&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. this에 대한 이해&lt;/b&gt;&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;자바스크립트에서 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;는 상당히 복잡하게 작동합니다.&lt;br&gt;함수의 유형에 따라 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;가 어떻게 작동하는지 알아봅니다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2574&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UFrlo/btshAZx5I8K/jUuo8KLsmicY6ITTLdDlbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UFrlo/btshAZx5I8K/jUuo8KLsmicY6ITTLdDlbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UFrlo/btshAZx5I8K/jUuo8KLsmicY6ITTLdDlbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUFrlo%2FbtshAZx5I8K%2FjUuo8KLsmicY6ITTLdDlbk%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;2574&quot; height=&quot;834&quot; data-origin-width=&quot;2574&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1) 일반 함수에서의 this&lt;/b&gt;&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;전역 함수에서:&lt;/b&gt; 함수를 호출하면 기본적으로 전역 객체를 가리킵니다. (일반적으로 window 객체)&lt;/li&gt;&lt;li&gt;&lt;b&gt;메소드 함수에서:&lt;/b&gt; 메소드 함수를 호출하면 메소드를 소유한 객체를 가리킵니다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;생성자에서:&lt;/b&gt; 생성자를 호출하면 새로 생성된 인스턴스를 가리킵니다.&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2) 화살표 함수에서의 this&lt;/b&gt;&lt;/h3&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;화살표 함수에서는 자체적으로 가리키는 this가 없습니다. 대신 상위 스코프의 this를 가리킵니다.&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;u&gt;조금 더 자세히 알아볼까요?&lt;/u&gt;&lt;/b&gt;&lt;br&gt;먼저, JavaScript에서 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;는 실행 컨텍스트에 따라 동적으로 결정되는 특수한 변수입니다.&lt;br&gt;이를 이해하기 위해 다음의 경우들을 자세히 살펴보겠습니다.&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 일반 함수에서의 this&lt;/b&gt;&lt;/h4&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
 &lt;li&gt;&lt;b&gt;전역 함수에서&lt;/b&gt; 
  &lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt; 
   &lt;li&gt;JavaScript에서 함수를 전역 스코프에서 호출하면, this는 전역 객체를 가리킵니다.&lt;/li&gt; 
   &lt;li&gt;웹 브라우저에서는 전역 객체가 window입니다.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function globalFunction() {
&amp;nbsp;&amp;nbsp;console.log(this); // 'this'는 window 객체를 가리킨다.
}

globalFunction(); // 로그: Window {...}&lt;/code&gt;&lt;/pre&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;메소드 함수에서:&lt;/b&gt; 객체의 메소드로 함수를 호출하면, this는 그 함수를 호출한 객체를 가리킵니다.&lt;/li&gt;&lt;/ul&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const myObject = {
&amp;nbsp;&amp;nbsp;property: 'I am an object property',
&amp;nbsp;&amp;nbsp;method: function() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log(this.property); // 'this'는 myObject를 가리킨다.
&amp;nbsp;&amp;nbsp;}
}

myObject.method(); // 로그: &quot;I am an object property&quot;&lt;/code&gt;&lt;/pre&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;생성자에서:&lt;/b&gt; new 키워드로 생성자 함수를 호출하면, this는 생성된 인스턴스를 가리킵니다.&lt;/li&gt;&lt;/ul&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function MyConstructor() {
&amp;nbsp;&amp;nbsp;this.property = 'I am an instance property';
}

const newInstance = new MyConstructor();
console.log(newInstance.property); // 로그: &quot;I am an instance property&quot;&lt;/code&gt;&lt;/pre&gt;&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 화살표 함수에서의 this&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;화살표 함수(arrow function)는 자체적으로 this 바인딩을 생성하지 않습니다.&lt;br&gt;대신, 화살표 함수는 this 값을 감싸는 (즉, 둘러싼) 함수나 코드의 this를 가리킵니다.&lt;br&gt;이를 렉시컬 바인딩(lexical binding)이라고 부릅니다.&lt;/p&gt;&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const myObject = {
  property: 'I am an object property',
  method: function() {
    // 화살표 함수 내부에서의 'this'는 method를 둘러싼 함수의 'this'를 가리킵니다.
    const arrowFunction = () =&amp;gt; console.log(this.property);
    arrowFunction();
  }
}

myObject.method(); // 로그: &quot;I am an object property&quot;&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;여기서 &lt;b&gt;&lt;u&gt;arrowFunction&lt;/u&gt;&lt;/b&gt; 내의 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;는 method 함수의 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;를 가리킵니다.&lt;br&gt;이것은 method 함수가 호출되면 &lt;b&gt;&lt;u&gt;myObject&lt;/u&gt;&lt;/b&gt;를 가리키기 때문에, &lt;b&gt;&lt;u&gt;arrowFunction&lt;/u&gt;&lt;/b&gt;에서의 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt; 역시 &lt;b&gt;&lt;u&gt;myObject&lt;/u&gt;&lt;/b&gt;를 가리키게 됩니다.&lt;br&gt;이렇게 this가 동작하는 방식은 JavaScript에서 중요한 개념입니다.&lt;br&gt;이는 함수가 어떻게 호출되는지에 따라 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;의 값이 달라지기 때문입니다.&lt;br&gt;이러한 이유로 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;를 사용할 때는 항상 주의해야 합니다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&gt;&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Arguments에 대한 이해&lt;/b&gt;&lt;/h2&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;일반 함수에서:&lt;/b&gt; &lt;b&gt;&lt;u&gt;arguments&lt;/u&gt;&lt;/b&gt;를 사용할 수 있습니다. 이는 함수가 호출될 때 전달된 인자들의 정보를 담고 있는 객체입니다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;화살표 함수에서:&lt;/b&gt; 화살표 함수에서는 &lt;b&gt;&lt;u&gt;arguments&lt;/u&gt;&lt;/b&gt;를 사용할 경우 에러가 발생합니다. 화살표 함수에서는 대신 rest 파라미터를 사용하여 &lt;b&gt;&lt;u&gt;arguments&lt;/u&gt;&lt;/b&gt;를 대체할 수 있습니다.&lt;/li&gt;&lt;/ul&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&gt;&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 생성자 함수의 사용&lt;/b&gt;&lt;/h2&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;일반 함수에서:&lt;/b&gt; 생성자 함수를 사용할 수 있습니다. 이는 new 키워드와 함께 함수를 호출하여 새 객체를 생성하고 초기화하는 데 사용됩니다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;화살표 함수에서:&lt;/b&gt; 화살표 함수에서는 this가 렉시컬 스코프를 가리키기 때문에 생성자 함수를 사용할 수 없습니다.&lt;/li&gt;&lt;/ul&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;함수 선언문, 함수 표현식, 화살표 함수의 가장 큰 차이점은 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;입니다.&lt;br&gt;일반 함수는 동적으로 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;가 결정되지만, 화살표 함수는 정적으로 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;가 결정됩니다.&lt;br&gt;따라서 이벤트 호출이나 콜백 함수를 사용할 때 &lt;b&gt;&lt;u&gt;this&lt;/u&gt;&lt;/b&gt;를 어떻게 사용하냐에 따라 적절한 함수를 선택할 수 있습니다.&lt;br&gt;&lt;br&gt;+ 최신 버전의 JS에서 export const도 지원한다고 합니다. 이제 선택할 때 고려할 부분이 하나 줄었네요!&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;References&lt;/p&gt;&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;&lt;div class=&quot;moreless-content&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hhyemi.github.io/2021/06/09/arrow.html&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://hhyemi.github.io/2021/06/09/arrow.html&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;JavaScript - 화살표 함수와 일반 함수의 차이 - CODE:H&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;화살표 함수(Arrow Function)란 화살표함수는 ES6에서 새로 추가된 내용이다. 기존 함수 표현식과 비교하면 간결한표현으로 간단하게 사용가능하다. function fun() { // 일반함수 ... } const arrFun = () =&gt; { // &quot; data-og-host=&quot;hhyemi.github.io&quot; data-og-source-url=&quot;https://hhyemi.github.io/2021/06/09/arrow.html&quot; data-og-url=&quot;https://hhyemi.github.io/2021/06/09/arrow.html&quot;&gt;&lt;a href=&quot;https://hhyemi.github.io/2021/06/09/arrow.html&quot; target=&quot;_blank&quot; data-source-url=&quot;https://hhyemi.github.io/2021/06/09/arrow.html&quot;&gt;&lt;div class=&quot;og-image&quot;&gt;&lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;JavaScript - 화살표 함수와 일반 함수의 차이 - CODE:H&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;화살표 함수(Arrow Function)란 화살표함수는 ES6에서 새로 추가된 내용이다. 기존 함수 표현식과 비교하면 간결한표현으로 간단하게 사용가능하다. function fun() { // 일반함수 ... } const arrFun = () =&amp;gt; { // &lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;hhyemi.github.io&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://developer0809.tistory.com/102&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://developer0809.tistory.com/102&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[#. React] React functional 함수형 컴포넌트에서 const(arrow function) vs function 차이점&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;React Hook을 사용하는 함수형 컴포넌트에서 ① const(Arrow Function) import React from 'react'; const TestPage = () =&gt; { return( TEST ) } export default TestPage; ② Function import React from 'react'; function TestPage() { return( TEST ) } e&quot; data-og-host=&quot;developer0809.tistory.com&quot; data-og-source-url=&quot;https://developer0809.tistory.com/102&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/T3I4m/hySPkDkEPi/8g4DulS8GZdZyO7cbXSyvK/img.png?width=186&amp;amp;height=38&amp;amp;face=0_0_186_38,https://scrap.kakaocdn.net/dn/u1BYF/hySPiesbuH/0px0nLfDF10x09eCZ6Fy00/img.png?width=186&amp;amp;height=38&amp;amp;face=0_0_186_38,https://scrap.kakaocdn.net/dn/o7dJW/hySNhnXkWW/P1jwOG84kAcYryJ1OkiRaK/img.png?width=264&amp;amp;height=200&amp;amp;face=0_0_264_200&quot; data-og-url=&quot;https://developer0809.tistory.com/102&quot;&gt;&lt;a href=&quot;https://developer0809.tistory.com/102&quot; target=&quot;_blank&quot; data-source-url=&quot;https://developer0809.tistory.com/102&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/T3I4m/hySPkDkEPi/8g4DulS8GZdZyO7cbXSyvK/img.png?width=186&amp;amp;height=38&amp;amp;face=0_0_186_38,https://scrap.kakaocdn.net/dn/u1BYF/hySPiesbuH/0px0nLfDF10x09eCZ6Fy00/img.png?width=186&amp;amp;height=38&amp;amp;face=0_0_186_38,https://scrap.kakaocdn.net/dn/o7dJW/hySNhnXkWW/P1jwOG84kAcYryJ1OkiRaK/img.png?width=264&amp;amp;height=200&amp;amp;face=0_0_264_200')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;[#. React] React functional 함수형 컴포넌트에서 const(arrow function) vs function 차이점&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;React Hook을 사용하는 함수형 컴포넌트에서 ① const(Arrow Function) import React from 'react'; const TestPage = () =&amp;gt; { return( TEST ) } export default TestPage; ② Function import React from 'react'; function TestPage() { return( TEST ) } e&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;developer0809.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://velog.io/@jjunyjjuny/React-함수-컴포넌트-선언문-vs-표현식은-취향-차이일까&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://velog.io/@jjunyjjuny/React-함수-컴포넌트-선언문-vs-표현식은-취향-차이일까&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[ React ] 함수 컴포넌트, 선언문 vs 표현식은 취향 차이일까?&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;React의 컴포넌트 함수를 선언하는 두 가지 방법.&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@jjunyjjuny/React-함수-컴포넌트-선언문-vs-표현식은-취향-차이일까&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DCaZQ/hySPnGPxfB/2BA17Gu3adBGOI6OiKsan0/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/nCp59/hySPeJSDO1/7ukSSdkQFI9nvuKrHaebA0/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/skNDD/hySM7exsEH/G65FQFfMs2DsUtqnNEwYDK/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350&quot; data-og-url=&quot;https://velog.io/@jjunyjjuny/React-함수-컴포넌트-선언문-vs-표현식은-취향-차이일까&quot;&gt;&lt;a href=&quot;https://velog.io/@jjunyjjuny/React-함수-컴포넌트-선언문-vs-표현식은-취향-차이일까&quot; target=&quot;_blank&quot; data-source-url=&quot;https://velog.io/@jjunyjjuny/React-함수-컴포넌트-선언문-vs-표현식은-취향-차이일까&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DCaZQ/hySPnGPxfB/2BA17Gu3adBGOI6OiKsan0/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/nCp59/hySPeJSDO1/7ukSSdkQFI9nvuKrHaebA0/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/skNDD/hySM7exsEH/G65FQFfMs2DsUtqnNEwYDK/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;[ React ] 함수 컴포넌트, 선언문 vs 표현식은 취향 차이일까?&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;React의 컴포넌트 함수를 선언하는 두 가지 방법.&lt;/p&gt;&lt;p class=&quot;og-host&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;&lt;a href=&quot;https://chaeyoung2.tistory.com/76&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://chaeyoung2.tistory.com/76&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;화살표 함수(arrowFunction)와 일반함수(function)의 차이점 알아보기&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;자바스크립트에서 일반함수와 화살표함수 두가지의 함수가 있다. 이 두 함수는 기능이 다르다. 차이점을 알아보자 ⭐1. this 1) 일반함수 - 일반적으로 함수를 호출할 경우 global 객체를 가리킨다(&quot; data-og-host=&quot;chaeyoung2.tistory.com&quot; data-og-source-url=&quot;https://chaeyoung2.tistory.com/76&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cBPqrW/hySPqQ5hBs/s8npwlVg1hEtKhxk96O7wk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/Py0op/hySNdFQRy2/zr8G5VjovMKXRulRR841j1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot; data-og-url=&quot;https://chaeyoung2.tistory.com/76&quot;&gt;&lt;a href=&quot;https://chaeyoung2.tistory.com/76&quot; target=&quot;_blank&quot; data-source-url=&quot;https://chaeyoung2.tistory.com/76&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cBPqrW/hySPqQ5hBs/s8npwlVg1hEtKhxk96O7wk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/Py0op/hySNdFQRy2/zr8G5VjovMKXRulRR841j1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;화살표 함수(arrowFunction)와 일반함수(function)의 차이점 알아보기&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;자바스크립트에서 일반함수와 화살표함수 두가지의 함수가 있다. 이 두 함수는 기능이 다르다. 차이점을 알아보자 ⭐1. this 1) 일반함수 - 일반적으로 함수를 호출할 경우 global 객체를 가리킨다(&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;chaeyoung2.tistory.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;/div&gt;&lt;/div&gt;</description>
      <category>Development Study/Frontend</category>
      <category>const</category>
      <category>function</category>
      <category>hoisting</category>
      <category>JavaScript</category>
      <category>함수 선언식</category>
      <category>함수 표현식</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/233</guid>
      <comments>https://time-map-installer.tistory.com/233#entry233comment</comments>
      <pubDate>Tue, 30 May 2023 11:14:35 +0900</pubDate>
    </item>
    <item>
      <title>사이드 메뉴 확인방법</title>
      <link>https://time-map-installer.tistory.com/notice/232</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ElGk9/btshytrazqv/O8HHRRpppPeNWtKW7y3H4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ElGk9/btshytrazqv/O8HHRRpppPeNWtKW7y3H4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ElGk9/btshytrazqv/O8HHRRpppPeNWtKW7y3H4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FElGk9%2Fbtshytrazqv%2FO8HHRRpppPeNWtKW7y3H4K%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;662&quot; height=&quot;720&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;720&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;MacOS 기준 &lt;u&gt;&lt;b&gt;command + '+'&lt;/b&gt;&lt;/u&gt; or &lt;u&gt;&lt;b&gt;command + '-'&lt;/b&gt;&lt;/u&gt;를 이용해 조절할 수 있습니다.&lt;/p&gt;</description>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/notice/232</guid>
      <pubDate>Fri, 26 May 2023 16:19:14 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] 도커는 뭐하는 고래일까? + EC2에서 Docker 띄워보기 실습</title>
      <link>https://time-map-installer.tistory.com/231</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1067&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qGuzu/btshy7AQzPs/kkRbaW87vtGDAPAQQlY5ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qGuzu/btshy7AQzPs/kkRbaW87vtGDAPAQQlY5ZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qGuzu/btshy7AQzPs/kkRbaW87vtGDAPAQQlY5ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqGuzu%2Fbtshy7AQzPs%2FkkRbaW87vtGDAPAQQlY5ZK%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;400&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1067&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는&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;Docker는&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;br /&gt;&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;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 이 Docker에 대해 알아보도록 하겠습니다.&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;AWS Series&lt;/h2&gt;
&lt;figure id=&quot;og_1685083513165&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;[Learn About AWS] Series&quot; data-og-description=&quot;이 글은 AWS 수업 시간에 진행되는 내용들을 담아 둔 시리즈이다 상반기동안 업데이트 할 예정이며 최대한 위에있는 이미지처럼 웃으면서 할 것이다 [이론] [Computer Science] 네트워크 기초 익혀먹&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/181&quot; data-og-url=&quot;https://time-map-installer.tistory.com/181&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kP3XP/hySKBAf79R/J51JVWCT53rKlGkDThVsY0/img.png?width=259&amp;amp;height=194&amp;amp;face=0_0_259_194,https://scrap.kakaocdn.net/dn/cydAJS/hySLEPHLJF/GKhFbwdG88V4Pb2slUsKCk/img.png?width=259&amp;amp;height=194&amp;amp;face=0_0_259_194&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/181&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/181&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kP3XP/hySKBAf79R/J51JVWCT53rKlGkDThVsY0/img.png?width=259&amp;amp;height=194&amp;amp;face=0_0_259_194,https://scrap.kakaocdn.net/dn/cydAJS/hySLEPHLJF/GKhFbwdG88V4Pb2slUsKCk/img.png?width=259&amp;amp;height=194&amp;amp;face=0_0_259_194');&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;[Learn About AWS] Series&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 AWS 수업 시간에 진행되는 내용들을 담아 둔 시리즈이다 상반기동안 업데이트 할 예정이며 최대한 위에있는 이미지처럼 웃으면서 할 것이다 [이론] [Computer Science] 네트워크 기초 익혀먹&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;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;h2 data-ke-size=&quot;size26&quot;&gt;Docker란 무엇일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는&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;이는&amp;nbsp;&quot;어디서나&amp;nbsp;빌드하고,&amp;nbsp;배포하고,&amp;nbsp;실행할&amp;nbsp;수&amp;nbsp;있는&quot;&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;마치 고래등에 올려진 컨테이너 박스들처럼 프로그램과 실행에 필요한 것들을 컨테이너에 Shipping합니다.&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;Docker는 Linux 커널을 기반으로 하므로 Linux 환경에서 동작하게 됩니다.&lt;/p&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;u&gt;&lt;b&gt;컨테이너에 대해 이해하고 싶다면?&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1685077575918&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;민첩한 클라우드 환경 구성 필수품 &amp;lsquo;컨테이너&amp;rsquo; (1) - 데이터넷&quot; data-og-description=&quot;가상화 기술과도 유사한 개념인 컨테이너는 애플리케이션 실행에 필요한 파일과 라이브러리(lib)를 패키지화한 후, 필요할 때마다 이를 실행시켜 동일한 환경을 이용할 수 있도록 하는 역할을 &quot; data-og-host=&quot;www.datanet.co.kr&quot; data-og-source-url=&quot;http://www.datanet.co.kr/news/articleView.html?idxno=131759&quot; data-og-url=&quot;http://www.datanet.co.kr/news/articleView.html?idxno=131759&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dTCRk3/hySKFilw9u/ovM8L00Xyy5BARkqK0basK/img.jpg?width=600&amp;amp;height=184&amp;amp;face=0_0_600_184,https://scrap.kakaocdn.net/dn/eITq1/hySLwD45iT/NOxDSJ3VNKKxAcAqHEQhK0/img.jpg?width=600&amp;amp;height=292&amp;amp;face=0_0_600_292&quot;&gt;&lt;a href=&quot;http://www.datanet.co.kr/news/articleView.html?idxno=131759&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://www.datanet.co.kr/news/articleView.html?idxno=131759&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dTCRk3/hySKFilw9u/ovM8L00Xyy5BARkqK0basK/img.jpg?width=600&amp;amp;height=184&amp;amp;face=0_0_600_184,https://scrap.kakaocdn.net/dn/eITq1/hySLwD45iT/NOxDSJ3VNKKxAcAqHEQhK0/img.jpg?width=600&amp;amp;height=292&amp;amp;face=0_0_600_292');&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;민첩한 클라우드 환경 구성 필수품 &amp;lsquo;컨테이너&amp;rsquo; (1) - 데이터넷&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;가상화 기술과도 유사한 개념인 컨테이너는 애플리케이션 실행에 필요한 파일과 라이브러리(lib)를 패키지화한 후, 필요할 때마다 이를 실행시켜 동일한 환경을 이용할 수 있도록 하는 역할을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.datanet.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;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Docker와 가상 머신(Virtual Machines)의 차이점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker와&amp;nbsp;전통적인&amp;nbsp;가상&amp;nbsp;머신(VM)의&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1189&quot; data-origin-height=&quot;860&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1IS8m/btshsFZ2iKg/9br9NUdCX46WlrHZ5CZnW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1IS8m/btshsFZ2iKg/9br9NUdCX46WlrHZ5CZnW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1IS8m/btshsFZ2iKg/9br9NUdCX46WlrHZ5CZnW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1IS8m%2FbtshsFZ2iKg%2F9br9NUdCX46WlrHZ5CZnW1%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;434&quot; data-origin-width=&quot;1189&quot; data-origin-height=&quot;860&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;가상&amp;nbsp;머신&lt;/b&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면&amp;nbsp;&lt;b&gt;Docker&lt;/b&gt;는&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;이는&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Docker 아키텍처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker&amp;nbsp;아키텍처는&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;구성되어&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-26 오후 1.53.23.png&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ck8fqn/btshtEzNQrb/m61pSkG5WbLYxm9Zru2Tx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ck8fqn/btshtEzNQrb/m61pSkG5WbLYxm9Zru2Tx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ck8fqn/btshtEzNQrb/m61pSkG5WbLYxm9Zru2Tx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fck8fqn%2FbtshtEzNQrb%2Fm61pSkG5WbLYxm9Zru2Tx0%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;299&quot; data-filename=&quot;스크린샷 2023-05-26 오후 1.53.23.png&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;464&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;Docker vs VMs - 시작시간, 오버헤드 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상머신과&amp;nbsp;Docker&amp;nbsp;컨테이너는&amp;nbsp;&lt;b&gt;시작&amp;nbsp;시간&lt;/b&gt;과&amp;nbsp;&lt;b&gt;오버헤드&lt;/b&gt;&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;&lt;b&gt;먼저, 시작시간의 차이를 살펴보겠습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상&amp;nbsp;머신은&amp;nbsp;운영체제를&amp;nbsp;포함하므로,&amp;nbsp;CPU,&amp;nbsp;메모리,&amp;nbsp;하드&amp;nbsp;디스크&amp;nbsp;등의&amp;nbsp;하드웨어를&amp;nbsp;가상화하고&amp;nbsp;OS를&amp;nbsp;부팅하는&amp;nbsp;데&amp;nbsp;시간이&amp;nbsp;걸립니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면,&amp;nbsp;Docker&amp;nbsp;컨테이너는&amp;nbsp;OS가&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;&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;머신은&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;발생시킵니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&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-origin-width=&quot;1472&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csxcyU/btshxHCL0tz/Ou1V0a3pymuKRKiBvBDSL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csxcyU/btshxHCL0tz/Ou1V0a3pymuKRKiBvBDSL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csxcyU/btshxHCL0tz/Ou1V0a3pymuKRKiBvBDSL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsxcyU%2FbtshxHCL0tz%2FOu1V0a3pymuKRKiBvBDSL1%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;324&quot; data-origin-width=&quot;1472&quot; data-origin-height=&quot;794&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;Docker Hub&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는&amp;nbsp;&lt;b&gt;Docker&amp;nbsp;Hub&lt;/b&gt;라는&amp;nbsp;퍼블릭&amp;nbsp;레지스트리를&amp;nbsp;제공합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 수많은 Docker 이미지를 찾아볼 수 있는 곳으로, 사용자들이 소프트웨어를 컨테이너화해서 공유하는 플랫폼입니다.&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1685077671943&quot; style=&quot;color: #333333; text-align: start;&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;Docker Hub Container Image Library | App Containerization&quot; data-og-description=&quot;Deliver your business through Docker Hub Package and publish apps and plugins as containers in Docker Hub for easy download and deployment by millions of Docker users worldwide.&quot; data-og-host=&quot;hub.docker.com&quot; data-og-source-url=&quot;https://hub.docker.com/&quot; data-og-url=&quot;https://hub.docker.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eAcBK/hySLyIDGP9/33D7D2qLslIBjFstSlCmO1/img.png?width=416&amp;amp;height=250&amp;amp;face=0_0_416_250&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://hub.docker.com/&quot; data-source-url=&quot;https://hub.docker.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eAcBK/hySLyIDGP9/33D7D2qLslIBjFstSlCmO1/img.png?width=416&amp;amp;height=250&amp;amp;face=0_0_416_250');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Docker Hub Container Image Library | App Containerization&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Deliver your business through Docker Hub Package and publish apps and plugins as containers in Docker Hub for easy download and deployment by millions of Docker users worldwide.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;hub.docker.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;h2 data-ke-size=&quot;size26&quot;&gt;컨테이너 사용 명령어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는&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;컨테이너의&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;이러한&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아래에는 많이 사용하는 예시 명령어 8개를 모아두었습니다.&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;u&gt;&lt;b&gt;1. `-d`&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 옵션은 컨테이너를 &quot;분리된&quot; 모드로 실행합니다.&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;u&gt;&lt;b&gt;2. `-p`&lt;/b&gt;&lt;/u&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;포맷은 `-p &amp;lt;host-port&amp;gt;:&amp;lt;container-port&amp;gt;`입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 `-p 8080:80`은 컨테이너의 80번 포트를 호스트의 8080번 포트에 바인딩합니다.&lt;/p&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;u&gt;&lt;b&gt;3. `-v`&lt;/b&gt;&lt;/u&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;예를 들어, `-v /host/dir:/container/dir`은 호스트의 `/host/dir` 디렉토리를 컨테이너의 `/container/dir` 디렉토리에 연결합니다.&lt;/p&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;u&gt;&lt;b&gt;4. `-e`&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 옵션은 컨테이너 내의 환경 변수를 설정합니다. 예를 들어, `-e VAR=VALUE`은 컨테이너 내에서 `VAR`라는 이름의 환경 변수를 `VALUE` 값으로 설정합니다.&lt;/p&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;u&gt;&lt;b&gt;5. `--name`&lt;/b&gt;&lt;/u&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;이 이름은 Docker CLI를 통해 컨테이너를 참조하는데 사용할 수 있습니다.&lt;/p&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;u&gt;&lt;b&gt;6. `--rm`&lt;/b&gt;&lt;/u&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;이는 일회성 작업에서 유용하며 디스크 공간을 절약할 수 있습니다.&lt;/p&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;u&gt;&lt;b&gt;7. `-it`&lt;/b&gt;&lt;/u&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;`-i`와 `-t`를 결합한 옵션으로, `-i`는 STDIN을 연결하고, `-t`는 TTY를 할당합니다.&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;u&gt;&lt;b&gt;8. `--link`&lt;/b&gt;&lt;/u&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;하지만 이 옵션은 이제는 사용되지 않으며, 대신 네트워크를 사용하여 컨테이너를 연결하는 것이 권장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실습1 - Docker 만들어서 띄워보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 AWS에서 Docker를 띄워볼 겁니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 EC2에 접속해봅시다. EC2를 생성하는 방법은 &lt;b&gt;아래에서&lt;/b&gt; 확인해보세요&lt;/p&gt;
&lt;figure id=&quot;og_1685078285799&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;[Learn About AWS] 1회차 실습내용 정리&quot; data-og-description=&quot;AWS Series [Learn About AWS] Series time-map-installer.tistory.com What is EC2? EC2는 무엇일까? EC2 - Elastic Compute Cloud 가상 서버 기능을 제공하는 클라우드 서비스 AWS의 물리적 서버를 가상화 기술을 이용하여 사용&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/182&quot; data-og-url=&quot;https://time-map-installer.tistory.com/182&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/CQ7x6/hySKtoFYvX/V0ivoPyFfwtMbaO7TaeKyk/img.png?width=800&amp;amp;height=478&amp;amp;face=0_0_800_478,https://scrap.kakaocdn.net/dn/cdwMOR/hySLH6GcFn/HFjoXAJJkr3WGGc6n0qAO1/img.png?width=800&amp;amp;height=478&amp;amp;face=0_0_800_478,https://scrap.kakaocdn.net/dn/fjXmq/hySKA9bHpa/co7SRukY8ugYNCkmz0qb5k/img.png?width=1216&amp;amp;height=1242&amp;amp;face=0_0_1216_1242&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/182&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/182&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/CQ7x6/hySKtoFYvX/V0ivoPyFfwtMbaO7TaeKyk/img.png?width=800&amp;amp;height=478&amp;amp;face=0_0_800_478,https://scrap.kakaocdn.net/dn/cdwMOR/hySLH6GcFn/HFjoXAJJkr3WGGc6n0qAO1/img.png?width=800&amp;amp;height=478&amp;amp;face=0_0_800_478,https://scrap.kakaocdn.net/dn/fjXmq/hySKA9bHpa/co7SRukY8ugYNCkmz0qb5k/img.png?width=1216&amp;amp;height=1242&amp;amp;face=0_0_1216_1242');&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;[Learn About AWS] 1회차 실습내용 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;AWS Series [Learn About AWS] Series time-map-installer.tistory.com What is EC2? EC2는 무엇일까? EC2 - Elastic Compute Cloud 가상 서버 기능을 제공하는 클라우드 서비스 AWS의 물리적 서버를 가상화 기술을 이용하여 사용&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;h3 data-ke-size=&quot;size23&quot;&gt;1. Repository 인덱스 갱신하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;먼저, Ubuntu의 패키지 인덱스를 업데이트해야 합니다. 이는 다음 명령을 통해 가능합니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685078852076&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt update&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;새로운 버전의 패키지를 얻거나 패키지 설치를 위해 반드시 수행해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 도커 다운로드를 위한 필요한 패키지 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;도커를 설치하기 전에, 몇 가지 필요한 패키지들이 있습니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685078900778&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt install apt-transport-https ca-certificates curl software-properties-common&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;명령어들은&amp;nbsp;Docker&amp;nbsp;설치에&amp;nbsp;필요한&amp;nbsp;패키지를&amp;nbsp;설치합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;apt-transport-https&lt;/b&gt;는&amp;nbsp;https를&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;b&gt;ca-certificates&lt;/b&gt;는&amp;nbsp;SSL&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;b&gt;curl&lt;/b&gt;은&amp;nbsp;URL에서&amp;nbsp;데이터를&amp;nbsp;전송받는&amp;nbsp;도구이며,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;software-properties-common&lt;/b&gt;은&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Curl 명령어로 Docker의 GPG 키 다운받기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;이제 Docker의 official GPG 키를 추가해야 합니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685078979219&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker의 공식 GPG 키를 다운로드하고 apt 패키지 관리자의 신뢰 키 목록에 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 Docker 패키지가 올바른 출처에서 제공되었는지를 확인하는데 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Docker repository 추가하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;Docker&amp;nbsp;repository를&amp;nbsp;시스템에&amp;nbsp;추가합니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685079017763&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo add-apt-repository &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable&quot;
sudo apt update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 저장소를 시스템의 APT 소스 목록에 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;$(lsb_release -cs)&lt;/b&gt;는 현재 운영체제의 출시 코드명을 출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 함으로써 Docker 저장소를 자동으로 현재 우분투 버전에 맞춰 업데이트합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 후에는 패키지 목록을 다시 업데이트합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Docker 설치하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;이제 Docker를 설치할 준비가 되었습니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685079045731&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt install docker-ce&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;명령어는&amp;nbsp;&lt;b&gt;Docker&amp;nbsp;CE&amp;nbsp;(Community&amp;nbsp;Edition)&lt;/b&gt;을&amp;nbsp;설치합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;좋습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;Docker가&amp;nbsp;정상적으로&amp;nbsp;설치되었는지&amp;nbsp;확인하기&amp;nbsp;위해&amp;nbsp;아래&amp;nbsp;명령을&amp;nbsp;실행합니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685079108772&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo systemctl status docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. Hello World 이미지 받기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;Docker가 정상적으로 설치되었다면, 'hello-world' 이미지를 받아봅시다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685079128889&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker pull hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker pull&lt;/b&gt; 명령어는 Docker Hub에서 'hello-world' 이미지를 다운로드합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;받은&amp;nbsp;이미지&amp;nbsp;목록을&amp;nbsp;확인하기&amp;nbsp;위해&amp;nbsp;아래&amp;nbsp;명령을&amp;nbsp;실행합니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685079153288&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker images&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker&amp;nbsp;images&lt;/b&gt;는&amp;nbsp;현재&amp;nbsp;시스템에&amp;nbsp;있는&amp;nbsp;Docker&amp;nbsp;이미지&amp;nbsp;목록을&amp;nbsp;출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Hello&amp;nbsp;World&amp;nbsp;이미지를&amp;nbsp;실행해봅시다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685079176494&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker run hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker&amp;nbsp;run&lt;/b&gt; 명령어는 'hello-world' 이미지를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;실행한 Docker 컨테이너들의 목록을 확인합니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685079195506&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker ps -a&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker&amp;nbsp;ps&amp;nbsp;-a&lt;/b&gt;는&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;size18&quot;&gt;&lt;u&gt;&lt;b&gt;필요하지 않은 컨테이너를 삭제합니다. 컨테이너ID는 `docker ps -a` 명령어를 통해 확인할 수 있습니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685079208390&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker rm &amp;lt;컨테이너ID&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker&amp;nbsp;rm&lt;/b&gt;&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;size18&quot;&gt;&lt;u&gt;&lt;b&gt;마지막으로, 현재 Docker 이미지 목록을 다시 확인합니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685079227467&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker images&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker&amp;nbsp;images&lt;/b&gt;는 다시 현재 시스템의 Docker 이미지 목록을 출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 실습을 통해 Docker를 설치하고, 이미지를 받아서 실행하고, 컨테이너를 관리하는 방법을 배웠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실습2 - EC2에서 Docker를 사용한 MySQL 설치 &amp;amp; 관리해보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Docker 세팅을 모두 마치고 MySQL을 설치하고 관리해보는 실습을 해보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 도커 버전 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1685080686334&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker -v&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Docker&lt;/b&gt;가 이미 설치되어 있고 동작 중인지 확인하기 위해 사용합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. MySQL 5.6 버전을 다운로드&lt;/h3&gt;
&lt;pre id=&quot;code_1685080712894&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker pull mysql:5.6&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;Docker Hub에서 &lt;b&gt;MySQL 5.6 버전의 이미지&lt;/b&gt;를 다운로드합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Docker 이미지 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1685080788581&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker images&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 시스템에 있는 &lt;b&gt;Docker 이미지 목록&lt;/b&gt;을 출력합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. MySQL Docker 컨테이너 생성 및 실행&lt;/h3&gt;
&lt;pre id=&quot;code_1685080808541&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker run --name mysql-lecture -e MYSQL_ROOT_PASSWORD=&amp;lt;password&amp;gt; -d -p 3306:3306 mysql:5.6&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MySQL&lt;/b&gt;을 실행하는 Docker 컨테이너를 생성하고 백그라운드에서 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 컨테이너의 이름을 &lt;b&gt;&quot;mysql-lecture&quot;&lt;/b&gt;로 설정하고, MySQL의 root 사용자 비밀번호를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;password&amp;gt;(ex. 1234)&lt;/b&gt;로 설정하며, 호스트의 3306 포트를 컨테이너의 3306 포트에 바인딩합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Docker 컨테이너 리스트 출력&lt;/h3&gt;
&lt;pre id=&quot;code_1685080837856&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker ps -a&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 도커 컨테이너의 &lt;b&gt;상태를 확인&lt;/b&gt;합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. MySQL Docker 컨테이너 시작/중지/재시작&lt;/h3&gt;
&lt;pre id=&quot;code_1685080875057&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# MySQL Docker 컨테이너 중지
sudo docker stop mysql-lecture
# MySQL Docker 컨테이너 시작
sudo docker start mysql-lecture
# MySQL Docker 컨테이너 재시작
sudo docker restart mysql-lecture&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너를 시작하거나 중지하거나 재시작합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. MySQL Docker 컨테이너 접속&lt;/h3&gt;
&lt;pre id=&quot;code_1685080909843&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker exec -it mysql-lecture /bin/bash
root@dc557b92f573:/# mysql -u root -p
Enter password:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'mysql-lecture' 컨테이너에 bash 쉘로 접속하고, MySQL에 root 사용자로 로그인합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. DB, User 생성하기, User 권한주기&lt;/h3&gt;
&lt;pre id=&quot;code_1685080965080&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE DATABASE 디비명;
CREATE USER '유저명'@'%' IDENTIFIED BY '비밀번호';
GRANT ALL PRIVILEGES ON 디비명.* TO '유저명'@'%';
flush privileges;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL에서 데이터베이스를 생성하고 사용자를 생성한 후 해당 사용자에게 모든 권한을 부여합니다.&lt;br /&gt;+ 한 줄씩 실행하면서 자신만의 유저명과 비밀번호를 만들어보세요&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;u&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685082085214&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE DATABASE testdb;
CREATE USER 'tuser'@'%' IDENTIFIED BY '1234';
GRANT ALL PRIVILEGES ON testdb.* TO 'tuser'@'%';
flush privileges;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. IP로 바로 컨테이너 MySQL로 접속해보기&lt;/h3&gt;
&lt;pre id=&quot;code_1685081009965&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 컨테이너 ip 확인
sudo docker inspect &amp;lt;컨테이너ID&amp;gt;
# 컨테이너 MYSQL로 바로 접속하기( 바로 mysql 터미널로 접속됨 )
mysql -u root -p -h 172.17.0.2 --port 3306&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;'docker inspect'&lt;/b&gt;로 컨테이너의 IP 주소를 확인하고, MySQL 클라이언트로 해당 IP의 MySQL에 접속합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 접속하기 전에 &lt;b&gt;7번의 첫째 줄&lt;/b&gt;을 참고해서 접속하시면 됩니다.&lt;br /&gt;+ 컨테이너ID는 ubuntu 환경으로 넘어온 후&amp;nbsp;&lt;b&gt;sudo docker ps -a&lt;/b&gt;를 통해 확인해보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10. 외부에서 Mysql 접속해 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 host는 EC2 인스턴스의 &lt;b&gt;퍼블릭 IPv4 DNS&lt;/b&gt;를 사용합니다.&lt;br /&gt;user는 위에서 만들었던 &lt;b&gt;&amp;lt;유저명&amp;gt;(tuser)&lt;/b&gt;을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;password는 위에서 만들었던 &lt;b&gt;&amp;lt;비밀번호&amp;gt;(1234)&lt;/b&gt;를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트는 위에서 열었던 &lt;b&gt;8088&lt;/b&gt;을 사용하되, EC2 인스턴스의 보안 그룹 -&amp;gt; 인바운드 규칙 -&amp;gt; 8088 추가 작성 후 저장한 뒤 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형은 &lt;b&gt;Anywhere-IPv4&lt;/b&gt;를 선택해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-26 오후 4.03.23.png&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tVZBq/btshypvErry/OzrkvwGb7c9mHBbOoHPxk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tVZBq/btshypvErry/OzrkvwGb7c9mHBbOoHPxk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tVZBq/btshypvErry/OzrkvwGb7c9mHBbOoHPxk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtVZBq%2FbtshypvErry%2FOzrkvwGb7c9mHBbOoHPxk1%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;1428&quot; height=&quot;212&quot; data-filename=&quot;스크린샷 2023-05-26 오후 4.03.23.png&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Toad, My SQL Workbench 등을 이용하여 접속해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;11. 컨테이너, 이미지 모두 삭제 후 port 설정 연습하기&lt;/h3&gt;
&lt;pre id=&quot;code_1685082627916&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker stop &amp;lt;컨테이너ID&amp;gt;&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;pre id=&quot;code_1685082609681&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker rm &amp;lt;컨테이너ID&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;그럼 이제 사라진 docker의 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1685083681078&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker image rm &amp;lt;ImageID&amp;gt;&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;+ 혹시모르니 지우는 건 하나씩 하는 걸 추천합니다. 잘못지웠다가 지우면 안될 것 까지 지우면 안되잖아요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1685081148728&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker run --name mysql-lecture -e MYSQL_ROOT_PASSWORD=&amp;lt;password&amp;gt; -d -p 8088:3306 mysql:5.6&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 컨테이너와 이미지를 삭제한 후, 호스트의 8088 포트를 컨테이너의 3306 포트에 바인딩하여 MySQL 컨테이너를 다시 실행합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;12. AWS EC2 보안 그룹에서 8088 port 를 열어줍니다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS EC2 콘솔에서 해당 EC2 인스턴스의 보안 그룹 설정을 수정하여 8088 포트를 열어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;이번에는 figlet, lolcat, cowsay 패키지를 다운받아서 문자열을 입력받아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;무지개색 아스키 아트 동물&lt;/b&gt;을 출력하는 재밌는 실습을 해보도록 할게요!&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.&amp;nbsp;**figlet&amp;nbsp;패키지&amp;nbsp;다운로드**&lt;/h3&gt;
&lt;pre id=&quot;code_1685086354829&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get install figlet&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;figlet&lt;/b&gt;은 텍스트를 아스키 아트로 변환하는데 사용되는 유틸리티입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.&amp;nbsp;**lolcat&amp;nbsp;패키지&amp;nbsp;다운로드**&lt;/h3&gt;
&lt;pre id=&quot;code_1685086365899&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get install lolcat&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;lolcat&lt;/b&gt;은 출력된 텍스트에 무지개색을 입히는 유틸리티입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.&amp;nbsp;**cowsay&amp;nbsp;패키지&amp;nbsp;다운로드**&lt;/h3&gt;
&lt;pre id=&quot;code_1685086411513&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get install cowsay&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;cowsay&lt;/b&gt;는 문자열을 입력받아 그를 말하는 아스키 아트 동물을 출력하는 프로그램입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.&amp;nbsp;**cowsay&amp;nbsp;사용해보기**&lt;/h3&gt;
&lt;pre id=&quot;code_1685086430309&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cowsay Hi!&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;스크린샷 2023-05-26 오후 4.49.33.png&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tIvfp/btshy7Bcw7s/q8GHJkpQRbTw7BaAzcJyL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tIvfp/btshy7Bcw7s/q8GHJkpQRbTw7BaAzcJyL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tIvfp/btshy7Bcw7s/q8GHJkpQRbTw7BaAzcJyL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtIvfp%2Fbtshy7Bcw7s%2Fq8GHJkpQRbTw7BaAzcJyL0%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;382&quot; height=&quot;222&quot; data-filename=&quot;스크린샷 2023-05-26 오후 4.49.33.png&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력한 문자열인 &lt;b&gt;&quot;Hi!&quot;&lt;/b&gt;를 말하는 기본 cow(소) 그림을 출력합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.&amp;nbsp;**cowsay에&amp;nbsp;다른&amp;nbsp;동물&amp;nbsp;그림&amp;nbsp;적용해보기**&lt;/h3&gt;
&lt;pre id=&quot;code_1685086444349&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cowsay -f dragon-and-cow Hi!&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;스크린샷 2023-05-26 오후 4.49.52.png&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRhnyi/btshyLSNgJO/KwNXg3ajlzAox9o3uJcnLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRhnyi/btshyLSNgJO/KwNXg3ajlzAox9o3uJcnLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRhnyi/btshyLSNgJO/KwNXg3ajlzAox9o3uJcnLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRhnyi%2FbtshyLSNgJO%2FKwNXg3ajlzAox9o3uJcnLk%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;928&quot; height=&quot;526&quot; data-filename=&quot;스크린샷 2023-05-26 오후 4.49.52.png&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;526&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;-f&quot;&lt;/b&gt; 옵션을 사용해 &lt;b&gt;&quot;dragon-and-cow&quot;&lt;/b&gt; 그림으로 문자열 &lt;b&gt;&quot;Hi!&quot;&lt;/b&gt;를 말하게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;/usr/share/cowsay/cows&quot;&lt;/b&gt; 디렉토리에는 다양한 동물 그림이 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.&amp;nbsp;**cowsay와&amp;nbsp;lolcat&amp;nbsp;함께&amp;nbsp;사용해보기**&lt;/h3&gt;
&lt;pre id=&quot;code_1685086483513&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cowsay -f dragon-and-cow Hi! | lolcat&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;스크린샷 2023-05-26 오후 4.50.08.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2Ol00/btshyMxnocM/YHzGpJAb8VXsdIqrucwbg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2Ol00/btshyMxnocM/YHzGpJAb8VXsdIqrucwbg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2Ol00/btshyMxnocM/YHzGpJAb8VXsdIqrucwbg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2Ol00%2FbtshyMxnocM%2FYHzGpJAb8VXsdIqrucwbg1%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;916&quot; height=&quot;528&quot; data-filename=&quot;스크린샷 2023-05-26 오후 4.50.08.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;cowsay&lt;/b&gt;의 출력 결과를 &lt;b&gt;lolcat&lt;/b&gt;으로 보내서 무지개색으로 출력합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.&amp;nbsp;**cowsay와&amp;nbsp;lolcat의&amp;nbsp;다양한&amp;nbsp;옵션&amp;nbsp;사용해보기**&lt;/h3&gt;
&lt;pre id=&quot;code_1685086497163&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cowsay -f dragon-and-cow Hi! | lolcat -a -s 500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;-a&quot;&lt;/b&gt; 옵션으로 lolcat의 애니메이션 효과를 활성화하고, &lt;b&gt;&quot;-s&quot;&lt;/b&gt; 옵션으로 애니메이션 속도를 500으로 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 무지개색 애니메이션이 느리게 이동하면서 출력됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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 data-ke-size=&quot;size16&quot;&gt;기지개를 펴니 개운하네요 드디어 끝났습니다. 재밌었어요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Backend</category>
      <category>AWS</category>
      <category>docker</category>
      <category>EC2</category>
      <category>MySQL</category>
      <category>SQL</category>
      <category>보안 그룹</category>
      <category>실습</category>
      <category>아스키 아트</category>
      <category>이미지</category>
      <category>컨테이너</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/231</guid>
      <comments>https://time-map-installer.tistory.com/231#entry231comment</comments>
      <pubDate>Fri, 26 May 2023 15:08:35 +0900</pubDate>
    </item>
    <item>
      <title>[AWS S3] AWS S3 익혀먹기</title>
      <link>https://time-map-installer.tistory.com/230</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dImXwD/btshlYyHM9i/VOTvXHtnAflT3CnlajLJl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dImXwD/btshlYyHM9i/VOTvXHtnAflT3CnlajLJl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dImXwD/btshlYyHM9i/VOTvXHtnAflT3CnlajLJl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdImXwD%2FbtshlYyHM9i%2FVOTvXHtnAflT3CnlajLJl1%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;580&quot; height=&quot;297&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;클라우드 기술의 핵심 중 하나인 AWS S3에 대해 알아보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 서비스는 우리가 필요한 정보를 언제 어디서나 접근할 수 있도록 해주는 매우 중요한 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에서도 AWS S3는 간편하면서도 안전한 데이터 관리를 가능하게 하는 뛰어난 서비스입니다.&lt;br /&gt;&lt;br /&gt;이 글이 도움되었으면 좋겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 글을 시작하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AWS S3?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS S3(Amazon Simple Storage Service)는 Amazon Web Services에서 제공하는 객체 스토리지 서비스입니다.&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;br /&gt;&lt;br /&gt;AWS&amp;nbsp;S3는&amp;nbsp;강한&amp;nbsp;일관성&amp;nbsp;모델을&amp;nbsp;제공합니다.&amp;nbsp;이는&amp;nbsp;AWS&amp;nbsp;S3에&amp;nbsp;데이터를&amp;nbsp;쓴&amp;nbsp;직후에&amp;nbsp;이&amp;nbsp;데이터를&amp;nbsp;읽을&amp;nbsp;수&amp;nbsp;있음을&amp;nbsp;의미합니다.&amp;nbsp;이는&amp;nbsp;S3를&amp;nbsp;사용하는&amp;nbsp;모든&amp;nbsp;애플리케이션에서&amp;nbsp;데이터&amp;nbsp;일관성을&amp;nbsp;유지할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;데이터 일관성이란?&lt;/h2&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;u&gt;&lt;b&gt;여기에는 강한 일관성과 최종 일관성이 있습니다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;강한 일관성&lt;/b&gt;
&lt;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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최종 일관성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최종 일관성은 시스템이 어떤 정해진 시간 경과 후에는 일관된 상태에 도달하게 됨을 보장하는 일관성 모델입니다.&lt;/li&gt;
&lt;li&gt;즉, 데이터 변경이 일어난 직후에는 일관성이 깨질 수 있지만, 시간이 지나면 결국 일관성이 달성됩니다.&lt;/li&gt;
&lt;li&gt;이 모델은 분산 시스템에서 더 효율적으로 구현할 수 있지만, 일시적인 데이터 불일치를 허용한다는 점에서 사용자에게 혼란을 줄 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;S3 데이터 일관성 모델&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-25 오후 4.45.37.png&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkBPb1/btshj3UOJLp/5e7ENQYVSmGkzMJgoCpQXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkBPb1/btshj3UOJLp/5e7ENQYVSmGkzMJgoCpQXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkBPb1/btshj3UOJLp/5e7ENQYVSmGkzMJgoCpQXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkBPb1%2Fbtshj3UOJLp%2F5e7ENQYVSmGkzMJgoCpQXk%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;403&quot; data-filename=&quot;스크린샷 2023-05-25 오후 4.45.37.png&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS S3는 &lt;b&gt;'강한 일관성'&lt;/b&gt; 모델을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 파일이 S3에 작성(업로드)되거나 수정되면, 그 변경사항이 즉시 모든 S3 사용자에게 보여지는 것을 의미합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;S3 버킷 스토리지 유형&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-25 오후 4.47.53.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0b1GR/btshpALdl3C/pZYLP8bCgt5Fs214jR9bD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0b1GR/btshpALdl3C/pZYLP8bCgt5Fs214jR9bD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0b1GR/btshpALdl3C/pZYLP8bCgt5Fs214jR9bD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0b1GR%2FbtshpALdl3C%2FpZYLP8bCgt5Fs214jR9bD1%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;421&quot; data-filename=&quot;스크린샷 2023-05-25 오후 4.47.53.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;954&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 그림은 사용자가 AWS S3 버킷에 파일을 업로드하거나 다운로드하고, 이들 파일이 여러 S3 스토리지 클래스 중 어디에 저장되는지를 보여줍니다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;AWS&amp;nbsp;S3는&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이들 클래스 중 일부는 다음과 같습니다&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;S3 Standard&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 데이터 유형에 대한 범용 스토리지.&lt;/li&gt;
&lt;li&gt;빠른 액세스, 고가용성, 고내구성을 제공.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;S3 Intelligent-Tiering&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;액세스 패턴에 따라 자동으로 데이터를 이동시키는 스토리지.&lt;/li&gt;
&lt;li&gt;자주 액세스하는 데이터는 S3 Standard에, 그렇지 않은 데이터는 S3 Standard-IA에 저장.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;S3 Standard-IA&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자주 액세스하지 않는 데이터에 대한 비용 효율적인 스토리지.&lt;/li&gt;
&lt;li&gt;약간의 검색 지연이 있을 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;S3 One Zone-IA&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 가용성 영역에서만 데이터를 저장.&lt;/li&gt;
&lt;li&gt;더 낮은 비용을 위해 일부 내구성을 희생.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;S3 Glacier &amp;amp; S3 Glacier Deep Archive&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;장기 아카이브 데이터에 대한 비용 효율적인 스토리지.&lt;/li&gt;
&lt;li&gt;데이터 검색에 상당한 시간이 소요될 수 있으며, 추가 비용이 발생할 수 있음.&amp;nbsp;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/AWS</category>
      <category>AWS</category>
      <category>S3</category>
      <category>데이터 일관성</category>
      <category>버킷</category>
      <category>스토리지</category>
      <category>일관성</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/230</guid>
      <comments>https://time-map-installer.tistory.com/230#entry230comment</comments>
      <pubDate>Thu, 25 May 2023 17:05:40 +0900</pubDate>
    </item>
    <item>
      <title>[Computer Science] 네트워크 기초 익혀먹기</title>
      <link>https://time-map-installer.tistory.com/229</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LSkRN/btshp21xVKL/A3XyEPNO89edjEEfKII78K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LSkRN/btshp21xVKL/A3XyEPNO89edjEEfKII78K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LSkRN/btshp21xVKL/A3XyEPNO89edjEEfKII78K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLSkRN%2Fbtshp21xVKL%2FA3XyEPNO89edjEEfKII78K%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;580&quot; height=&quot;297&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 사이언스, 우리가 흔히 CS라고 부르는 이 분야는 모든 관련분야의 시작이라고 불릴 정도로 매우 근본적이고도 매우 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 경우에는 이 CS 지식이 앞으로 개발자로 살아가는데 많은 도움을 줄 것이라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 지금부터 네트워크 기초를 익혀먹어보도록 할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;무엇을 목표하나요?&lt;/b&gt;&lt;/h4&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;리눅스의 시스템 설정 명령어를 익혀봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시스템 구분&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;1. 노드 (Node)&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;인터넷에&amp;nbsp;연결된&amp;nbsp;시스템의&amp;nbsp;가장&amp;nbsp;일반적인&amp;nbsp;용어입니다.&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드는&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;이&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;예를&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;2. 호스트 (Host)&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;컴퓨팅&amp;nbsp;기능이&amp;nbsp;있는&amp;nbsp;시스템으로,&amp;nbsp;노드의&amp;nbsp;한&amp;nbsp;종류입니다.&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트는&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;시스템은&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;3. 클라이언트 (Client)&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;서비스를&amp;nbsp;요청하는&amp;nbsp;시스템으로,&amp;nbsp;노드의&amp;nbsp;다른&amp;nbsp;종류입니다.&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트는&amp;nbsp;서비스를&amp;nbsp;요청하는&amp;nbsp;시스템을&amp;nbsp;의미합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를&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;클라이언트는&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;4. 서버(Server)&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;서비스를 제공하는 시스템으로, 노드의 다른 종류입니다.&lt;/h4&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;서버는 클라이언트로부터의 요청을 처리하고 응답을 보내는 역할을 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;네트워크 주소&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;1. IP 주소&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IP 주소&lt;/b&gt;는 &lt;b&gt;TCP/IP 프로토콜&lt;/b&gt;에서 사용되며, 네트워크에서 컴퓨터와 전산장비를 구별하기 위한 &lt;b&gt;주소 체계&lt;/b&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IPv4&lt;/b&gt;&amp;nbsp;프로토콜에서는&amp;nbsp;&lt;b&gt;32비트&amp;nbsp;크기의&lt;/b&gt;&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;b&gt;232개의&amp;nbsp;주소&lt;/b&gt;를&amp;nbsp;나타낼&amp;nbsp;수&amp;nbsp;있으며,&amp;nbsp;총&amp;nbsp;4,294,967,296개의&amp;nbsp;주소가&amp;nbsp;가능합니다.&lt;br /&gt;&lt;b&gt;IPv6&lt;/b&gt;에서는 IP 주소를 &lt;b&gt;비트 주소 체계로 확장&lt;/b&gt;하여 더 많은 주소를 표현할 수 있게 되었습니다.&lt;br /&gt;&lt;br /&gt;IP&amp;nbsp;주소는&amp;nbsp;&lt;b&gt;4개의&amp;nbsp;십진수&lt;/b&gt;로&amp;nbsp;표현되며,&amp;nbsp;각&amp;nbsp;십진수는&amp;nbsp;&lt;b&gt;8비트로&amp;nbsp;구성&lt;/b&gt;되어&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를&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;예를&amp;nbsp;들어,&amp;nbsp;&lt;b&gt;&quot;192.168.0.1&quot;&lt;/b&gt;과 같은 형식으로 표현됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnUzbc/btshjwijLVa/1zaRKeLy4mT3DRSi1wOLh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnUzbc/btshjwijLVa/1zaRKeLy4mT3DRSi1wOLh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnUzbc/btshjwijLVa/1zaRKeLy4mT3DRSi1wOLh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnUzbc%2FbtshjwijLVa%2F1zaRKeLy4mT3DRSi1wOLh0%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;600&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP&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;기기는&amp;nbsp;고유한&amp;nbsp;IP&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;2. IP 주소 체계&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷&amp;nbsp;프로토콜&amp;nbsp;주소(IP&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;IP 주소는 두 부분으로 구성되어 있습니다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;네트워크 ID &lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 부분은 특정 네트워크를 구분하고 식별하는 데 사용됩니다.&lt;/li&gt;
&lt;li&gt;이는 인터넷에서 그룹을 식별하기 위한 주소 영역입니다.&lt;br /&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;호스트 ID &lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 부분은 해당 네트워크 내의 특정 컴퓨터 또는 장치를 식별합니다.&lt;/li&gt;
&lt;li&gt;이는 해당 네트워크에서 각각의 호스트를 식별하기 위한 주소 영역입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클래스&amp;nbsp;C&lt;/b&gt;의&amp;nbsp;IP&amp;nbsp;주소는&amp;nbsp;&lt;b&gt;8비트&lt;/b&gt;의&amp;nbsp;호스트&amp;nbsp;ID를&amp;nbsp;가지며,&amp;nbsp;이를&amp;nbsp;통해&amp;nbsp;하나의&amp;nbsp;네트워크&amp;nbsp;내에서&amp;nbsp;최대&amp;nbsp;256대의&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;size18&quot;&gt;&lt;u&gt;&lt;b&gt;잠시만요, 클래스 C라 하면 클래스 A와 B도 있겠네요?&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네,&amp;nbsp;그렇습니다.&amp;nbsp;인터넷&amp;nbsp;프로토콜&amp;nbsp;(IP)&amp;nbsp;주소는&amp;nbsp;클래스&amp;nbsp;A,&amp;nbsp;B,&amp;nbsp;C,&amp;nbsp;D,&amp;nbsp;E로&amp;nbsp;나누어집니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각&amp;nbsp;클래스는&amp;nbsp;서로&amp;nbsp;다른&amp;nbsp;비트&amp;nbsp;길이의&amp;nbsp;네트워크&amp;nbsp;ID와&amp;nbsp;호스트&amp;nbsp;ID를&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;간단하게 각 클래스를 설명하면 다음과 같습니다&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;클래스 A&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 클래스의 IP 주소는 0.0.0.0에서 127.255.255.255까지 범위를 가집니다.&lt;/li&gt;
&lt;li&gt;네트워크 ID는 처음 8비트에 위치하며, 나머지 24비트는 호스트 ID입니다.&lt;/li&gt;
&lt;li&gt;따라서 클래스 A는 큰 네트워크에 적합하며, 각 네트워크에는 수백만 개의 호스트가 있을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클래스 B&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 클래스의 IP 주소는 128.0.0.0에서 191.255.255.255까지 범위를 가집니다.&lt;/li&gt;
&lt;li&gt;네트워크 ID는 처음 16비트에 위치하며, 나머지 16비트는 호스트 ID입니다.&lt;/li&gt;
&lt;li&gt;클래스 B는 중간 크기의 네트워크에 적합하며, 각 네트워크에는 수만 개의 호스트가 있을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클래스 C&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 클래스의 IP 주소는 192.0.0.0에서 223.255.255.255까지 범위를 가집니다.&lt;/li&gt;
&lt;li&gt;네트워크 ID는 처음 24비트에 위치하며, 나머지 8비트는 호스트 ID입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클래스 C&lt;/b&gt;는 작은 네트워크에 적합하며, 각 네트워크에는 최대 256개의 호스트가 있을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클래스&amp;nbsp;D와&amp;nbsp;E는&amp;nbsp;일반적인&amp;nbsp;네트워킹에서는&amp;nbsp;사용되지&amp;nbsp;않습니다.&amp;nbsp;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;클래스&amp;nbsp;D&lt;/b&gt;는&amp;nbsp;멀티캐스트를&amp;nbsp;위해&amp;nbsp;예약되어&amp;nbsp;있으며,&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클래스&amp;nbsp;E&lt;/b&gt;는&amp;nbsp;실험&amp;nbsp;및&amp;nbsp;연구용으로&amp;nbsp;예약되어&amp;nbsp;있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;간단히&amp;nbsp;말하면,&amp;nbsp;IP&amp;nbsp;주소는&amp;nbsp;&lt;b&gt;&quot;도로명&quot;&lt;/b&gt;과&amp;nbsp;&lt;b&gt;&quot;집&amp;nbsp;번호&quot;&lt;/b&gt;처럼&amp;nbsp;작동합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;도로명&quot;&lt;/b&gt;은&amp;nbsp;네트워크&amp;nbsp;ID에&amp;nbsp;해당하며,&amp;nbsp;&lt;b&gt;&quot;집&amp;nbsp;번호&quot;&lt;/b&gt;는&amp;nbsp;호스트&amp;nbsp;ID에&amp;nbsp;해당합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서&amp;nbsp;각각의&amp;nbsp;장치는&amp;nbsp;고유한&amp;nbsp;&lt;b&gt;&quot;집&amp;nbsp;번호&quot;&lt;/b&gt;를&amp;nbsp;가지지만,&amp;nbsp;같은&amp;nbsp;&lt;b&gt;&quot;도로&quot;&amp;nbsp;&lt;/b&gt;(즉,&amp;nbsp;네트워크)에&amp;nbsp;있는&amp;nbsp;다른&amp;nbsp;장치와는&amp;nbsp;같은&amp;nbsp;&lt;b&gt;&quot;도로명&quot;&lt;/b&gt;을&amp;nbsp;공유합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;3. 호스트 이름 시스템&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS&amp;nbsp;(Domain&amp;nbsp;Name&amp;nbsp;System)는&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;사람들은&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;IP&amp;nbsp;주소를&amp;nbsp;사용하여&amp;nbsp;서로를&amp;nbsp;인식하고&amp;nbsp;통신합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS의&amp;nbsp;주요&amp;nbsp;역할은&amp;nbsp;사람이&amp;nbsp;읽을&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;도메인&amp;nbsp;이름을&amp;nbsp;해당하는&amp;nbsp;IP&amp;nbsp;주소로&amp;nbsp;변환하는&amp;nbsp;것입니다.&lt;br /&gt;&lt;br /&gt;다음과 같은 과정을 생각해볼 수 있습니다&lt;/p&gt;
&lt;pre id=&quot;code_1684998365347&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[사용자] -- www.example.com 요청 --&amp;gt; [DNS 서버]
[DNS 서버] -- IP 주소 반환 --&amp;gt; [사용자]
[사용자] -- IP 주소 사용하여 접속 --&amp;gt; [www.example.com 서버]&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가&amp;nbsp;웹&amp;nbsp;브라우저에&amp;nbsp;&quot;www.example.com&quot;을&amp;nbsp;입력합니다.&lt;/li&gt;
&lt;li&gt;이 요청이 DNS 서버로 전송됩니다.&lt;/li&gt;
&lt;li&gt;DNS 서버는 &quot;www.example.com&quot;에&amp;nbsp;해당하는&amp;nbsp;IP&amp;nbsp;주소를&amp;nbsp;찾습니다&amp;nbsp;(예:&amp;nbsp;192.0.2.44).&lt;/li&gt;
&lt;li&gt;DNS 서버는 IP 주소를 웹 브라우저에 반환합니다.&lt;/li&gt;
&lt;li&gt;웹 브라우저는 반환된 IP 주소를 사용하여 &quot;www.example.com&quot;&amp;nbsp;서버에&amp;nbsp;연결하고&amp;nbsp;사용자에게&amp;nbsp;웹&amp;nbsp;페이지를&amp;nbsp;표시합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;4. 서브넷 마스크(Subnet Mask)&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브넷&amp;nbsp;마스크는&amp;nbsp;IP&amp;nbsp;주소와&amp;nbsp;같은&amp;nbsp;32비트&amp;nbsp;숫자이며,&amp;nbsp;IP&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;이&amp;nbsp;마스크는&amp;nbsp;'1'&amp;nbsp;비트들이&amp;nbsp;연속적으로&amp;nbsp;나열된&amp;nbsp;후에&amp;nbsp;'0'&amp;nbsp;비트들이&amp;nbsp;오는&amp;nbsp;형태로&amp;nbsp;이루어져&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'1'들은&amp;nbsp;네트워크&amp;nbsp;부분을,&amp;nbsp;'0'들은&amp;nbsp;호스트&amp;nbsp;부분을&amp;nbsp;나타냅니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;비트&amp;nbsp;AND&amp;nbsp;연산&lt;/b&gt;은&amp;nbsp;IP&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;이를&amp;nbsp;&lt;b&gt;마스킹(Masking)&lt;/b&gt;이라고&amp;nbsp;합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;이를 시각적으로 표현하면?&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1684998615902&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IP 주소:     192.168.1.1     = 11000000.10101000.00000001.00000001
서브넷 마스크: 255.255.255.0 = 11111111.11111111.11111111.00000000

# 이 둘을 AND 연산하면

네트워크 주소:               = 11000000.10101000.00000001.00000000 = 192.168.1.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서,&amp;nbsp;이&amp;nbsp;IP&amp;nbsp;주소의&amp;nbsp;네트워크&amp;nbsp;주소는&amp;nbsp;192.168.1.0입니다.&lt;br /&gt;&lt;br /&gt;이렇게&amp;nbsp;서브넷&amp;nbsp;마스크는&amp;nbsp;IP&amp;nbsp;주소에서&amp;nbsp;네트워크&amp;nbsp;ID와&amp;nbsp;호스트&amp;nbsp;ID를&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;서브넷&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;&lt;b&gt;5. 서브넷팅&lt;/b&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브넷팅은&amp;nbsp;IP&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;서브넷팅의&amp;nbsp;주요&amp;nbsp;목적은&amp;nbsp;IP&amp;nbsp;주소&amp;nbsp;공간을&amp;nbsp;효율적으로&amp;nbsp;사용하고&amp;nbsp;네트워크&amp;nbsp;관리를&amp;nbsp;향상시키는&amp;nbsp;것입니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;간단한 예시&lt;/b&gt;&lt;br /&gt;우리가 C 클래스 IP 주소인 &lt;b&gt;192.168.1.0/24&lt;/b&gt;를 가지고 있다고 가정해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;'/24'&lt;/b&gt;는 서브넷 마스크가 &lt;b&gt;255.255.255.0&lt;/b&gt;임을 나타내는 &lt;b&gt;CIDR 표기법&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 네트워크는 &lt;b&gt;최대 254개의 호스트&lt;/b&gt; (2^(32-24) - 2 = 254)를 지원할 수 있습니다.&lt;br /&gt;&lt;br /&gt;만약 우리가 이 네트워크를 두 개의 동등한 작은 네트워크로 분할하고자 한다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브넷 마스크에 1비트를 추가하여 &lt;b&gt;'/25'&lt;/b&gt; 또는 &lt;b&gt;255.255.255.128&lt;/b&gt;을 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면, 두 개의 서브넷이 생성되며 각각은 최대 126개의 호스트를 지원할 수 있습니다 (2^(32-25) - 2 = 126).&lt;br /&gt;&lt;br /&gt;&lt;b&gt; 이것도 시각적으로 표현해본다면?&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1684999088690&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;192.168.1.0/24 (255.255.255.0)
|
|--- 서브넷팅 ---
|
|--- 192.168.1.0/25 (255.255.255.128) - 호스트 범위: 192.168.1.1 - 192.168.1.126
|
|--- 192.168.1.128/25 (255.255.255.128) - 호스트 범위: 192.168.1.129 - 192.168.1.254&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;서브넷팅은 네트워크를 더 작은 부분으로 분할하여 IP 주소 공간을 효율적으로 사용하고, 네트워크 관리를 향상시키는데 도움이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 큰 네트워크를 여러 개의 작은 네트워크로 분할하여 각 네트워크의 트래픽을 격리시키고, 각각의 네트워크를 개별적으로 관리할 수 있게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 이 IP 주소의 네트워크 주소는 192.168.1.0입니다.&lt;br /&gt;&lt;br /&gt;이렇게&amp;nbsp;서브넷&amp;nbsp;마스크는&amp;nbsp;IP&amp;nbsp;주소에서&amp;nbsp;네트워크&amp;nbsp;ID와&amp;nbsp;호스트&amp;nbsp;ID를&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;서브넷&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;네트워크 관련 설정과 명령어&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;1. TCP/IP &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷에서 데이터를 전송하는 데 사용되는 프로토콜 세트입니다.&lt;br /&gt;&lt;u&gt;&lt;b&gt;&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;2. 호스트 이름과 도메인 이름 &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트&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;이름은&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;&lt;u&gt;&lt;b&gt;3. IP 주소 &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터의&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;&lt;u&gt;&lt;b&gt;4. 네트워크 주소&lt;/b&gt;&lt;/u&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;IP 주소의 네트워크 부분이 이에 해당합니다.&lt;/p&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;u&gt;&lt;b&gt;5. 브로드캐스트 주소 &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크의&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;6. 게이트웨이 &lt;/b&gt;&lt;/u&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;이 장치는 서로 다른 네트워크 간에 트래픽을 전달하는 역할을 합니다.&lt;/p&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;u&gt;&lt;b&gt;7. 넷마스크와 클래스 &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넷마스크는&amp;nbsp;IP&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;IP&amp;nbsp;클래스는&amp;nbsp;IP&amp;nbsp;주소&amp;nbsp;할당&amp;nbsp;방식을&amp;nbsp;나타내며,&amp;nbsp;현재는&amp;nbsp;클래스&amp;nbsp;없는&amp;nbsp;주소&amp;nbsp;지정&amp;nbsp;방식(CIDR)이&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;&lt;u&gt;&lt;b&gt;8. DNS 서버 주소 &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인&amp;nbsp;이름을&amp;nbsp;IP&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;&lt;u&gt;&lt;b&gt;9. 리눅스에서의 네트워크 장치 이름 &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서&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;&lt;u&gt;&lt;b&gt;10. nm-connection-deitor &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서&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;&lt;u&gt;&lt;b&gt;11. systemctl start/stop/restart/status networking&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;12. ifconfig 장치명 &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'ifconfig'은&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;하지만&amp;nbsp;최신의&amp;nbsp;리눅스&amp;nbsp;배포판에서는&amp;nbsp;'ip'&amp;nbsp;명령어가&amp;nbsp;'ifconfig'을&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;&lt;u&gt;&lt;b&gt;13. nslookup &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'nslookup'은&amp;nbsp;DNS&amp;nbsp;쿼리를&amp;nbsp;수행하여&amp;nbsp;도메인&amp;nbsp;이름을&amp;nbsp;IP&amp;nbsp;주소로&amp;nbsp;변환하거나&amp;nbsp;IP&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;&lt;u&gt;&lt;b&gt;14. ping IP 주소 또는 URL&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP&amp;nbsp;주소&amp;nbsp;또는&amp;nbsp;URL에&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;&lt;u&gt;&lt;b&gt;15. 네트워크 기본 정보가 설정된 파일 &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'/etc/resolv.conf'는&amp;nbsp;시스템이&amp;nbsp;DNS&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;DNS&amp;nbsp;서버의&amp;nbsp;IP&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;&lt;u&gt;&lt;b&gt;16. /etc/hosts &lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터의&amp;nbsp;IP&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/CS(Computer Science)</category>
      <category>Computer science</category>
      <category>CS</category>
      <category>IP</category>
      <category>ip주소</category>
      <category>Network</category>
      <category>네트워크</category>
      <category>서브마스킹</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/229</guid>
      <comments>https://time-map-installer.tistory.com/229#entry229comment</comments>
      <pubDate>Thu, 25 May 2023 16:14:04 +0900</pubDate>
    </item>
    <item>
      <title>[React] useMemo 쓰지 마세요?</title>
      <link>https://time-map-installer.tistory.com/228</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5yvt5/btshlXyv4vP/vbYPsLZzFHX8XPkc9qAva1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5yvt5/btshlXyv4vP/vbYPsLZzFHX8XPkc9qAva1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5yvt5/btshlXyv4vP/vbYPsLZzFHX8XPkc9qAva1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5yvt5%2FbtshlXyv4vP%2FvbYPsLZzFHX8XPkc9qAva1%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;282&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 useEffect 대응방안에서 useMemo를 사용해서 개선하는 방향과 관련된 글을 쓰다가 생각난 건데,&lt;br /&gt;어떤 글에서 useMemo를 쓰지 말라는 주제로 글이 있던 것이 생각났습니다.&lt;br /&gt;&lt;br /&gt;그래서 이참에 이것도 알아보기로 하였죠. 왜 useMemo를 쓰면 안 되는 건지,&lt;br /&gt;그전에&amp;nbsp;useMemo는&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;&lt;b&gt;이전 글 보고오기&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1685003302300&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;[React] 왜 React에서는 useEffect의 사용을 추천하지 않을까? - Side Effect 관리&quot; data-og-description=&quot;React는 페이스북에서 개발한 강력한 프론트엔드 라이브러리 입니다. 상당히 많은 부분에서 useEffect 훅을 이용하여 개발을 진행하는데, React에서는 useEffect를 지양하고 있다고 합니다. 이번 글에서&quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/227&quot; data-og-url=&quot;https://time-map-installer.tistory.com/227&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxCHlz/hySKvlwRXw/LkwLPN6WX6gkukL13uniCK/img.png?width=800&amp;amp;height=563&amp;amp;face=0_0_800_563,https://scrap.kakaocdn.net/dn/lfJ0l/hySKFVYJLN/98MD8XQKDxPuCob6mJqyW0/img.png?width=1494&amp;amp;height=1052&amp;amp;face=0_0_1494_1052&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/227&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/227&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxCHlz/hySKvlwRXw/LkwLPN6WX6gkukL13uniCK/img.png?width=800&amp;amp;height=563&amp;amp;face=0_0_800_563,https://scrap.kakaocdn.net/dn/lfJ0l/hySKFVYJLN/98MD8XQKDxPuCob6mJqyW0/img.png?width=1494&amp;amp;height=1052&amp;amp;face=0_0_1494_1052');&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;[React] 왜 React에서는 useEffect의 사용을 추천하지 않을까? - Side Effect 관리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;React는 페이스북에서 개발한 강력한 프론트엔드 라이브러리 입니다. 상당히 많은 부분에서 useEffect 훅을 이용하여 개발을 진행하는데, React에서는 useEffect를 지양하고 있다고 합니다. 이번 글에서&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;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;h2 data-ke-size=&quot;size26&quot;&gt;1. useMemo란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useMemo&lt;/b&gt;는 React의 내장 &lt;b&gt;Hook&lt;/b&gt; 중 하나로, 함수를 인자로 받아 그 함수의 메모이제이션된 반환 값을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 &lt;b&gt;Hook&lt;/b&gt;은 주로 &quot;비용이 많이 드는 연산&quot;을 수행할 때 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useMemo&lt;/b&gt;를 사용하여 계산 결과를 캐시하면, 해당 값이 변경되지 않는 한 컴포넌트가 다시 렌더링 될 때마다 값을 다시 계산하는 대신 이전에 계산된 값을 재사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메모이제이션?&lt;/h4&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;일반적으로 함수나 계산 결과를 메모이제이션하여 이후에 동일한 입력에 대해 다시 계산할 필요 없이 저장된 결과를 재사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서의 useMemo 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;&lt;b&gt;알고리즘 공부할 때 봤던 것 같기도 하고..?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞습니다. 메모이제이션은 &lt;b&gt;동적 계획법(Dynamic Programming)&lt;/b&gt;에서 흔히 사용되는 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 계획법은 문제를 작은 부분 문제(sub-problems)로 분할하고, 각 부분 문제의 결과를 저장하여 중복 계산을 피하는 최적화 기법입니다.&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;React의 &lt;b&gt;useMemo&lt;/b&gt; Hook은 이와 유사한 개념을 사용하여 중복 계산을 피하고 성능을 최적화하는 데 활용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;비용이 많이 드는 연산?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한&amp;nbsp;작업은&amp;nbsp;&lt;b&gt;useMemo&lt;/b&gt;나&amp;nbsp;&lt;b&gt;useRef&lt;/b&gt;를&amp;nbsp;사용하여&amp;nbsp;결과를&amp;nbsp;캐시함으로써&amp;nbsp;성능을&amp;nbsp;향상할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;다양한 예시 중 일부를 살펴보겠습니다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;1. 데이터 계산&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대량의 데이터를 가공하거나 필터링하는 작업은 비용이 많이 드는 연산에 해당할 수 있습니다. 예를 들어, 수천 개의 항목을 정렬하거나 복잡한 계산을 수행하는 경우 &lt;b&gt;useMemo&lt;/b&gt;를 사용하여 계산 결과를 캐시할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684982621661&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const sortedData = useMemo(() =&amp;gt; {
  // 복잡한 정렬 알고리즘 또는 계산 로직
  return computeExpensiveSort(data);
}, [data]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;2. 외부 리소스 접근&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 API 호출 또는 데이터베이스 쿼리와 같은 비동기 작업은 비용이 많이 드는 연산에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 작업의 결과를 &lt;b&gt;useMemo&lt;/b&gt;를 사용하여 캐시하면, 동일한 요청이 여러 번 호출되는 것을 방지할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684982650183&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const data = useMemo(() =&amp;gt; {
  const fetchData = async () =&amp;gt; {
    // 외부 API 호출 또는 데이터베이스 쿼리
    const response = await fetchSomeData();
    return response.data;
  };

  return fetchData();
}, []);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;3. 복잡한 계산&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수학적인 계산이나 알고리즘 로직을 수행하는 경우에도 &lt;b&gt;useMemo&lt;/b&gt;를 활용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 계산 결과를 캐시 하여 중복 계산을 피할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684982765965&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const result = useMemo(() =&amp;gt; {
  // 복잡한 계산 로직
  return performComplexCalculation(a, b);
}, [a, b]);&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;&lt;b&gt;useMemo&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;2. useMemo를 사용하는 상황은 언제일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useMemo&lt;/b&gt;의&amp;nbsp;주요&amp;nbsp;목적은&amp;nbsp;성능&amp;nbsp;최적화입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를&amp;nbsp;들어,&amp;nbsp;특정&amp;nbsp;&lt;b&gt;prop&lt;/b&gt;이&amp;nbsp;변경되었을&amp;nbsp;때만&amp;nbsp;연산을&amp;nbsp;수행해야&amp;nbsp;하는&amp;nbsp;상황에서&amp;nbsp;&lt;b&gt;useMemo&lt;/b&gt;를&amp;nbsp;사용하면,&amp;nbsp;해당&amp;nbsp;&lt;b&gt;prop&lt;/b&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;/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;3. useMemo를 사용하면 안되는 이유는 무엇일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 모든 상황에서 &lt;b&gt;useMemo&lt;/b&gt;를 사용하는 것은 바람직하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모이제이션된 값이 비용이 적게 드는 계산이거나 컴포넌트가 언마운트되지 않는 한 계속 유지되어야 하는 값인 경우에는 &lt;b&gt;useMemo&lt;/b&gt;를 사용하는 것이 적절하지 않을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 원시값인 boolean이나 문자열과 같은 값에 대해서는 &lt;b&gt;useMemo&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;4. 그렇다면 어떤 방법을 사용해야 할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useMemo&lt;/b&gt;의 대안으로 &lt;b&gt;useRef&lt;/b&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useRef&lt;/b&gt;는 값의 변경을 유지하기 위해 사용되는 &lt;b&gt;Hook&lt;/b&gt;으로, &lt;b&gt;useMemo&lt;/b&gt;와 유사한 역할을 수행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useRef&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;useMemo&lt;/b&gt;와 같이 &lt;b&gt;useRef&lt;/b&gt;를 사용하여 &quot;비용이 많이 드는 연산&quot;의 결과를 유지하고 싶은 경우에는 다음과 같이 사용할 수 있습니다&lt;/p&gt;
&lt;pre id=&quot;code_1684982471070&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useRef } from 'react';

function ExampleComponent() {
  const expensiveValueRef = useRef(computeExpensiveValue());

  // ... 컴포넌트 내에서 expensiveValueRef를 사용하는 코드 ...

  return &amp;lt;div&amp;gt;...&amp;lt;/div&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의&amp;nbsp;예시에서&amp;nbsp;&lt;b&gt;computeExpensiveValue()&lt;/b&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;&lt;b&gt;useRef&lt;/b&gt;를&amp;nbsp;사용하여&amp;nbsp;이&amp;nbsp;값을&amp;nbsp;유지하면서&amp;nbsp;불필요한&amp;nbsp;재계산을&amp;nbsp;피할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;useRef&lt;/b&gt;는&amp;nbsp;주로&amp;nbsp;&lt;b&gt;DOM&lt;/b&gt;&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;활용하여&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;b&gt;useRef&lt;/b&gt;는&amp;nbsp;&lt;b&gt;useMemo&lt;/b&gt;와&amp;nbsp;달리&amp;nbsp;값을&amp;nbsp;캐시하는&amp;nbsp;용도뿐만&amp;nbsp;아니라&amp;nbsp;&lt;b&gt;DOM&lt;/b&gt;&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;&lt;b&gt;useMemo&lt;/b&gt;의&amp;nbsp;대안으로서&amp;nbsp;&lt;b&gt;useRef&lt;/b&gt;를&amp;nbsp;사용하여&amp;nbsp;값을&amp;nbsp;유지하는&amp;nbsp;방법을&amp;nbsp;고려할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMemo는 React에서 성능을 향상하기 위해 사용되는 Hook입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비용이 많이 드는 연산의 결과를 캐시하여 중복 계산을 피하고 성능을 최적화하는 데 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 모든 상황에서 useMemo를 사용할 필요는 없으며, 작업이 간단하거나 비용이 적은 경우에는 메모리 사용과 성능 저하를 초래할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMemo를 사용할 때는 신중하게 판단해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, useRef를 활용하여 값의 참조를 유지하거나 다른 성능 최적화 기법도 고려해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최적화 기법은 애플리케이션의 특성과 요구사항에 따라 다르므로 프로파일링과 성능 테스트를 통해 적절한 기법을 선택해야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&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;References&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.logrocket.com/when-not-to-use-usememo-react-hook/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.logrocket.com/when-not-to-use-usememo-react-hook/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1684981445911&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;When not to use the useMemo React Hook - LogRocket Blog&quot; data-og-description=&quot;Learn when not to use the useMemo React Hook, such as when a memoized value is not used frequently or a computation is not expensive.&quot; data-og-host=&quot;blog.logrocket.com&quot; data-og-source-url=&quot;https://blog.logrocket.com/when-not-to-use-usememo-react-hook/&quot; data-og-url=&quot;http://blog.logrocket.com/when-not-to-use-usememo-react-hook/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbwkoR/hySKEJw9Fh/ibqDBXfZuM8vNLhk8NiTVK/img.png?width=730&amp;amp;height=487&amp;amp;face=226_206_451_374&quot;&gt;&lt;a href=&quot;https://blog.logrocket.com/when-not-to-use-usememo-react-hook/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.logrocket.com/when-not-to-use-usememo-react-hook/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbwkoR/hySKEJw9Fh/ibqDBXfZuM8vNLhk8NiTVK/img.png?width=730&amp;amp;height=487&amp;amp;face=226_206_451_374');&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;When not to use the useMemo React Hook - LogRocket Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn when not to use the useMemo React Hook, such as when a memoized value is not used frequently or a computation is not expensive.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.logrocket.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://blog.bitsrc.io/when-using-usememo-is-a-really-bad-idea-a2bdeb909812&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.bitsrc.io/when-using-usememo-is-a-really-bad-idea-a2bdeb909812&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1684981918278&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;When Using useMemo is A Really Bad Idea&quot; data-og-description=&quot;Situations in which this otherwise powerful React Hook is completely useless&quot; data-og-host=&quot;blog.bitsrc.io&quot; data-og-source-url=&quot;https://blog.bitsrc.io/when-using-usememo-is-a-really-bad-idea-a2bdeb909812&quot; data-og-url=&quot;https://blog.bitsrc.io/when-using-usememo-is-a-really-bad-idea-a2bdeb909812&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bYM7WA/hySKAUGRFQ/iKsEYfCkb8jZxwGLVOyEyK/img.jpg?width=1200&amp;amp;height=658&amp;amp;face=0_0_1200_658&quot;&gt;&lt;a href=&quot;https://blog.bitsrc.io/when-using-usememo-is-a-really-bad-idea-a2bdeb909812&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.bitsrc.io/when-using-usememo-is-a-really-bad-idea-a2bdeb909812&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bYM7WA/hySKAUGRFQ/iKsEYfCkb8jZxwGLVOyEyK/img.jpg?width=1200&amp;amp;height=658&amp;amp;face=0_0_1200_658');&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;When Using useMemo is A Really Bad Idea&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Situations in which this otherwise powerful React Hook is completely useless&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.bitsrc.io&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://www.developerway.com/posts/how-to-use-memo-use-callback&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.developerway.com/posts/how-to-use-memo-use-callback&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1684982042866&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;How to useMemo and useCallback: you can remove most of them&quot; data-og-description=&quot;What is the purpose of useMemo and useCallback hooks, mistakes and best practices in using them, and why removing most of them might be a good idea.&quot; data-og-host=&quot;www.developerway.com&quot; data-og-source-url=&quot;https://www.developerway.com/posts/how-to-use-memo-use-callback&quot; data-og-url=&quot;https://www.developerway.com/posts/how-to-use-memo-use-callback&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bI5Tu3/hySKz2yD5b/w1IFELvW0CgBvnWsEFa0r1/img.png?width=1000&amp;amp;height=640&amp;amp;face=0_0_1000_640&quot;&gt;&lt;a href=&quot;https://www.developerway.com/posts/how-to-use-memo-use-callback&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.developerway.com/posts/how-to-use-memo-use-callback&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bI5Tu3/hySKz2yD5b/w1IFELvW0CgBvnWsEFa0r1/img.png?width=1000&amp;amp;height=640&amp;amp;face=0_0_1000_640');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;How to useMemo and useCallback: you can remove most of them&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What is the purpose of useMemo and useCallback hooks, mistakes and best practices in using them, and why removing most of them might be a good idea.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.developerway.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;/div&gt;
&lt;/div&gt;</description>
      <category>Development Study/Frontend</category>
      <category>Algorithm</category>
      <category>DP</category>
      <category>dynamic programming</category>
      <category>Hook</category>
      <category>JavaScript</category>
      <category>react</category>
      <category>useMemo</category>
      <category>useRef</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/228</guid>
      <comments>https://time-map-installer.tistory.com/228#entry228comment</comments>
      <pubDate>Thu, 25 May 2023 11:54:43 +0900</pubDate>
    </item>
    <item>
      <title>[React] useEffect 쓰지 마세요?</title>
      <link>https://time-map-installer.tistory.com/227</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ycxkk/btshh7QsEvd/qeTFsMU0PNanqUYYIqxx90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ycxkk/btshh7QsEvd/qeTFsMU0PNanqUYYIqxx90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ycxkk/btshh7QsEvd/qeTFsMU0PNanqUYYIqxx90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYcxkk%2Fbtshh7QsEvd%2FqeTFsMU0PNanqUYYIqxx90%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;282&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React는 페이스북에서 개발한 강력한 프론트엔드 라이브러리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상당히 많은 부분에서 useEffect 훅을 이용하여 개발을 진행하는데, React에서는 useEffect를 지양하고 있다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 useEffect 훅을 지양하는 이유가 무엇인지, 그리고 연관되어 있는 개념인 Side Effect 관리에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Side Effect란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Side Effect는 컴포넌트 외부에 영향을 미치는 작업을 의미합니다.&lt;/b&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;React 컴포넌트는 &lt;b&gt;Side Effect&lt;/b&gt; 관리를 위해서 &lt;b&gt;useEffect&lt;/b&gt; 훅을 제공합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 불필요한 Side Effect로 인한 성능 저하&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useEffect&lt;/b&gt; 훅을 사용할 때에는 &lt;b&gt;Side Effect&lt;/b&gt;가 정말로 필요한 경우에만 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불필요한 &lt;b&gt;Side Effect&lt;/b&gt;는 컴포넌트의 성능을 저하시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 컴포넌트가 화면에 표시될 때마다 데이터를 가져오는 &lt;b&gt;Side Effect&lt;/b&gt;가 존재한다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면이 렌더링 될 때마다 매번 데이터를 가져와야 하므로 불필요한 네트워크 요청이 발생할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684979421206&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Counter() {
  const [count, setCount] = useState(0);
  const [doubleCount, setDoubleCount] = useState(0);
  
  useEffect(() =&amp;gt; {
    console.log('Calculating doubleCount...');
    setDoubleCount(count * 2);
  }, [count]);
  
  function increment() {
    setCount(count + 1);
  }
  
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;Count: {count}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Double Count: {doubleCount}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;Increment&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의&amp;nbsp;코드에서는&amp;nbsp;&lt;b&gt;count&lt;/b&gt;가&amp;nbsp;변경될&amp;nbsp;때마다&amp;nbsp;&lt;b&gt;useEffect&lt;/b&gt;가&amp;nbsp;실행되어&amp;nbsp;&lt;b&gt;doubleCount&lt;/b&gt;를&amp;nbsp;업데이트합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나&amp;nbsp;&lt;b&gt;doubleCount&lt;/b&gt;는&amp;nbsp;&lt;b&gt;count&lt;/b&gt;에 의존하고 있기 때문에 이 작업은 &lt;b&gt;Side Effect&lt;/b&gt;입니다.&lt;br /&gt;&lt;br /&gt;문제는&amp;nbsp;&lt;b&gt;doubleCount&lt;/b&gt;를&amp;nbsp;업데이트하는&amp;nbsp;&lt;b&gt;useEffect&lt;/b&gt;가&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;이는&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;예를&amp;nbsp;들어,&amp;nbsp;&lt;b&gt;increment&lt;/b&gt;&amp;nbsp;버튼을&amp;nbsp;클릭하여&amp;nbsp;&lt;b&gt;count&lt;/b&gt;를&amp;nbsp;증가시킬&amp;nbsp;때마다&amp;nbsp;&lt;b&gt;doubleCount&lt;/b&gt;를&amp;nbsp;다시&amp;nbsp;계산하고&amp;nbsp;화면을&amp;nbsp;업데이트하는&amp;nbsp;것은&amp;nbsp;비효율적입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Side Effect 관리를 위한 최적의 방법 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Side Effect&lt;/b&gt;를 관리하기 위해 &lt;b&gt;useEffect&lt;/b&gt; 훅을 사용하는 것은 일반적으로 옳은 접근 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 &lt;b&gt;useEffect&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;Side Effect를 최적으로 관리하기 위해서는 다음과 같은 방법들을 고려할 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;1. 불필요한 Side Effect를 제거하고, 컴포넌트 렌더링 시에 계산을 수행할 수 있는 useMemo 훅을 활용하는 방법&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1684979146986&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Counter() {
  const [count, setCount] = useState(0);
  
  const doubleCount = useMemo(() =&amp;gt; {
    console.log('Calculating doubleCount...');
    return count * 2;
  }, [count]);
  
  function increment() {
    setCount(count + 1);
  }
  
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;Count: {count}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Double Count: {doubleCount}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;Increment&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의&amp;nbsp;코드에서는&amp;nbsp;&lt;b&gt;doubleCount&lt;/b&gt;를&amp;nbsp;&lt;b&gt;useMemo&lt;/b&gt;를&amp;nbsp;사용하여&amp;nbsp;계산하고&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;count&lt;/b&gt;가&amp;nbsp;변경될&amp;nbsp;때만&amp;nbsp;&lt;b&gt;doubleCount&lt;/b&gt;를&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;이렇게&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 하지만, &lt;b&gt;useMemo&lt;/b&gt;를 사용하는 것도 문제가 있다고 합니다. -&lt;/p&gt;
&lt;figure id=&quot;og_1684983329442&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;[React] useMemo 쓰지 마세요?&quot; data-og-description=&quot;최근 useEffect 대응방안에서 useMemo를 사용해서 개선하는 방향과 관련된 글을 쓰다가 생각난 건데, 어떤 글에서 useMemo를 쓰지 말라는 주제로 글이 있던 것이 생각났습니다. 그래서 이참에 이것도 &quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/228&quot; data-og-url=&quot;https://time-map-installer.tistory.com/228&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/VaH9a/hySKHM4hii/72AApbkGhuisKdXujkjI10/img.png?width=800&amp;amp;height=563&amp;amp;face=0_0_800_563,https://scrap.kakaocdn.net/dn/rwZxu/hySKxjnxYC/4hmkVA3vku6pcAtugXpwrk/img.png?width=800&amp;amp;height=563&amp;amp;face=0_0_800_563,https://scrap.kakaocdn.net/dn/ciqNja/hySKHTPA3M/OnD56ZZwYiedWeiuEWMYbk/img.png?width=1494&amp;amp;height=1052&amp;amp;face=0_0_1494_1052&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/228&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/228&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/VaH9a/hySKHM4hii/72AApbkGhuisKdXujkjI10/img.png?width=800&amp;amp;height=563&amp;amp;face=0_0_800_563,https://scrap.kakaocdn.net/dn/rwZxu/hySKxjnxYC/4hmkVA3vku6pcAtugXpwrk/img.png?width=800&amp;amp;height=563&amp;amp;face=0_0_800_563,https://scrap.kakaocdn.net/dn/ciqNja/hySKHTPA3M/OnD56ZZwYiedWeiuEWMYbk/img.png?width=1494&amp;amp;height=1052&amp;amp;face=0_0_1494_1052');&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;[React] useMemo 쓰지 마세요?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;최근 useEffect 대응방안에서 useMemo를 사용해서 개선하는 방향과 관련된 글을 쓰다가 생각난 건데, 어떤 글에서 useMemo를 쓰지 말라는 주제로 글이 있던 것이 생각났습니다. 그래서 이참에 이것도&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;u&gt;&lt;b&gt;2. 프롭 변경 시에 상태를 명시적으로 재설정하는 key 속성 활용하는 방법&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1684979179208&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function TodoList({ todos }) {
  const [filteredTodos, setFilteredTodos] = useState([]);
  
  useEffect(() =&amp;gt; {
    // todos를 필터링하여 filteredTodos를 설정하는 로직
    setFilteredTodos(todos.filter(todo =&amp;gt; !todo.completed));
  }, [todos]);
  
  return (
    &amp;lt;ul&amp;gt;
      {filteredTodos.map(todo =&amp;gt; (
        &amp;lt;li key={todo.id}&amp;gt;{todo.text}&amp;lt;/li&amp;gt;
      ))}
    &amp;lt;/ul&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의&amp;nbsp;코드에서&amp;nbsp;todos가&amp;nbsp;변경될&amp;nbsp;때마다&amp;nbsp;useEffect가&amp;nbsp;실행되어&amp;nbsp;filteredTodos를&amp;nbsp;업데이트합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;todos&amp;nbsp;프롭이&amp;nbsp;변경되었을&amp;nbsp;때&amp;nbsp;컴포넌트가&amp;nbsp;다시&amp;nbsp;렌더링 되지만,&amp;nbsp;이전&amp;nbsp;filteredTodos&amp;nbsp;상태를&amp;nbsp;재사용하고&amp;nbsp;싶은&amp;nbsp;경우&amp;nbsp;key&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;3. 렌더링 중에 상태를 직접 업데이트하여 상태를 조절하는 방법&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1684979202236&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Counter() {
  const [count, setCount] = useState(0);
  
  function increment() {
    setCount(prevCount =&amp;gt; prevCount + 1);
  }
  
  function decrement() {
    setCount(prevCount =&amp;gt; prevCount - 1);
  }
  
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;Count: {count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;Increment&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={decrement}&amp;gt;Decrement&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의&amp;nbsp;코드에서&amp;nbsp;increment와&amp;nbsp;decrement&amp;nbsp;함수에서&amp;nbsp;setCount를&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;이&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Side Effect를 최소화하는 최적의 패턴 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Side Effect를 최소화하고 코드를 더 간결하게 유지하기 위해서는 다음과 같은 패턴을 고려해 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 이벤트 핸들러 내에서 필요한 로직을 직접 처리하여 불필요한 Side Effect를 피하는 패턴&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 자식 컴포넌트의 상태 변경 알림을 부모 컴포넌트로부터 받아와서 상태를 업데이트하는 패턴&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 데이터 플로우를 부모에서 자식으로 유지하여 코드의 가독성과 예측 가능성을 향상하는 패턴&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Side Effect 관리와 데이터 가져오기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 가져올 때 &lt;b&gt;useEffect&lt;/b&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;예를 들어보자면, 데이터를 가져오기 위해 &lt;b&gt;useEffect&lt;/b&gt;를 사용하여 네트워크 요청을 수행할 경우 &lt;b&gt;Race Condition&lt;/b&gt;과 같은 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Race Condition이란?&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경쟁&amp;nbsp;상태(Race&amp;nbsp;Condition)는&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;프로그래밍에서는&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;예시를&amp;nbsp;통해&amp;nbsp;설명해 보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가정해 보세요,&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;그러나&amp;nbsp;출금&amp;nbsp;액수가&amp;nbsp;계좌&amp;nbsp;잔액보다&amp;nbsp;크면&amp;nbsp;출금이&amp;nbsp;불가능합니다.&lt;br /&gt;&lt;br /&gt;친구&amp;nbsp;A:&amp;nbsp;계좌&amp;nbsp;잔액&amp;nbsp;확인&lt;br /&gt;친구&amp;nbsp;B:&amp;nbsp;계좌&amp;nbsp;잔액&amp;nbsp;확인&lt;br /&gt;친구&amp;nbsp;A:&amp;nbsp;출금&amp;nbsp;시도&amp;nbsp;(잔액이&amp;nbsp;충분하므로&amp;nbsp;출금&amp;nbsp;성공)&lt;br /&gt;친구&amp;nbsp;B:&amp;nbsp;출금&amp;nbsp;시도&amp;nbsp;(잔액이&amp;nbsp;충분해&amp;nbsp;보였지만,&amp;nbsp;이미&amp;nbsp;친구&amp;nbsp;A가&amp;nbsp;출금하여&amp;nbsp;잔액이&amp;nbsp;부족함)&lt;br /&gt;위의&amp;nbsp;예시에서&amp;nbsp;친구&amp;nbsp;B는&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;이렇게&amp;nbsp;서로&amp;nbsp;동시에&amp;nbsp;경쟁하여&amp;nbsp;발생하는&amp;nbsp;상황을&amp;nbsp;경쟁&amp;nbsp;상태(Race&amp;nbsp;Condition)라고&amp;nbsp;합니다.&lt;br /&gt;&lt;br /&gt;프로그래밍에서도&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;예를&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&lt;br /&gt;따라서 경쟁 상태를 피하기 위해서는 동기화 메커니즘이나 mutual exclusion와 같은 방법을 사용하여 여러 스레드나 프로세스가 동시에 공유 자원에 접근하는 것을 제어해야 합니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+ 경쟁 조건을 해결하기 위해 이전에 요청한 데이터를 무시하는 &lt;b&gt;cleanup 함수&lt;/b&gt;를 추가해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;React에서 useEffect를 사용할 때에는 Side Effect 관리에 주의해야 합니다. Side Effect를 최소화하고 코드를 더 효율적으로 유지하기 위해 다른 패턴과 최적의 방법을 고려해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 가져오는 작업에서는 다른 조건들을 고려하여 클린업 함수를 추가하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 고려 사항을 통해 React 애플리케이션을 더욱 효율적으로 개발하고 유지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&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;References&lt;span style=&quot;background-color: #fafafa; caret-color: auto; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1684980478863&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;Don't use useEffect&quot; data-og-description=&quot;In this post We'll prove that in 90% of our components we don't need to write a useEffect ... ...&quot; data-og-host=&quot;dev.to&quot; data-og-source-url=&quot;https://dev.to/rem0nfawzi/dont-use-useeffect-3ca8&quot; data-og-url=&quot;https://dev.to/rem0nfawzi/dont-use-useeffect-3ca8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bP2F2D/hySKwY1CHt/JTgZpzPPs5Gb8hE14qEYfK/img.png?width=1128&amp;amp;height=600&amp;amp;face=0_0_1128_600&quot;&gt;&lt;a href=&quot;https://dev.to/rem0nfawzi/dont-use-useeffect-3ca8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dev.to/rem0nfawzi/dont-use-useeffect-3ca8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bP2F2D/hySKwY1CHt/JTgZpzPPs5Gb8hE14qEYfK/img.png?width=1128&amp;amp;height=600&amp;amp;face=0_0_1128_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Don't use useEffect&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In this post We'll prove that in 90% of our components we don't need to write a useEffect ... ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dev.to&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1684980476198&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;React Without useEffect&quot; data-og-description=&quot;useEffect is the hardest part of React - and the most error-prone. What if we could build entire React apps without this hook? It's possible - but you need a framework.&quot; data-og-host=&quot;marmelab.com&quot; data-og-source-url=&quot;https://marmelab.com/blog/2023/02/01/react-without-useEffect.html&quot; data-og-url=&quot;https://marmelab.com//blog/2023/02/01/react-without-useEffect.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jsw90/hySKBlHRFG/xE2KGm7rC4vm4Kmt8VKe50/img.jpg?width=320&amp;amp;height=240&amp;amp;face=0_0_320_240,https://scrap.kakaocdn.net/dn/cqUKgr/hySKFVXwGz/woW0tMtNmkk1HFfWQrTSY1/img.jpg?width=320&amp;amp;height=240&amp;amp;face=0_0_320_240,https://scrap.kakaocdn.net/dn/KazCG/hySKBst3LL/9vryYxm8Ck4ShKswkIcMp0/img.png?width=720&amp;amp;height=397&amp;amp;face=0_0_720_397&quot;&gt;&lt;a href=&quot;https://marmelab.com/blog/2023/02/01/react-without-useEffect.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://marmelab.com/blog/2023/02/01/react-without-useEffect.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jsw90/hySKBlHRFG/xE2KGm7rC4vm4Kmt8VKe50/img.jpg?width=320&amp;amp;height=240&amp;amp;face=0_0_320_240,https://scrap.kakaocdn.net/dn/cqUKgr/hySKFVXwGz/woW0tMtNmkk1HFfWQrTSY1/img.jpg?width=320&amp;amp;height=240&amp;amp;face=0_0_320_240,https://scrap.kakaocdn.net/dn/KazCG/hySKBst3LL/9vryYxm8Ck4ShKswkIcMp0/img.png?width=720&amp;amp;height=397&amp;amp;face=0_0_720_397');&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;React Without useEffect&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;useEffect is the hardest part of React - and the most error-prone. What if we could build entire React apps without this hook? It's possible - but you need a framework.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;marmelab.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1684980472824&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;You Might Not Need an Effect &amp;ndash; React&quot; data-og-description=&quot;The library for web and native user interfaces&quot; data-og-host=&quot;react.dev&quot; data-og-source-url=&quot;https://react.dev/learn/you-might-not-need-an-effect&quot; data-og-url=&quot;https://react.dev/learn/you-might-not-need-an-effect&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bEs5bb/hySKB0jzjv/eqJShVed8mjF693lKqTc7K/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/cH7M0S/hySKCZeCQa/TMlwF887qje9ta6K4XLkBK/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567&quot;&gt;&lt;a href=&quot;https://react.dev/learn/you-might-not-need-an-effect&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react.dev/learn/you-might-not-need-an-effect&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bEs5bb/hySKB0jzjv/eqJShVed8mjF693lKqTc7K/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567,https://scrap.kakaocdn.net/dn/cH7M0S/hySKCZeCQa/TMlwF887qje9ta6K4XLkBK/img.png?width=1080&amp;amp;height=567&amp;amp;face=0_0_1080_567');&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;You Might Not Need an Effect &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The library for web and native user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react.dev&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;/div&gt;
&lt;/div&gt;</description>
      <category>Development Study/Frontend</category>
      <category>cleanup</category>
      <category>react</category>
      <category>sideeffect</category>
      <category>useEffect</category>
      <category>useMemo</category>
      <category>패턴</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/227</guid>
      <comments>https://time-map-installer.tistory.com/227#entry227comment</comments>
      <pubDate>Thu, 25 May 2023 11:05:05 +0900</pubDate>
    </item>
    <item>
      <title>[Template] SEO 향상을 위한 Header 템플릿</title>
      <link>https://time-map-installer.tistory.com/226</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JC9J4/btsg4ikoZyi/skbKUQVbi02rpjvGwkZ6V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JC9J4/btsg4ikoZyi/skbKUQVbi02rpjvGwkZ6V1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JC9J4/btsg4ikoZyi/skbKUQVbi02rpjvGwkZ6V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJC9J4%2Fbtsg4ikoZyi%2FskbKUQVbi02rpjvGwkZ6V1%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;284&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SEO가 무엇인지 궁금하다면?&lt;/h2&gt;
&lt;figure id=&quot;og_1684855149012&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;[SEO] 서비스의 검색 엔진 순위를 높여보자, 헤더에 SEO 적용하기&quot; data-og-description=&quot;SEO(검색 엔진 최적화)는 웹사이트의 검색 엔진 순위를 높이기 위한 중요한 요소 입니다. 웹사이트를 아무리 잘 만들어 두어도 검색이 되지 않는다면 사용자는 쉽게 들어오지 않을 것이고, 결국 &quot; data-og-host=&quot;time-map-installer.tistory.com&quot; data-og-source-url=&quot;https://time-map-installer.tistory.com/225&quot; data-og-url=&quot;https://time-map-installer.tistory.com/225&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wGjev/hySJdZ5f1j/M8pf19QpxqmQZ9GckjAoEk/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/iqOAB/hySJm3N7Qz/3F1Ss3kbSt6xPOo2ldgj4k/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/L7XgX/hySKzNZ2W7/wbvlkWeACKuR2wDcz29Ko1/img.png?width=1350&amp;amp;height=832&amp;amp;face=0_0_1350_832&quot;&gt;&lt;a href=&quot;https://time-map-installer.tistory.com/225&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://time-map-installer.tistory.com/225&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wGjev/hySJdZ5f1j/M8pf19QpxqmQZ9GckjAoEk/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/iqOAB/hySJm3N7Qz/3F1Ss3kbSt6xPOo2ldgj4k/img.png?width=800&amp;amp;height=493&amp;amp;face=0_0_800_493,https://scrap.kakaocdn.net/dn/L7XgX/hySKzNZ2W7/wbvlkWeACKuR2wDcz29Ko1/img.png?width=1350&amp;amp;height=832&amp;amp;face=0_0_1350_832');&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;[SEO] 서비스의 검색 엔진 순위를 높여보자, 헤더에 SEO 적용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SEO(검색 엔진 최적화)는 웹사이트의 검색 엔진 순위를 높이기 위한 중요한 요소 입니다. 웹사이트를 아무리 잘 만들어 두어도 검색이 되지 않는다면 사용자는 쉽게 들어오지 않을 것이고, 결국&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;time-map-installer.tistory.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;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;h2 data-ke-size=&quot;size26&quot;&gt;Head 태그 안에 이런 식으로 만들어 보는건 어때요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;head 태그 안에 이러한 정보들을 넣으면 SEO를 향상시키는데 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇 가지 추가 예시를 넣어두었고, 이를 참고하여 작성하면 되겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684854430635&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    &amp;lt;!-- 웹 페이지의 타이틀을 정의. 이는 검색 엔진 결과 페이지와 브라우저 탭에서 표시됨 --&amp;gt;
    &amp;lt;title&amp;gt;당신의 웹사이트 | 웹 개발에 대한 모든 것&amp;lt;/title&amp;gt;

    &amp;lt;!-- 웹 페이지의 문자 인코딩을 정의 --&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;!-- 웹 페이지의 설명을 제공. 이는 검색 엔진에서 페이지의 간략한 요약으로 사용됨 --&amp;gt;
    &amp;lt;meta name=&quot;description&quot; content=&quot;프론트엔드 개발에 대해 배우세요. HTML, CSS, JavaScript 등의 기본 개념부터 최신 트렌드에 이르기까지, 모든 것을 한 곳에서 배울 수 있습니다.&quot;&amp;gt;
    &amp;lt;!-- 웹 페이지의 주요 키워드를 정의 --&amp;gt;
    &amp;lt;meta name=&quot;keywords&quot; content=&quot;프론트엔드 개발, 웹 개발, HTML, CSS, JavaScript&quot;&amp;gt;
    &amp;lt;!-- 웹 페이지가 모바일 기기에서 올바르게 보이도록 설정 --&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&amp;gt;
    &amp;lt;!-- 검색 엔진에 페이지를 어떻게 색인화하고 추적할지 지시 --&amp;gt;
    &amp;lt;meta name=&quot;robots&quot; content=&quot;index, follow&quot;&amp;gt;

    &amp;lt;!-- Open Graph (Facebook) 메타데이터를 정의. 이는 페이지가 소셜 미디어 플랫폼에서 어떻게 보일지 결정 --&amp;gt;
    &amp;lt;meta property=&quot;og:title&quot; content=&quot;당신의 웹사이트 | 웹 개발에 대한 모든 것&quot;&amp;gt;
    &amp;lt;meta property=&quot;og:description&quot; content=&quot;프론트엔드 개발에 대해 배우세요. HTML, CSS, JavaScript 등의 기본 개념부터 최신 트렌드에 이르기까지, 모든 것을 한 곳에서 배울 수 있습니다.&quot;&amp;gt;
    &amp;lt;meta property=&quot;og:image&quot; content=&quot;URL_TO_YOUR_IMAGE&quot;&amp;gt;
    &amp;lt;meta property=&quot;og:url&quot; content=&quot;YOUR_WEBSITE_URL&quot;&amp;gt;
    &amp;lt;meta property=&quot;og:type&quot; content=&quot;website&quot;&amp;gt;

    &amp;lt;!-- Twitter 메타데이터를 정의. 이는 트윗에 페이지가 포함될 때 어떻게 보일지 결정 --&amp;gt;
    &amp;lt;meta name=&quot;twitter:card&quot; content=&quot;summary_large_image&quot;&amp;gt;
    &amp;lt;meta name=&quot;twitter:title&quot; content=&quot;당신의 웹사이트 | 웹 개발에 대한 모든 것&quot;&amp;gt;
    &amp;lt;meta name=&quot;twitter:description&quot; content=&quot;프론트엔드 개발에 대해 배우세요. HTML, CSS, JavaScript 등의 기본 개념부터 최신 트렌드에 이르기까지, 모든 것을 한 곳에서 배울 수 있습니다.&quot;&amp;gt;
    &amp;lt;meta name=&quot;twitter:image&quot; content=&quot;URL_TO_YOUR_IMAGE&quot;&amp;gt;

    &amp;lt;!-- 웹사이트의 아이콘을 정의 --&amp;gt;
    &amp;lt;link rel=&quot;icon&quot; type=&quot;image/png&quot; sizes=&quot;32x32&quot; href=&quot;/favicon-32x32.png&quot;&amp;gt;
    &amp;lt;link rel=&quot;icon&quot; type=&quot;image/png&quot; sizes=&quot;16x16&quot; href=&quot;/favicon-16x16.png&quot;&amp;gt;
&amp;lt;/head&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;어? meta 안에 name과 property가 있네요? 어떤 차이가 있는건가요?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;name&amp;nbsp;속성은&amp;nbsp;일반적으로&amp;nbsp;HTML&amp;nbsp;문서의&amp;nbsp;메타데이터를&amp;nbsp;정의하는&amp;nbsp;데&amp;nbsp;사용됩니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;반면에&amp;nbsp;property&amp;nbsp;속성은&amp;nbsp;Open&amp;nbsp;Graph&amp;nbsp;프로토콜에서&amp;nbsp;사용됩니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로토콜은 주로 소셜 미디어 플랫폼 (예: Facebook)에서 웹 페이지의 정보를 정확하게 추출하고 표시하는 데 사용됩다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;property 속성은 og:title, og:description, og:image 등과 같은 Open Graph 프로퍼티를 정의하는 데 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 property와 name 속성 모두 메타데이터를 정의하는 데 사용되지만, 그들이 사용되는 컨텍스트와 목적이 다르다는 것을 기억해야 합니다.&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;Next.js에서 meta tag를 활용하고 싶다면?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법은 간단합니다. &amp;lt;head&amp;gt; 태그 대신 &amp;lt;Head&amp;gt; 태그를 이용하면 된다고 합니다.&lt;/p&gt;
&lt;figure id=&quot;og_1684855439355&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;Learn | Next.js&quot; data-og-description=&quot;Production grade React applications that scale. The world&amp;rsquo;s leading companies use Next.js by Vercel to build pre-rendered applications, static websites, and more.&quot; data-og-host=&quot;nextjs.org&quot; data-og-source-url=&quot;https://nextjs.org/learn/seo/rendering-and-ranking/metadata&quot; data-og-url=&quot;https://nextjs.org/learn&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1nblo/hySJh2uZFq/MbAvpC244aIkLjxwlTk5S0/img.png?width=1686&amp;amp;height=882&amp;amp;face=0_0_1686_882&quot;&gt;&lt;a href=&quot;https://nextjs.org/learn/seo/rendering-and-ranking/metadata&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nextjs.org/learn/seo/rendering-and-ranking/metadata&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1nblo/hySJh2uZFq/MbAvpC244aIkLjxwlTk5S0/img.png?width=1686&amp;amp;height=882&amp;amp;face=0_0_1686_882');&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;Learn | Next.js&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Production grade React applications that scale. The world&amp;rsquo;s leading companies use Next.js by Vercel to build pre-rendered applications, static websites, and more.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nextjs.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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>(3D)Dev Deep Dive/Templates &amp;amp; Guides</category>
      <category>Frontend</category>
      <category>head</category>
      <category>header</category>
      <category>SEO</category>
      <category>tag</category>
      <category>template</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/226</guid>
      <comments>https://time-map-installer.tistory.com/226#entry226comment</comments>
      <pubDate>Wed, 24 May 2023 00:18:22 +0900</pubDate>
    </item>
    <item>
      <title>[SEO] 서비스의 검색 엔진 순위를 높여보자, 헤더에 SEO 적용하기</title>
      <link>https://time-map-installer.tistory.com/225</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;832&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9nwai/btshb54G2j6/WCXK5TF4UZpiHW4vR6OPC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9nwai/btshb54G2j6/WCXK5TF4UZpiHW4vR6OPC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9nwai/btshb54G2j6/WCXK5TF4UZpiHW4vR6OPC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9nwai%2Fbtshb54G2j6%2FWCXK5TF4UZpiHW4vR6OPC0%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;370&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;832&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEO(검색 엔진 최적화)는 웹사이트의 검색 엔진 순위를 높이기 위한 중요한 요소 입니다.&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;오늘 알아볼 것은 프론트엔드의 헤더에 SEO를 어떻게 적용하면 좋을 지에 대한 내용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SEO(Search Engine Optimization)란 무엇일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEO는 &quot;검색 엔진 최적화&quot;를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주위에서 많이 사용하는 Google, Bing, Naver 등의 검색 엔진에서 웹사이트가 더 잘 보이도록 향상시키는 과정을 말합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;어떻게 SEO를 적용해야하죠?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 방법이 있지만, 많이 사용하는 오늘 소개할 방법은 바로 Header에 데이터를 추가하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래에는 헤더에 특정 태그들을 사용하여 SEO를 향상시키는 몇 가지 방법을 제시해 두었습니다.&lt;/p&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;u&gt;&lt;b&gt;1. Title 태그&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Title 태그는 웹 페이지의 제목을 정의하며, 사용자와 검색 엔진 모두에게 페이지의 내용에 대한 간략한 개요를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEO에 중요한 역할을 하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684851491716&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;프론트엔드 개발에 대한 모든 것 - 나의 웹사이트&amp;lt;/title&amp;gt;
&amp;lt;/head&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;&lt;u&gt;&lt;b&gt;2. Description 메타 태그&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Description 메타 태그는 웹 페이지의 내용에 대한 간단한 요약을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 검색 결과에 표시되며, 사용자가 웹페이지를 클릭하는 데 도움이 될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684851570663&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    &amp;lt;meta 
    	name=&quot;description&quot; 
        content=&quot;프론트엔드 개발에 대해 배우세요. HTML, CSS, JavaScript 등의 기본 개념부터 
        		최신 트렌드에 이르기까지, 모든 것을 한 곳에서 배울 수 있습니다.&quot;
    &amp;gt;
&amp;lt;/head&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;&lt;u&gt;&lt;b&gt;3. Keywords 메타 태그&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Keywords 메타 태그는 웹페이지의 주요 키워드를 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 태그의 중요성은 과거에 비해 감소된 상태이지만 일부 검색 엔진에서는 여전히 이를 참조할 수 있다고 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684851953821&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    &amp;lt;meta name=&quot;keywords&quot; content=&quot;프론트엔드 개발, 웹 개발, HTML, CSS, JavaScript&quot;&amp;gt;
&amp;lt;/head&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;&lt;u&gt;&lt;b&gt;4.  Viewport 메타 태그&lt;/b&gt;&lt;/u&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;모바일 친화성을 고려하는 것 또한 검색 엔진의 랭킹 요소 중 하나입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684853194930&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&amp;gt;
&amp;lt;/head&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;&lt;u&gt;&lt;b&gt;5. Robots 메타 태그&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 태그는 검색엔진로봇(크롤러)에게 페이지를 어떻게 색인화할지 지시합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684853232898&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    &amp;lt;meta name=&quot;robots&quot; content=&quot;index, follow&quot;&amp;gt;
&amp;lt;/head&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;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>Frontend</category>
      <category>header</category>
      <category>meta</category>
      <category>metadata</category>
      <category>SEO</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/225</guid>
      <comments>https://time-map-installer.tistory.com/225#entry225comment</comments>
      <pubDate>Tue, 23 May 2023 23:49:16 +0900</pubDate>
    </item>
    <item>
      <title>[Recoil vs Redux] Frontend Engineer를 위한 React 상태관리 도구 알아보기</title>
      <link>https://time-map-installer.tistory.com/224</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IoeW3/btsg4hZnBpj/XZ9bOnAaZMvPJdiHsLOI40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IoeW3/btsg4hZnBpj/XZ9bOnAaZMvPJdiHsLOI40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IoeW3/btsg4hZnBpj/XZ9bOnAaZMvPJdiHsLOI40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIoeW3%2Fbtsg4hZnBpj%2FXZ9bOnAaZMvPJdiHsLOI40%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;362&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;652&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;리액트를 사용하고 있는 저는 리액트에서 쓸 수 있는 상태관리 툴들에 대해 알아보아야겠다 싶었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 작성 시점 기준의 정보를 가지고 상태관리 툴에 대한 리서치를 시작하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상태관리(State Management)란 무엇일까?&lt;/h2&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;&quot;시간이 지남에 따라 변경되는 모든 데이터에 대한 상태&quot;&lt;/b&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;만약 쇼핑몰에 들어간다고 가정을 해볼까요?&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;h4 data-ke-size=&quot;size20&quot;&gt;상태관리, 꼭 해야할까?&lt;/h4&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;아래에 예상되는 문제들을 작성해 두었습니다.&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;b&gt;prop drilling :&amp;nbsp;&lt;/b&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;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;h2 data-ke-size=&quot;size26&quot;&gt;React 상태관리 툴 알아보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제, React에서 사용하는 상태관리 툴들에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 많이 사용된는 강력한 툴로는 Recoil과 Redux가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Recoil&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1684823162515&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;Installation | Recoil&quot; data-og-description=&quot;NPM&quot; data-og-host=&quot;recoiljs.org&quot; data-og-source-url=&quot;https://recoiljs.org/docs/introduction/installation&quot; data-og-url=&quot;https://recoiljs.org/docs/introduction/installation&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cYxaB3/hySJn80xRw/tw10kCpKkXdIHXxyNhPJGk/img.png?width=1420&amp;amp;height=646&amp;amp;face=0_0_1420_646,https://scrap.kakaocdn.net/dn/cctza4/hySJeKX3xm/PN5a2VBBkg1ZZEfKkswlXk/img.png?width=1420&amp;amp;height=646&amp;amp;face=0_0_1420_646&quot;&gt;&lt;a href=&quot;https://recoiljs.org/docs/introduction/installation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://recoiljs.org/docs/introduction/installation&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cYxaB3/hySJn80xRw/tw10kCpKkXdIHXxyNhPJGk/img.png?width=1420&amp;amp;height=646&amp;amp;face=0_0_1420_646,https://scrap.kakaocdn.net/dn/cctza4/hySJeKX3xm/PN5a2VBBkg1ZZEfKkswlXk/img.png?width=1420&amp;amp;height=646&amp;amp;face=0_0_1420_646');&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;Installation | Recoil&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;NPM&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;recoiljs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;1. 특징&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Recoil은 페이스북에서 만든 상태 관리 라이브러리로, React 개념에 기반합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 개념으로는 atom과 selector이 있습니다.&lt;/p&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;u&gt;&lt;b&gt;2. 장점&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React와의 밀접성으로 인해 React를 미리 알고 있다면 Recoil을 배우기가 쉽습니다.&lt;/li&gt;
&lt;li&gt;상태 업데이트가 필요한 컴포넌트만 렌더링하므로 성능이 우수합니다.&amp;nbsp;&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;&lt;u&gt;&lt;b&gt;3. 단점&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단점이라고 보자면 Redux에 비해 사용된 지 얼마 안되어 커뮤니티의 데이터풀이 Redux만큼 크지 않습니다.&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;&lt;u&gt;&lt;b&gt;4. Recoil은 이 때 사용하면 좋아요&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React의 컨셉과 가깝게 상태관리를 하고 싶을 경우&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Redux&lt;/b&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1684823204395&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;Usage Guides Index | Redux&quot; data-og-description=&quot;The Usage Guides section provides practical guidance on how to correctly use Redux in real-world applications, including project setup and architecture, patterns, practices, and techniques.&quot; data-og-host=&quot;redux.js.org&quot; data-og-source-url=&quot;https://redux.js.org/usage/&quot; data-og-url=&quot;https://redux.js.org/usage/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bfSTag/hySJma9Hie/wYpvbQqjISVqbyqgCiWDb1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bsXTaF/hySJma9Hj7/NDXFkWq5nqxHJnb5Db28Qk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://redux.js.org/usage/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redux.js.org/usage/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bfSTag/hySJma9Hie/wYpvbQqjISVqbyqgCiWDb1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bsXTaF/hySJma9Hj7/NDXFkWq5nqxHJnb5Db28Qk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Usage Guides Index | Redux&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Usage Guides section provides practical guidance on how to correctly use Redux in real-world applications, including project setup and architecture, patterns, practices, and techniques.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redux.js.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;1. 특징&lt;/b&gt;&lt;/u&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;모든 상태 변화는 액션에 의해 트리거되고, 리듀서에 의해 상태가 새로 생성됩니다.&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;u&gt;&lt;b&gt;2. 장점&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Time Travel 디버깅 기능을 제공합니다.&lt;/li&gt;
&lt;li&gt;= 이전 상태로 되돌아갈 수 있습니다.&lt;/li&gt;
&lt;li&gt;미들웨어를 지원하여 커스텀 로직을 쉽게 추가할 수 있습니다.&lt;/li&gt;
&lt;li&gt;커뮤니티의 크기가 크고 많은 리소스가 있어서 문제가 발생하였을 때 빠르게 도움을 받을 수 있습니다.&lt;/li&gt;
&lt;li&gt;상태의 변화가 예측이 가능하고 추적하기가 쉽습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;3. 단점&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태 관리를 위해 많은 코드를 작성해야 합니다.&lt;/li&gt;
&lt;li&gt;러닝커브가 높은 편이며, 알아야 할 기본 개념들이 많습니다.&lt;/li&gt;
&lt;li&gt;비동기 로직을 처리하기 위해서는 추가 라이브러리를 적용해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;4. Redux는 이 때 사용하면 좋아요&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션의 상태 변화를 철저하게 관리하고 싶은 경우 사용합니다.&lt;/li&gt;
&lt;li&gt;많은 큰 규모 프로젝트나 팀에서는 Redux를 쓰고 현재 많은 Regacy 코드가 Redux를 사용합니다.&lt;/li&gt;
&lt;li&gt;미들웨어를 사용하여 커스텀 로직을 추가하고 싶은 경우에 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Recoil vs Redux&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux와&amp;nbsp;Recoil의&amp;nbsp;가장&amp;nbsp;큰&amp;nbsp;차이점은&amp;nbsp;접근&amp;nbsp;방식입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux는&amp;nbsp;&lt;b&gt;전역&amp;nbsp;상태&amp;nbsp;관리&lt;/b&gt;에&amp;nbsp;중점을&amp;nbsp;두는&amp;nbsp;반면,&amp;nbsp;Recoil은&amp;nbsp;&lt;b&gt;로컬&amp;nbsp;상태&amp;nbsp;관리&lt;/b&gt;에&amp;nbsp;중점을&amp;nbsp;둡니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Recoil은&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;반면에&amp;nbsp;Redux는&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;어떤&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>react</category>
      <category>Recoil</category>
      <category>Redux</category>
      <category>state management</category>
      <category>단점</category>
      <category>비교</category>
      <category>사용해야하는이유</category>
      <category>상태 관리</category>
      <category>장점</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/224</guid>
      <comments>https://time-map-installer.tistory.com/224#entry224comment</comments>
      <pubDate>Tue, 23 May 2023 15:27:04 +0900</pubDate>
    </item>
    <item>
      <title>[D3.js] 강력한 성능의 시각화 라이브러리, D3.js에 대해 알아보자</title>
      <link>https://time-map-installer.tistory.com/223</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KE6K0/btsgG4y92II/uT07QvAKcPMfP16HMdkiK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KE6K0/btsgG4y92II/uT07QvAKcPMfP16HMdkiK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KE6K0/btsgG4y92II/uT07QvAKcPMfP16HMdkiK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKE6K0%2FbtsgG4y92II%2FuT07QvAKcPMfP16HMdkiK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;280&quot; height=&quot;280&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js 라는 이름을 들어 보셨나요? &quot;Data-Driven Documents&quot;의 줄임말인 D3.js는 웹 표준을 이용해 데이터를 시각화하는 데 사용되는 강력한 자바스크립트 라이브러리입니다. 주로 프론트엔드 고도화의 주제로 많이 쓰이며 시각화 중에 가장 복잡하고 커스터마이징이 자유롭다는 특징을 가지고 있습니다.&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;D3.js란&amp;nbsp;무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js는 웹에서 데이터를 시각화하기 위한 도구로서, 대화형 그래프와 동적인 시각화를 만드는 데에 주로 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM, HTML, CSS, SVG 등의 웹 표준 기술을 활용해 사용자의 데이터를 브라우저 상에서 시각적으로 표현할 수 있게 해줍니다.&lt;br /&gt;&lt;br /&gt;D3.js의&amp;nbsp;중요한&amp;nbsp;특징&amp;nbsp;중&amp;nbsp;하나는&amp;nbsp;&lt;b&gt;'데이터&amp;nbsp;중심'&lt;/b&gt;&amp;nbsp;접근법입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&amp;nbsp;D3.js는&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;D3.js의&amp;nbsp;장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js는 동적인 데이터 시각화를 지원하며, 사용자의 입력에 반응하는 인터랙티브한 시각화를 만들 수 있습니다.&lt;/p&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;u&gt;&lt;b&gt;1. 동적이고 인터랙티브한 시각화&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js는 데이터를 실시간으로 로드하고, 사용자의 상호작용에 반응하는 동적인 시각화를 생성하는 데 매우 유용합니다. 이를 통해 사용자는 시각화를 통해 데이터를 더 깊이 이해하고 탐색할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;2. 데이터 중심의 접근&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js는 데이터 중심의 접근 방식을 사용하여 웹 페이지의 요소를 생성하고, 갱신하며, 제거합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 개발자가 데이터를 더욱 효과적으로 제어하고 표현할 수 있게 해줍니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;3. 웹 표준 기술 활용&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js는 HTML, CSS, SVG 등의 웹 표준 기술을 이용하여 데이터를 시각화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 D3.js가 현대 웹 브라우저와 호환되며, 플러그인이나 추가 기술 없이 독립적으로 동작할 수 있음을 의미합니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;4. 강력한 시각화 기능과 유연성&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js는 표준적인 차트와 그래프뿐 아니라 복잡한 지형도, 코르도그램, 썬버스트 다이어그램 등의 시각화를 생성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 개발자가 직접 사용자 정의 시각화를 생성할 수 있도록 매우 유연한 API를 제공합니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;5. 다양한 도구와 커뮤니티의 지원&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js는 많은 도구, 플러그인, 라이브러리가 지원하며, 활발한 커뮤니티에 의해 지원됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 문제 해결이나 새로운 기능 구현에 있어 큰 도움이 됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;D3.js의&amp;nbsp;핵심&amp;nbsp;개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;1. Selections&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3의 가장 중요한 개념 중 하나는 선택(Selection)입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택은 웹 페이지의 특정 요소나 그룹을 참조하고, 해당 요소들에 대한 동작을 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3의 모든 동작은 선택을 기반으로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 D3의 select 및 selectAll 메서드를 사용하여 요소를 선택하고, attr 및 style 메서드를 사용하여 선택한 요소의 속성이나 스타일을 변경할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;2. Data Binding&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3는 데이터 바인딩을 지원하여 데이터와 웹 페이지 요소를 연결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 웹 문서를 데이터에 기반하여 동적으로 생성하고 갱신할 수 있게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3의 data 메서드를 사용하면 웹 페이지의 요소에 데이터를 바인딩할 수 있으며, enter, exit, update 메서드를 사용하면 데이터의 변화에 따른 요소의 생성, 갱신, 제거를 제어할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;3. Transitions&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3는 전환(Transition) 기능을 통해 요소의 속성이나 스타일이 시간에 따라 부드럽게 변화하는 모습을 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 사용자가 시각화의 변화를 더욱 자연스럽게 인지하도록 돕습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3의 transition 메서드를 사용하면 요소의 전환을 생성하고, duration, delay, ease 메서드를 사용하면 전환의 지속시간, 지연시간, 이징 방식을 제어할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;4. Scales and Axes&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3는 스케일(Scale)과 축(Axis) 기능을 제공하여 데이터를 효과적으로 시각화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케일은 데이터의 도메인(입력 범위)를 시각화의 범위(출력 범위)로 매핑하는 함수이며, 축은 스케일을 기반으로 시각화의 축을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3의 다양한 스케일 메서드와 축 메서드를 사용하면 데이터를 원하는 방식으로 시각화할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;5. SVGs&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3는 SVG(Scalable Vector Graphics)를 사용하여 데이터를 시각화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SVG는 벡터 기반의 그래픽을 생성하며, 웹 페이지에 직접적으로 통합될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SVG 요소의 속성과 스타일을 조작함으로써, D3는 데이터를 다양한 형태와 색상의 그래픽으로 표현할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;D3.js를 시작하려면?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js를 사용하려면 자바스크립트에 대한 기본적인 이해가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 웹 페이지에 D3.js를 추가하는 가장 간단한 방법은 다음과 같은 스크립트 태그를 사용하는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684678873590&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://d3js.org/d3.v5.min.js&quot;&amp;gt;&amp;lt;/script&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;이렇게 스크립트를 추가한 후에는 D3.js를 이용해 데이터를 시각화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D3.js는 이러한 특징들을 통해 다양한 데이터 시각화 요구 사항을 충족시키는 데 있어 강력한 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 D3.js를 효과적으로 사용하기 위해서는 HTML, CSS, SVG, 그리고 자바스크립트에 대한 깊은 이해가 필요하며, 이는 D3.js의 학습 곡선을 다소 가파르게 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 한번 D3.js를 익히면, 웹 상에서 가장 진보된 데이터 시각화를 만들 수 있는 도구 중 하나가 될 것입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Frontend</category>
      <category>D3</category>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>Library</category>
      <category>TypeScript</category>
      <category>시각화</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/223</guid>
      <comments>https://time-map-installer.tistory.com/223#entry223comment</comments>
      <pubDate>Mon, 22 May 2023 00:56:14 +0900</pubDate>
    </item>
    <item>
      <title>[Nest.JS] TypeScript 기반 프레임워크, Nest.JS에 대해 알아보자</title>
      <link>https://time-map-installer.tistory.com/222</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/neTHD/btsgEfuvYwi/JPu3D3gxlI7ddiZxQyhkK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/neTHD/btsgEfuvYwi/JPu3D3gxlI7ddiZxQyhkK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/neTHD/btsgEfuvYwi/JPu3D3gxlI7ddiZxQyhkK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FneTHD%2FbtsgEfuvYwi%2FJPu3D3gxlI7ddiZxQyhkK0%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;387&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;773&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 저는 여러분에게 백엔드 개발에 있어 효율적이고 강력한 도구인 Nest.js에 대해 소개하고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글을 통해 Nest.js가 무엇인지 알아보고, 지금 프론트엔드 개발자의 길을 향해가고 있는 제가 이 기술을 배웠을 때의 이점이 무엇인지에 대해서도 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Nest.js란&amp;nbsp;무엇인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는&amp;nbsp;효율적이고&amp;nbsp;확장&amp;nbsp;가능한&amp;nbsp;서버&amp;nbsp;사이드&amp;nbsp;애플리케이션을&amp;nbsp;구축하기&amp;nbsp;위한&amp;nbsp;고성능의&amp;nbsp;Node.js&amp;nbsp;프레임워크입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Typescript를 기반으로 하지만, Javascript로도 사용이 가능하며,&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;OOP(Object Oriented Programming)&lt;br /&gt;FP(Functional Programming)&lt;br /&gt;FRP(Functional Reactive Programming)&lt;/blockquote&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;u&gt;&lt;b&gt;1. OOP (객체 지향 프로그래밍, Object-Oriented Programming)&lt;/b&gt;&lt;/u&gt;&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OOP의&amp;nbsp;주요&amp;nbsp;원칙에는&amp;nbsp;캡슐화,&amp;nbsp;상속,&amp;nbsp;다형성&amp;nbsp;등이&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;2. FP (함수형 프로그래밍, Functional Programming)&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;함수형&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;이는&amp;nbsp;데이터를&amp;nbsp;변화시키는&amp;nbsp;것보다&amp;nbsp;불변성(Immutable)을&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;함수형&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;Haskell,&amp;nbsp;Lisp,&amp;nbsp;Scala&amp;nbsp;등이&amp;nbsp;대표적인&amp;nbsp;함수형&amp;nbsp;프로그래밍&amp;nbsp;언어입니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;3. FRP (함수 반응형 프로그래밍, Functional Reactive Programming)&lt;/b&gt;&lt;/u&gt;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&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;FRP는&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Nest.js의&amp;nbsp;장점은&amp;nbsp;무엇인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;1. Typescript 기반&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는 TypeScript를 기반으로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript는 JavaScript의 슈퍼셋으로, 정적 타입을 지원하여 더 안전하고 예측 가능한 코드를 작성하게 돕습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Typescript는 코드 자동완성, 인텔리센스, 얼리 에러 디텍션 등의 IDE 기능을 제공하여 개발 과정을 보다 편리하게 만듭니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;2. 확장성과 모듈성&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는 확장성 있는 소프트웨어를 만드는 데 필요한 모든 도구를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 Nest.js는 모듈, 미들웨어, 파이프, 가드, 인터셉터 등의 기능을 지원하며, 이들은 개발자가 효과적으로 코드를 구성하고 재사용하게 해줍니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;3. DI (Dependency Injection) 컨테이너&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는 강력한 의존성 주입 기능을 제공합니다.&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;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;4. 테스트 용이성&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는 Jest와 Supertest 같은 테스트 프레임워크와 쉽게 통합할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로 인해 단위 테스트와 통합 테스트를 쉽게 작성하고 실행할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;5. 데코레이터와 메타 프로그래밍&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는 데코레이터를 광범위하게 활용하여 코드를 더욱 직관적이고 간결하게 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데코레이터를 사용하면 메타데이터를 활용하여 코드의 동작을 정의하거나 변경할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;6. 강력한 CLI&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는 프로젝트를 생성하고, 컴포넌트를 추가하고, 테스트를 실행하는 등의 작업을 수행하는데 사용할 수 있는 강력한 CLI(Command Line Interface)를 제공합니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;7. 다양한 라이브러리와의 통합&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는 TypeORM, Sequelize, Mongoose 등의 ORM 라이브러리와 쉽게 통합될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Passport, GraphQL, Websockets 등의 라이브러리와의 통합도 지원합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring&amp;nbsp;boot와&amp;nbsp;Nest.js의&amp;nbsp;유사성&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Nest.js는 그 구조와 디자인 패턴이 Java의 Spring Framework와 많이 닮아 있습니다.&lt;br /&gt;그래서&amp;nbsp;Spring&amp;nbsp;Framework&amp;nbsp;혹은&amp;nbsp;Spring&amp;nbsp;Boot에&amp;nbsp;익숙한&amp;nbsp;개발자들은&amp;nbsp;Nest.js를&amp;nbsp;보다&amp;nbsp;쉽게&amp;nbsp;접근할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;&lt;br /&gt;&lt;b&gt;양쪽 모두에게 해당되는 공통점이 있다면 서비스, 컨트롤러, 모듈 등의 구성 요소를 사용하여 애플리케이션의 구조를 체계적으로 관리할 수 있다는 점이라고 볼 수 있습니다.&lt;/b&gt;&lt;/blockquote&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;u&gt;&lt;b&gt;1. 구조화된 개발 패턴 지원&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot와 Nest.js는 모두 구조화된 개발 패턴을 적극적으로 지원합니다.&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;u&gt;&lt;b&gt;2. 의존성 주입 (Dependency Injection)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot와 Nest.js 모두 강력한 의존성 주입 메커니즘을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능은 모듈 간의 느슨한 결합을 가능하게 하여 코드의 유연성과 테스트 용이성을 높입니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;3. 데코레이터와 어노테이션의 사용&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js의 데코레이터는 Spring Boot의 어노테이션과 유사한 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들은 클래스, 메서드, 속성 등에 메타데이터를 첨부하여 프로그램의 동작 방식을 선언적으로 제어합니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;4. 테스트 지원&lt;/b&gt;&lt;/u&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;공통점&amp;nbsp;때문에&amp;nbsp;Spring&amp;nbsp;Boot에&amp;nbsp;익숙한&amp;nbsp;개발자라면&amp;nbsp;Nest.js를&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;또한,&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 data-ke-size=&quot;size16&quot;&gt;이는&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&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;개발자가&amp;nbsp;Nest.js를&amp;nbsp;사용했을&amp;nbsp;때의&amp;nbsp;이점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드&amp;nbsp;개발자가&amp;nbsp;Nest.js를&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;u&gt;&lt;b&gt;1. JavaScript/TypeScript 환경의 이해도 증가&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는 TypeScript 기반으로 작성되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 JavaScript 또는 TypeScript로 프론트엔드를 개발하는 개발자들은 Nest.js를 사용하여 백엔드를 더 효과적으로 이해하고 개발할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 한 언어를 통해 전체 스택을 관리할 수 있게 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;2. 통합 개발 환경&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는 웹 애플리케이션의 백엔드와 프론트엔드를 모두 관리할 수 있는 통합 개발 환경을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 프론트엔드와 백엔드 사이의 상호 작용을 더 잘 이해하고 효율적으로 개발하도록 돕습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;3. 프론트엔드와 백엔드 간의 통신 이해도 증가&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js를 사용하면 프론트엔드와 백엔드 간의 통신 방식을 직접 경험하고 이해하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 엔드포인트를 작성하고 데이터 형식을 결정하며, 이러한 과정을 통해 프론트엔드와 백엔드의 상호 작용에 대한 더 깊은 이해를 얻을 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;4. Full Stack 개발 능력 향상&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js를 통해 백엔드 개발에 대한 이해를 키우면, 프론트엔드 개발자는 풀 스택 개발자로 성장할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 개인의 기술적 역량을 넓히고, 시장에서 더 많은 기회를 얻는데 도움이 될 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;5. 코드 재사용&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js와 프론트엔드 프레임워크(예: Angular, React, Vue 등)가 모두 JavaScript/TypeScript로 작성되어 있으므로, 일부 로직이나 유틸리티 함수 등을 프론트엔드와 백엔드 사이에서 재사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 개발 시간을 단축시키고 코드의 일관성을 유지하는데 도움이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;6. 커뮤니티 및 학습 자료&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nest.js는 커뮤니티가 활발하고 풍부한 학습 자료가 있는 프레임워크입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 자료들은 프론트엔드 개발자가 Nest.js를 배우고, 문제를 해결하는 데 큰 도움이 됩니다.&lt;/p&gt;
&lt;figure id=&quot;og_1684601007715&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;Nestjs&quot; data-og-description=&quot;Nestjs content on DEV Community&quot; data-og-host=&quot;dev.to&quot; data-og-source-url=&quot;https://dev.to/t/nestjs&quot; data-og-url=&quot;https://dev.to/t/nestjs&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oKQSV/hySG4A2qft/VLOhJIn6bCau28MYB9EYFk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://dev.to/t/nestjs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dev.to/t/nestjs&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oKQSV/hySG4A2qft/VLOhJIn6bCau28MYB9EYFk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Nestjs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Nestjs content on DEV Community&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dev.to&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;지금까지 Nest에 대해 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 장점과 특징들을 가지고 있는 Nest를 선택할 이유들이 참 많은 것 같습니다.&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;여러 프레임워크에 대해 자세히 알아보고 신중히 결정하는 것도 좋으니, 조금 더 알아보고 하는 것도 괜찮습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 선택은 여러분의 몫이니까요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;End&lt;/b&gt;&lt;/p&gt;</description>
      <category>Development Study/Backend</category>
      <category>JavaScript</category>
      <category>nest</category>
      <category>Nest.js</category>
      <category>TypeScript</category>
      <category>백엔드</category>
      <category>선택</category>
      <category>이유</category>
      <category>장점</category>
      <category>프론트엔드</category>
      <author>ThreeLight</author>
      <guid isPermaLink="true">https://time-map-installer.tistory.com/222</guid>
      <comments>https://time-map-installer.tistory.com/222#entry222comment</comments>
      <pubDate>Sun, 21 May 2023 01:51:25 +0900</pubDate>
    </item>
  </channel>
</rss>