<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Mishka's 블로그</title>
    <link>https://mishka.tistory.com/</link>
    <description>Mishka 의 개발 끄적끄적</description>
    <language>ko</language>
    <pubDate>Tue, 12 May 2026 03:08:27 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Mishka</managingEditor>
    <image>
      <title>Mishka's 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/4716582/attach/9db1f643c2664b659672b6dc724f4ece</url>
      <link>https://mishka.tistory.com</link>
    </image>
    <item>
      <title>Vibe Coding에서 Spec-Driven Development로: AI에게 제대로 개발시키는 방법</title>
      <link>https://mishka.tistory.com/46</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구를 쓰면 개발 속도는 확실히 빨라집니다.&amp;nbsp;Cursor, Claude Code, GitHub Copilot, Codex 같은 도구에게 요구사항을 말하면 코드 초안이 빠르게 만들어집니다. 간단한 화면이나 프로토타입을 만들 때는 특히 유용합니다.&lt;/p&gt;
&lt;p data-end=&quot;346&quot; data-start=&quot;312&quot; data-ke-size=&quot;size16&quot;&gt;하지만 실제 프로젝트에 적용해보면 이런 문제가 자주 생깁니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;381&quot; data-start=&quot;348&quot; data-ke-style=&quot;style3&quot;&gt;- &amp;ldquo;처음에는 잘 돌아가는 것처럼 보였는데 &lt;b&gt;요구사항과 다르다&lt;/b&gt;.&amp;rdquo;&lt;br /&gt;- &amp;ldquo;코드는 그럴듯하지만 &lt;b&gt;예외 처리&lt;/b&gt;가 빠져 있다.&amp;rdquo;&lt;br /&gt;- &amp;ldquo;기존 프로젝트 구조를 무시하고 &lt;b&gt;새 패턴&lt;/b&gt;을 만들어버렸다.&amp;rdquo;&lt;br /&gt;- &amp;ldquo;수정해달라고 했더니 &lt;b&gt;관련 없는 파일&lt;/b&gt;까지 바뀌었다.&amp;rdquo;&lt;/blockquote&gt;
&lt;p data-end=&quot;517&quot; data-start=&quot;476&quot; data-ke-size=&quot;size16&quot;&gt;이런 방식의 AI 코딩을 흔히 &lt;b&gt;Vibe Coding&lt;/b&gt;이라고 부릅니다. 대략적인 느낌만 전달하고, AI가 만든 코드를 실행해보면서 되는 것 같으면 넘어가는 방식입니다.&amp;nbsp;작은 개인 프로젝트에서는 괜찮을 수 있지만, 실무 프로젝트에서는 위험합니다.&lt;/p&gt;
&lt;p data-end=&quot;517&quot; data-start=&quot;476&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;517&quot; data-start=&quot;476&quot; data-ke-size=&quot;size16&quot;&gt;AI는 빠르게 코드를 만들 수 있지만, 우리가 원하는 제품의 의도와 제약 조건을 항상 정확히 이해하지는 못하기 때문입니다.&lt;/p&gt;
&lt;p data-end=&quot;763&quot; data-start=&quot;689&quot; data-ke-size=&quot;size16&quot;&gt;그래서 최근에는 AI에게 바로 코드를 작성시키기보다, 먼저 명세를 만들고 그 명세를 기준으로 개발하게 하는 방식이 주목받고 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;822&quot; data-start=&quot;765&quot; data-ke-size=&quot;size16&quot;&gt;이 방식이 바로 &lt;b&gt;Spec-Driven Development&lt;/b&gt;, 즉 &lt;b&gt;명세 기반 개발&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h2 data-end=&quot;855&quot; data-start=&quot;836&quot; data-section-id=&quot;1l5k96h&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Vibe Coding의 문제점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;884&quot; data-start=&quot;857&quot; data-ke-size=&quot;size16&quot;&gt;Vibe Coding의 장점은 빠르다는 것입니다.&amp;nbsp;예를 들어 AI에게 이렇게 요청할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;929&quot; data-start=&quot;914&quot; data-ke-style=&quot;style3&quot;&gt;&amp;ldquo;로그인 페이지 만들어줘.&amp;rdquo;&lt;br /&gt;&amp;ldquo;게시판 CRUD 만들어줘.&amp;rdquo;&lt;br /&gt;&amp;ldquo;관리자 대시보드 만들어줘.&amp;rdquo;&lt;/blockquote&gt;
&lt;p data-end=&quot;1004&quot; data-start=&quot;967&quot; data-ke-size=&quot;size16&quot;&gt;AI는 금방 코드를 만들어줍니다. 하지만 문제는 그 다음입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1004&quot; data-start=&quot;967&quot;&gt;로그인 페이지를 만들긴 했지만 인증 실패 케이스가 빠져 있을 수 있습니다.&lt;/li&gt;
&lt;li data-end=&quot;1084&quot; data-start=&quot;1049&quot;&gt;게시판 CRUD를 만들긴 했지만 권한 체크가 없을 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;li data-end=&quot;1084&quot; data-start=&quot;1049&quot;&gt;관리자 대시보드를 만들긴 했지만 기존 디자인 시스템을 사용하지 않았을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1178&quot; data-start=&quot;1134&quot; data-ke-size=&quot;size16&quot;&gt;겉으로는 완성된 것처럼 보여도 내부적으로는 많은 문제가 숨어 있을 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1209&quot; data-start=&quot;1180&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1209&quot; data-start=&quot;1180&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Vibe Coding&lt;/b&gt;의 핵심 문제는 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 요구사항이 &lt;b&gt;불명확&lt;/b&gt;하다.&lt;br /&gt;- AI가 빈 부분을 &lt;b&gt;임의&lt;/b&gt;로 가정한다.&lt;br /&gt;- &lt;b&gt;완료 기준&lt;/b&gt;이 없다.&lt;br /&gt;- 수정 범위가 커질 수 있다.&lt;br /&gt;- &lt;b&gt;테스트 기준이 부족&lt;/b&gt;하다.&lt;/blockquote&gt;
&lt;p data-end=&quot;1342&quot; data-start=&quot;1296&quot; data-ke-size=&quot;size16&quot;&gt;즉, Vibe Coding은 &lt;b&gt;속도는 빠르지만 방향과 기준이 약한 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;1342&quot; data-start=&quot;1296&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1377&quot; data-start=&quot;1349&quot; data-section-id=&quot;1a0d2bn&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Spec-Driven Development란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;1434&quot; data-start=&quot;1379&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;S&lt;/b&gt;pec-&lt;b&gt;D&lt;/b&gt;riven &lt;b&gt;D&lt;/b&gt;evelopment는 말 그대로 &lt;b&gt;명세를 중심으로 개발하는 방식&lt;/b&gt;입니다. 여기서 명세는 단순한 문서가 아닙니다.&lt;br /&gt;사람과 AI가 함께 바라보는 기준입니다.&amp;nbsp;Vibe Coding은 보통 이렇게 진행됩니다.&lt;/p&gt;
&lt;p data-end=&quot;1549&quot; data-start=&quot;1512&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;1549&quot; data-start=&quot;1512&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아이디어 &amp;rarr; 바로 코드 생성 &amp;rarr; 실행해보기 &amp;rarr; 다시 수정&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;1589&quot; data-start=&quot;1551&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1589&quot; data-start=&quot;1551&quot; data-ke-size=&quot;size16&quot;&gt;반면 Spec-Driven Development는 이렇게 진행됩니다.&lt;/p&gt;
&lt;p data-end=&quot;1656&quot; data-start=&quot;1591&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-end=&quot;1656&quot; data-start=&quot;1591&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;아이디어 &amp;rarr; 명세 작성 &amp;rarr; 구현 계획 작성 &amp;rarr; 작업 단위 분해 &amp;rarr; 코드 생성 &amp;rarr; 명세 기준으로 검증&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;1700&quot; data-start=&quot;1658&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1700&quot; data-start=&quot;1658&quot; data-ke-size=&quot;size16&quot;&gt;가장 큰 차이는 &lt;b&gt;코드를 작성하기 전에 기준을 먼저 만든다&lt;/b&gt;는 점입니다.&amp;nbsp;명세에는 다음 내용이 들어갈 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 무엇을 만들 것인가?&lt;br /&gt;- 어떤 사용자가 사용하는가?&lt;br /&gt;- 정상 동작은 무엇인가?&lt;br /&gt;- 예외 상황은 무엇인가?&lt;br /&gt;- 완료 기준은 무엇인가?&lt;/blockquote&gt;
&lt;p data-end=&quot;1899&quot; data-start=&quot;1804&quot; data-ke-size=&quot;size16&quot;&gt;AI는 이 명세를 기준으로 코드를 작성합니다. 그래서 개발자는 AI에게 단순히 &amp;ldquo;만들어줘&amp;rdquo;라고 시키는 사람이 아니라, AI가 따라야 할 기준을 설계하는 사람이 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2026년 4월 29일 오전 09_42_41.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1086&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcZMM1/dJMcaib6iH7/aE5ojc9kNp5UPAseYImKn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcZMM1/dJMcaib6iH7/aE5ojc9kNp5UPAseYImKn0/img.png&quot; data-alt=&quot;Vibe Coding VS Spec Driven Development&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcZMM1/dJMcaib6iH7/aE5ojc9kNp5UPAseYImKn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcZMM1%2FdJMcaib6iH7%2FaE5ojc9kNp5UPAseYImKn0%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;1448&quot; height=&quot;1086&quot; data-filename=&quot;ChatGPT Image 2026년 4월 29일 오전 09_42_41.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Vibe Coding VS Spec Driven Development&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-end=&quot;1947&quot; data-start=&quot;1906&quot; data-section-id=&quot;pe42gu&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-end=&quot;1947&quot; data-start=&quot;1906&quot; data-section-id=&quot;pe42gu&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기본 흐름: Specify, Plan, Tasks, Implement&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1994&quot; data-start=&quot;1949&quot; data-ke-size=&quot;size16&quot;&gt;Spec-Driven Development는 다음 네 단계로 이해할 수 있습니다.&lt;/p&gt;
&lt;h2 data-end=&quot;2023&quot; data-start=&quot;1996&quot; data-section-id=&quot;17ys7d5&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Specify: 무엇을 만들지 정의하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;2066&quot; data-start=&quot;2025&quot; data-ke-size=&quot;size16&quot;&gt;Specify 단계에서는 구현 방법보다 &lt;b&gt;무엇을 만들지&lt;/b&gt;에 집중합니다.&amp;nbsp;예를 들어 &amp;ldquo;로그인 기능 만들어줘&amp;rdquo;라고만 하면 부족합니다.&lt;/p&gt;
&lt;p data-end=&quot;2123&quot; data-start=&quot;2102&quot; data-ke-size=&quot;size16&quot;&gt;조금 더 좋은 명세는 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;2268&quot; data-start=&quot;2125&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;사용자는 이메일과 비밀번호로 로그인할 수 있어야 한다. &lt;br /&gt;이메일 형식이 잘못되면 입력 단계에서 안내 메시지를 보여준다. &lt;br /&gt;로그인 실패 시 &amp;lsquo;이메일 또는 비밀번호를 확인해주세요&amp;rsquo;라는 메시지를 보여준다. &lt;br /&gt;로그인 성공 시 사용자는 /dashboard로 이동한다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;2304&quot; data-start=&quot;2270&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 AI가 구현해야 할 범위가 훨씬 명확해집니다.&amp;nbsp;명세는 길 필요가 없습니다. 대신 &lt;b&gt;구체적&lt;/b&gt;이어야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;2304&quot; data-start=&quot;2270&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;2370&quot; data-start=&quot;2345&quot; data-section-id=&quot;1jvbube&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Plan: 어떻게 구현할지 계획하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;2405&quot; data-start=&quot;2372&quot; data-ke-size=&quot;size16&quot;&gt;Plan 단계에서는 명세를 바탕으로 구현 전략을 정리합니다.&amp;nbsp;예를 들어 로그인 기능이라면 다음을 정할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 로그인 페이지 경로&lt;br /&gt;- 사용할 API&lt;br /&gt;- 폼 검증 방식&lt;br /&gt;- 에러 처리 방식&lt;br /&gt;- 기존 컴포넌트 재사용 여부&lt;br /&gt;- 테스트 방식&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2523&quot; data-start=&quot;2510&quot; data-ke-size=&quot;size16&quot;&gt;예시는 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 로그인 페이지는 /login 경로에 만든다.&lt;br /&gt;- 폼 검증은 zod를 사용한다.&lt;br /&gt;- API 호출은 src/api/auth.ts에 작성한다.&lt;br /&gt;- UI는 기존 Button, Input 컴포넌트를 재사용한다.&lt;br /&gt;- 테스트는 로그인 성공, 실패, 잘못된 이메일 입력 케이스를 포함한다.&lt;/blockquote&gt;
&lt;p data-end=&quot;2736&quot; data-start=&quot;2692&quot; data-ke-size=&quot;size16&quot;&gt;이 단계의 목적은 AI가 코드를 만들기 전에 구현 방향을 먼저 맞추는 것입니다.&lt;/p&gt;
&lt;p data-end=&quot;2736&quot; data-start=&quot;2692&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;2766&quot; data-start=&quot;2743&quot; data-section-id=&quot;1ql6sr8&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Tasks: 작업 단위로 나누기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;2800&quot; data-start=&quot;2768&quot; data-ke-size=&quot;size16&quot;&gt;Tasks 단계에서는 구현 계획을 작은 작업으로 나눕니다.&amp;nbsp;AI에게 큰 작업을 한 번에 맡기면 수정 범위가 커집니다.&lt;br /&gt;그래서 작고 검증 가능한 단위로 나누는 것이 좋습니다.&amp;nbsp;예를 들어 로그인 기능은 다음처럼 나눌 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. 로그인 페이지 UI 작성&lt;br /&gt;2. 이메일/비밀번호 입력 검증 추가&lt;br /&gt;3. 로그인 API 함수 작성&lt;br /&gt;4. 로그인 성공 시 라우팅 처리&lt;br /&gt;5. 로그인 실패 에러 메시지 처리&lt;br /&gt;6. 테스트 코드 작성&lt;/blockquote&gt;
&lt;p data-end=&quot;3026&quot; data-start=&quot;3008&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3026&quot; data-start=&quot;3008&quot; data-ke-size=&quot;size16&quot;&gt;나쁜 Task는 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;3026&quot; data-start=&quot;3008&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;ldquo;로그인 기능 전체 구현&amp;rdquo;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;3062&quot; data-start=&quot;3044&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3062&quot; data-start=&quot;3044&quot; data-ke-size=&quot;size16&quot;&gt;좋은 Task는 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;3127&quot; data-start=&quot;3064&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;ldquo;기존 Input, Button 컴포넌트를 사용해 로그인 폼 UI만 작성한다. 아직 API 연동은 하지 않는다.&amp;rdquo;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;3165&quot; data-start=&quot;3129&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3165&quot; data-start=&quot;3129&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 나누면 AI가 불필요한 파일을 수정할 가능성이 줄어듭니다.&lt;/p&gt;
&lt;p data-end=&quot;3165&quot; data-start=&quot;3129&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;3200&quot; data-start=&quot;3172&quot; data-section-id=&quot;k13zom&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Implement: 기준에 따라 구현하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;3234&quot; data-start=&quot;3202&quot; data-ke-size=&quot;size16&quot;&gt;Implement 단계에서 AI가 실제 코드를 작성합니다.&lt;/p&gt;
&lt;p data-end=&quot;3305&quot; data-start=&quot;3236&quot; data-ke-size=&quot;size16&quot;&gt;중요한 점은 AI에게 자유롭게 맡기는 것이 아니라, 앞에서 만든 명세와 작업 목록을 기준으로 구현하게 해야 한다는 것입니다.&lt;/p&gt;
&lt;p data-end=&quot;3305&quot; data-start=&quot;3236&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3323&quot; data-start=&quot;3307&quot; data-ke-size=&quot;size16&quot;&gt;좋은 요청은 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;3412&quot; data-start=&quot;3325&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;ldquo;위 명세와 작업 목록 중 1번 작업만 구현해줘. API 연동은 하지 말고 UI만 작성해줘. &lt;br /&gt;기존 컴포넌트 구조를 따르고, 작업 후 변경 파일을 요약해줘.&amp;rdquo;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;3534&quot; data-start=&quot;3451&quot; data-ke-size=&quot;size16&quot;&gt;AI가 라우트, 상태 관리, API, 토큰 저장 방식, UI 컴포넌트, 테스트 방식까지 모두 임의로 결정할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;3602&quot; data-start=&quot;3536&quot; data-ke-size=&quot;size16&quot;&gt;Spec-Driven Development에서 AI는 단독 개발자가 아니라, 명세를 따라 구현하는 실행자에 가깝습니다.&lt;/p&gt;
&lt;p data-end=&quot;3602&quot; data-start=&quot;3536&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;4322&quot; data-start=&quot;4301&quot; data-section-id=&quot;p1w2j9&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AGENTS.md와 함께 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;4383&quot; data-start=&quot;4324&quot; data-ke-size=&quot;size16&quot;&gt;앞선 글에서 다룬 AGENTS.md는 Spec-Driven Development와 함께 쓰기 좋습니다. (&lt;a href=&quot;https://mishka.tistory.com/44&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;AI 코딩 에이전트 제대로 쓰기: AGENTS.md 작성법&lt;/b&gt;&lt;/a&gt;)&amp;nbsp;AGENTS.md에는 프로젝트 공통 규칙을 적습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 패키지 매니저는 pnpm을 사용한다.&lt;br /&gt;- 새 파일은 TypeScript로 작성한다.&lt;br /&gt;- 기존 컴포넌트를 우선 재사용한다.&lt;br /&gt;- 테스트를 삭제하지 않는다.&lt;br /&gt;- 작업 후 typecheck, lint, test를 실행한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4590&quot; data-start=&quot;4559&quot; data-ke-size=&quot;size16&quot;&gt;반면 Spec 문서에는 특정 기능의 요구사항을 적습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 로그인 실패 시 에러 메시지를 표시한다.&lt;br /&gt;- 요청 중에는 버튼을 비활성화한다.&lt;br /&gt;- 성공 시 /dashboard로 이동한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4703&quot; data-start=&quot;4683&quot; data-ke-size=&quot;size16&quot;&gt;즉, 역할을 나누면 다음과 같습니다.&amp;nbsp;AGENTS.md는 프로젝트 전체 규칙입니다.&lt;/p&gt;
&lt;p data-end=&quot;4755&quot; data-start=&quot;4734&quot; data-ke-size=&quot;size16&quot;&gt;Spec 문서는 기능별 요구사항입니다.&amp;nbsp;AI 에이전트는 이 두 가지를 함께 참고할 때 더 좋은 결과를 만들 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;4755&quot; data-start=&quot;4734&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4755&quot; data-start=&quot;4734&quot; data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구는 매우 강력합니다.&amp;nbsp;하지만 AI에게 &amp;ldquo;알아서 만들어줘&amp;rdquo;라고만 요청하면, 빠르지만 불안정한 결과가 나올 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;4981&quot; data-start=&quot;4892&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Vibe Coding&lt;/b&gt;은 프로토타입에는 유용하지만, 실무 개발에서는 요구사항 누락, 잘못된 가정, 과도한 수정 범위, 테스트 부족 같은 문제를 만들 수 있습니다.&amp;nbsp;&lt;b&gt;Spec-Driven Development&lt;/b&gt;는 이 문제를 줄이기 위한 방법입니다.&lt;/p&gt;
&lt;p data-end=&quot;5043&quot; data-start=&quot;5029&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- Vibe Coding은 느낌 중심으로 바로 코드를 만드는 방식입니다.&lt;br /&gt;- Spec-Driven Development는 명세를 먼저 만들고, 그 명세를 기준으로 코드를 만드는 방식입니다.&lt;br /&gt;- 핵심 흐름은 Specify, Plan, Tasks, Implement입니다.&lt;br /&gt;- 명세는 사람과 AI가 함께 바라보는 기준입니다.&lt;br /&gt;- 작업을 작게 나누면 AI의 실수를 줄이고 리뷰하기 쉬워집니다.&lt;br /&gt;- AGENTS.md와 함께 사용하면 프로젝트 규칙과 기능 요구사항을 함께 관리할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-end=&quot;5422&quot; data-start=&quot;5317&quot; data-ke-size=&quot;size16&quot;&gt;앞으로 AI 코딩 도구를 더 적극적으로 사용할수록 개발자의 역할은 단순히 코드를 직접 치는 것에서, &lt;b&gt;명확한 요구사항을 정의하고 AI의 결과물을 검증하는 것&lt;/b&gt;으로 바뀔 가능성이 큽니다.&lt;/p&gt;
&lt;p data-end=&quot;5472&quot; data-start=&quot;5424&quot; data-ke-size=&quot;size16&quot;&gt;AI에게 코드를 잘 쓰게 하고 싶다면, 먼저 AI가 따라야 할 기준을 잘 써야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;5489&quot; data-start=&quot;5474&quot; data-ke-size=&quot;size16&quot;&gt;그 기준이 바로 명세입니다.&lt;/p&gt;
&lt;p data-end=&quot;5489&quot; data-start=&quot;5474&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-turn-start-message=&quot;true&quot; data-message-model-slug=&quot;gpt-5-5-thinking&quot; data-message-id=&quot;3a8b8bd3-5d20-454b-abc6-b1e609cb5f84&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2 data-end=&quot;5504&quot; data-start=&quot;5496&quot; data-section-id=&quot;3c0sv5&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;5673&quot; data-start=&quot;5506&quot; data-ke-size=&quot;size16&quot;&gt;GitHub Blog - Spec-driven development with AI&lt;br /&gt;&lt;a href=&quot;https://github.blog/ai-and-ml/generative-ai/spec-driven-development-with-ai-get-started-with-a-new-open-source-toolkit/&quot;&gt;https://github.blog/ai-and-ml/generative-ai/spec-driven-development-with-ai-get-started-with-a-new-open-source-toolkit/&lt;/a&gt;&lt;/p&gt;
&lt;p data-end=&quot;5727&quot; data-start=&quot;5675&quot; data-ke-size=&quot;size16&quot;&gt;GitHub Spec Kit&lt;br /&gt;&lt;a href=&quot;https://github.com/github/spec-kit&quot;&gt;https://github.com/github/spec-kit&lt;/a&gt;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;5852&quot; data-start=&quot;5729&quot; data-ke-size=&quot;size16&quot;&gt;Martin Fowler - Understanding Spec-Driven Development&lt;br /&gt;&lt;a href=&quot;https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html&quot;&gt;https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Programming</category>
      <category>Agents.md</category>
      <category>AI</category>
      <category>SDD</category>
      <category>Spec-Driven Development</category>
      <category>vibecoding</category>
      <category>개발</category>
      <category>바이브코딩</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/46</guid>
      <comments>https://mishka.tistory.com/46#entry46comment</comments>
      <pubDate>Wed, 29 Apr 2026 09:49:04 +0900</pubDate>
    </item>
    <item>
      <title>MCP 서버 만들기 입문: AI 에이전트가 내 API를 호출하게 하는 방법</title>
      <link>https://mishka.tistory.com/45</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 AI 개발 도구는 단순히 질문에 답하는 수준을 넘어섰습니다.&amp;nbsp;이제 AI는 코드를 읽고, 파일을 수정하고, 테스트를 실행하고, GitHub 이슈를 확인하고, 외부 API를 호출하는 방향으로 발전하고 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;297&quot; data-start=&quot;276&quot; data-ke-size=&quot;size16&quot;&gt;그런데 여기서 중요한 문제가 생깁니다.&lt;/p&gt;
&lt;p data-end=&quot;297&quot; data-start=&quot;276&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;346&quot; data-start=&quot;299&quot; data-ke-size=&quot;size16&quot;&gt;AI가 외부 도구나 데이터에 안전하고 일관된 방식으로 접근하려면 어떻게 해야 할까요?&lt;/p&gt;
&lt;p data-end=&quot;381&quot; data-start=&quot;348&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 AI에게 이런 작업을 시키고 싶다고 해보겠습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;408&quot; data-start=&quot;383&quot; data-ke-style=&quot;style3&quot;&gt;&amp;ldquo;내 프로젝트의 GitHub 이슈를 조회해줘&amp;rdquo;&lt;br /&gt;&amp;ldquo;고객 주문 API에서 최근 주문 목록을 가져와줘&amp;rdquo;&lt;br /&gt;&amp;ldquo;로컬 문서에서 특정 내용을 찾아줘&amp;rdquo;&lt;br /&gt;&amp;ldquo;사내 데이터베이스에서 프로젝트 상태를 확인해줘&amp;rdquo;&lt;/blockquote&gt;
&lt;p data-end=&quot;524&quot; data-start=&quot;491&quot; data-ke-size=&quot;size16&quot;&gt;이런 작업을 하려면 AI가 외부 시스템과 연결되어야 합니다.&amp;nbsp;기존 방식이라면 각 AI 도구마다 별도의 연동 코드를 만들어야 했습니다.&lt;/p&gt;
&lt;p data-end=&quot;665&quot; data-start=&quot;568&quot; data-ke-size=&quot;size16&quot;&gt;Claude용 GitHub 연동을 만들고, Cursor용 GitHub 연동을 만들고, ChatGPT용 GitHub 연동을 만들고, 사내 에이전트용 연동을 또 만드는 식입니다.&lt;/p&gt;
&lt;p data-end=&quot;698&quot; data-start=&quot;667&quot; data-ke-size=&quot;size16&quot;&gt;도구가 늘어날수록 같은 연동을 반복해서 만들어야 합니다. 이 문제를 해결하기 위해 등장한 표준이 바로 &lt;b&gt;MCP(Model Context Protocol)&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;698&quot; data-start=&quot;667&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;783&quot; data-start=&quot;775&quot; data-section-id=&quot;xewhac&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MCP란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;824&quot; data-start=&quot;785&quot; data-ke-size=&quot;size16&quot;&gt;MCP는 &lt;b&gt;Model Context Protocol&lt;/b&gt;의 약자입니다.&lt;/p&gt;
&lt;p data-end=&quot;887&quot; data-start=&quot;826&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 &lt;b&gt;AI 애플리케이션이 외부 도구와 데이터를 사용할 수 있도록 연결해주는 표준 프로토콜&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;933&quot; data-start=&quot;889&quot; data-ke-size=&quot;size16&quot;&gt;공식 문서에서는 MCP를 AI 애플리케이션을 위한 USB-C 포트에 비유합니다.&amp;nbsp;USB-C가 노트북, 모니터, 충전기, 외장하드 같은 다양한 장치를 하나의 표준으로 연결하듯이, MCP는 AI 에이전트가 파일 시스템, 데이터베이스, GitHub, 사내 API 같은 외부 시스템과 연결될 수 있게 합니다.&amp;nbsp;즉, MCP는 &lt;b&gt;AI와 외부 도구 사이의 공통 연결 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;933&quot; data-start=&quot;889&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1118&quot; data-start=&quot;1101&quot; data-section-id=&quot;1h5fnlo&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MCP를 왜 알아야 할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;1170&quot; data-start=&quot;1120&quot; data-ke-size=&quot;size16&quot;&gt;AI 서비스를 만들거나 AI 코딩 도구를 사용하다 보면 결국 다음 단계로 넘어가게 됩니다.&amp;nbsp;처음에는 AI에게 질문만 합니다. 그다음에는 AI에게 코드를 작성하게 합니다. 그리고 어느 순간부터는 AI에게 실제 작업을 시키고 싶어집니다.&lt;/p&gt;
&lt;p data-end=&quot;1270&quot; data-start=&quot;1255&quot; data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 작업입니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;1302&quot; data-start=&quot;1272&quot; data-ke-style=&quot;style3&quot;&gt;&amp;ldquo;이 GitHub 이슈를 보고 관련 파일을 수정해줘.&amp;rdquo;&lt;br /&gt;&amp;ldquo;이 프로젝트의 테스트 실패 원인을 찾아줘.&amp;rdquo;&lt;br /&gt;&amp;ldquo;DB에서 최근 주문 데이터를 보고 오류 패턴을 분석해줘.&amp;rdquo;&lt;br /&gt;&amp;ldquo;내 API 문서를 읽고 SDK 예시 코드를 만들어줘.&amp;rdquo;&lt;/blockquote&gt;
&lt;p data-end=&quot;1482&quot; data-start=&quot;1399&quot; data-ke-size=&quot;size16&quot;&gt;이런 작업을 하려면 AI가 단순히 대화만 해서는 안 됩니다. 외부 시스템에 접근하고, 필요한 데이터를 가져오고, 도구를 호출할 수 있어야 합니다.&amp;nbsp;MCP는 이 연결 방식을 표준화합니다. 그래서 앞으로 AI 에이전트, AI 코딩 도구, 사내 AI 업무 자동화 도구를 만들 때 MCP는 중요한 기반 기술이 될 가능성이 큽니다.&lt;/p&gt;
&lt;p data-end=&quot;1482&quot; data-start=&quot;1399&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1603&quot; data-start=&quot;1590&quot; data-section-id=&quot;gaghuf&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MCP의 기본 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;1633&quot; data-start=&quot;1605&quot; data-ke-size=&quot;size16&quot;&gt;MCP는 크게 세 가지 요소로 이해할 수 있습니다.&lt;/p&gt;
&lt;h4 data-end=&quot;1657&quot; data-start=&quot;1635&quot; data-ke-size=&quot;size20&quot;&gt;첫 번째는 &lt;b&gt;MCP Host&lt;/b&gt;입니다.&lt;/h4&gt;
&lt;p data-end=&quot;1762&quot; data-start=&quot;1659&quot; data-ke-size=&quot;size16&quot;&gt;MCP Host는 사용자가 직접 사용하는 AI 애플리케이션입니다.&lt;br /&gt;예를 들면 Claude Desktop, Cursor, AI IDE, 사내 AI 챗봇 같은 도구가 여기에 해당합니다.&lt;/p&gt;
&lt;p data-end=&quot;1762&quot; data-start=&quot;1659&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;1788&quot; data-start=&quot;1764&quot; data-ke-size=&quot;size20&quot;&gt;두 번째는 &lt;b&gt;MCP Client&lt;/b&gt;입니다.&lt;/h4&gt;
&lt;p data-end=&quot;1882&quot; data-start=&quot;1790&quot; data-ke-size=&quot;size16&quot;&gt;MCP Client는 Host 안에서 MCP Server와 통신하는 역할을 합니다.&lt;br /&gt;사용자가 직접 다루기보다는 AI 애플리케이션 내부에서 동작한다고 보면 됩니다.&lt;/p&gt;
&lt;p data-end=&quot;1882&quot; data-start=&quot;1790&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;1908&quot; data-start=&quot;1884&quot; data-ke-size=&quot;size20&quot;&gt;세 번째는 &lt;b&gt;MCP Server&lt;/b&gt;입니다.&lt;/h4&gt;
&lt;p data-end=&quot;2042&quot; data-start=&quot;1910&quot; data-ke-size=&quot;size16&quot;&gt;MCP Server는 실제 외부 도구나 데이터와 연결되는 서버입니다.&lt;br /&gt;예를 들어 GitHub 이슈를 조회하는 MCP Server, 로컬 파일을 읽는 MCP Server, 사내 API를 호출하는 MCP Server를 만들 수 있습니다. 구조를 간단히 표현하면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;2126&quot; data-start=&quot;2068&quot; data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;사용자 &amp;rarr; AI 애플리케이션 &amp;rarr; MCP Client &amp;rarr; MCP Server &amp;rarr; 외부 시스템&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-end=&quot;2238&quot; data-start=&quot;2128&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2238&quot; data-start=&quot;2128&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자가 AI에게 &amp;ldquo;최근 GitHub 이슈를 정리해줘&amp;rdquo;라고 말하면, AI 애플리케이션은 MCP Server를 통해 GitHub API를 호출하고, 그 결과를 바탕으로 답변할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;2238&quot; data-start=&quot;2128&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;2274&quot; data-start=&quot;2245&quot; data-section-id=&quot;1l7e7yr&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MCP Server는 무엇을 제공할 수 있을까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;2306&quot; data-start=&quot;2276&quot; data-ke-size=&quot;size16&quot;&gt;MCP Server는 보통 세 가지 기능을 제공합니다.&lt;/p&gt;
&lt;h4 data-end=&quot;2327&quot; data-start=&quot;2308&quot; data-ke-size=&quot;size20&quot;&gt;첫 번째는 &lt;b&gt;Tools&lt;/b&gt;입니다.&lt;/h4&gt;
&lt;p data-end=&quot;2359&quot; data-start=&quot;2329&quot; data-ke-size=&quot;size16&quot;&gt;Tools는 AI가 호출할 수 있는 함수나 액션입니다.&amp;nbsp; 예를 들어 다음과 같은 도구를 만들 수 있습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;2432&quot; data-start=&quot;2390&quot; data-ke-style=&quot;style3&quot;&gt;get_github_issues&lt;br /&gt;최근 GitHub 이슈 목록을 조회한다.&lt;br /&gt;&lt;br /&gt;get_order_list&lt;br /&gt;최근 주문 목록을 조회한다.&lt;/blockquote&gt;
&lt;p data-end=&quot;2568&quot; data-start=&quot;2535&quot; data-ke-size=&quot;size16&quot;&gt;즉, Tools는 &lt;b&gt;AI가 실제로 실행할 수 있는 기능&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-end=&quot;2568&quot; data-start=&quot;2535&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;2593&quot; data-start=&quot;2570&quot; data-ke-size=&quot;size20&quot;&gt;두 번째는 &lt;b&gt;Resources&lt;/b&gt;입니다.&lt;/h4&gt;
&lt;p data-end=&quot;2625&quot; data-start=&quot;2595&quot; data-ke-size=&quot;size16&quot;&gt;Resources는 AI가 읽을 수 있는 데이터입니다.&amp;nbsp;예를 들어 다음과 같은 리소스가 있을 수 있습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;2712&quot; data-start=&quot;2657&quot; data-ke-style=&quot;style3&quot;&gt;프로젝트 README&lt;br /&gt;API 문서&lt;br /&gt;로그 파일&lt;br /&gt;데이터베이스 테이블 정보&lt;br /&gt;고객 정책 문서&lt;/blockquote&gt;
&lt;p data-end=&quot;2760&quot; data-start=&quot;2714&quot; data-ke-size=&quot;size16&quot;&gt;Tools가 &amp;ldquo;행동&amp;rdquo;이라면 Resources는 &amp;ldquo;읽을 수 있는 정보&amp;rdquo;에 가깝습니다.&lt;/p&gt;
&lt;p data-end=&quot;2760&quot; data-start=&quot;2714&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;2783&quot; data-start=&quot;2762&quot; data-ke-size=&quot;size20&quot;&gt;세 번째는 &lt;b&gt;Prompts&lt;/b&gt;입니다.&lt;/h4&gt;
&lt;p data-end=&quot;2830&quot; data-start=&quot;2785&quot; data-ke-size=&quot;size16&quot;&gt;Prompts는 AI가 특정 작업을 더 잘 수행하도록 미리 정의한 프롬프트입니다.&amp;nbsp;예를 들어 다음과 같은 프롬프트를 제공할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;2925&quot; data-start=&quot;2864&quot; data-ke-style=&quot;style3&quot;&gt;버그 리포트 분석 프롬프트&lt;br /&gt;API 문서 요약 프롬프트&lt;br /&gt;코드 리뷰 프롬프트&lt;br /&gt;장애 보고서 작성 프롬프트&lt;/blockquote&gt;
&lt;p data-end=&quot;2977&quot; data-start=&quot;2927&quot; data-ke-size=&quot;size16&quot;&gt;이 세 가지 중에서 입문 단계에서는 &lt;b&gt;Tools&lt;/b&gt;를 먼저 이해하는 것이 가장 좋습니다.&lt;/p&gt;
&lt;p data-end=&quot;2977&quot; data-start=&quot;2927&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;2977&quot; data-start=&quot;2927&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MCP Tool을 설계할 때 중요한 점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP Tool을 만들 때는 단순히 &amp;ldquo;기능을 열어준다&amp;rdquo;가 아니라, AI에게 어떤 권한을 줄 것인지 신중하게 설계해야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;4433&quot; data-start=&quot;4417&quot; data-ke-size=&quot;size16&quot;&gt;특히 다음 기준이 중요합니다.&lt;/p&gt;
&lt;p data-end=&quot;4460&quot; data-start=&quot;4435&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;4460&quot; data-start=&quot;4435&quot; data-ke-size=&quot;size20&quot;&gt;첫 번째, 도구 이름은 &lt;b&gt;구체적&lt;/b&gt;으로 작성합니다.&lt;/h4&gt;
&lt;p data-end=&quot;4499&quot; data-start=&quot;4462&quot; data-ke-size=&quot;size16&quot;&gt;get_data보다는 get_project_status가 좋습니다.&lt;/p&gt;
&lt;p data-end=&quot;4499&quot; data-start=&quot;4462&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;4530&quot; data-start=&quot;4501&quot; data-ke-size=&quot;size20&quot;&gt;두 번째, 설명은 AI가 &lt;b&gt;판단&lt;/b&gt;할 수 있게 작성합니다.&lt;/h4&gt;
&lt;p data-end=&quot;4592&quot; data-start=&quot;4532&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;프로젝트 정보를 가져온다&amp;rdquo;보다는 &amp;ldquo;프로젝트 ID를 기준으로 진행률, 상태, 담당자를 조회한다&amp;rdquo;가 좋습니다.&lt;/p&gt;
&lt;p data-end=&quot;4592&quot; data-start=&quot;4532&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;4617&quot; data-start=&quot;4594&quot; data-ke-size=&quot;size20&quot;&gt;세 번째, 입력값은 &lt;b&gt;최소한&lt;/b&gt;으로 제한합니다.&lt;/h4&gt;
&lt;p data-end=&quot;4664&quot; data-start=&quot;4619&quot; data-ke-size=&quot;size16&quot;&gt;AI에게 너무 많은 입력 자유도를 주면 의도하지 않은 요청이 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;4664&quot; data-start=&quot;4619&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;4691&quot; data-start=&quot;4666&quot; data-ke-size=&quot;size20&quot;&gt;네 번째, &lt;b&gt;반환값&lt;/b&gt;은 필요한 정보만 포함합니다.&lt;/h4&gt;
&lt;p data-end=&quot;4733&quot; data-start=&quot;4693&quot; data-ke-size=&quot;size16&quot;&gt;내부 ID, 토큰, 민감한 고객 정보 등은 반환하지 않는 것이 좋습니다.&lt;/p&gt;
&lt;p data-end=&quot;4733&quot; data-start=&quot;4693&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;4761&quot; data-start=&quot;4735&quot; data-ke-size=&quot;size20&quot;&gt;다섯 번째, &lt;b&gt;쓰기 작업&lt;/b&gt;은 더 신중하게 다룹니다.&lt;/h4&gt;
&lt;p data-end=&quot;4807&quot; data-start=&quot;4763&quot; data-ke-size=&quot;size16&quot;&gt;조회 작업은 상대적으로 안전하지만, 생성&amp;middot;수정&amp;middot;삭제 작업은 위험할 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;4838&quot; data-start=&quot;4809&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 도구는 특히 주의해야 합니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;4936&quot; data-start=&quot;4840&quot; data-ke-style=&quot;style3&quot;&gt;delete_user&lt;br /&gt;update_payment_status&lt;br /&gt;send_email_to_customer&lt;br /&gt;deploy_production&lt;br /&gt;run_sql_query&lt;/blockquote&gt;
&lt;p data-end=&quot;4979&quot; data-start=&quot;4938&quot; data-ke-size=&quot;size16&quot;&gt;이런 도구는 반드시 권한 체크, 입력 검증, 승인 흐름을 고려해야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;4979&quot; data-start=&quot;4938&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4979&quot; data-start=&quot;4938&quot; data-ke-size=&quot;size16&quot;&gt;MCP는 AI 시대에 점점 중요해질 가능성이 높은 기술입니다. 이전의 AI 도구가 &amp;ldquo;질문에 답하는 도구&amp;rdquo;였다면,&lt;/p&gt;
&lt;p data-end=&quot;6860&quot; data-start=&quot;6776&quot; data-ke-size=&quot;size16&quot;&gt;MCP를 활용한 AI 에이전트는 &lt;b&gt;&amp;ldquo;외부 시스템과 연결되어 실제 작업을 수행하는 도구&amp;rdquo;&lt;/b&gt;에 가까워집니다.&lt;/p&gt;
&lt;p data-end=&quot;6860&quot; data-start=&quot;6776&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;6876&quot; data-start=&quot;6862&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-end=&quot;6913&quot; data-start=&quot;6878&quot; data-ke-style=&quot;style3&quot;&gt;- MCP는 Model Context Protocol의 약자입니다.&lt;br /&gt;- AI 애플리케이션이 외부 도구와 데이터를 사용할 수 있도록 연결하는 표준입니다.&lt;br /&gt;- MCP Server는 AI가 호출할 수 있는 Tools, 읽을 수 있는 Resources, 재사용 가능한 Prompts를 제공할 수 있습니다.&lt;br /&gt;- 입문 단계에서는 Tools부터 이해하는 것이 좋습니다.&lt;br /&gt;- MCP Tool은 이름, 설명, 입력값, 권한 범위를 명확하게 설계해야 합니다.&lt;br /&gt;- 쓰기 작업이나 민감한 정보 접근은 반드시 보안과 승인 흐름을 고려해야 합니다.&lt;/blockquote&gt;
&lt;p data-end=&quot;6959&quot; data-start=&quot;6915&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;7236&quot; data-start=&quot;7165&quot; data-ke-size=&quot;size16&quot;&gt;앞으로 AI 코딩 에이전트나 사내 AI 업무 자동화 도구를 만들 계획이 있다면, MCP는 한 번쯤 꼭 이해해둘 만한 기술입니다.&lt;/p&gt;
&lt;p data-end=&quot;7303&quot; data-start=&quot;7238&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 거창한 자동화보다 &lt;b&gt;&amp;ldquo;AI가 안전하게 조회할 수 있는 작은 도구 하나&amp;rdquo;&lt;/b&gt;를 만들어보는 것부터 시작하면 좋습니다.&lt;/p&gt;
&lt;p data-end=&quot;7303&quot; data-start=&quot;7238&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;7318&quot; data-start=&quot;7310&quot; data-section-id=&quot;3c0sv5&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;7409&quot; data-start=&quot;7320&quot; data-ke-size=&quot;size16&quot;&gt;Model Context Protocol 공식 문서&lt;br /&gt;&lt;a href=&quot;https://modelcontextprotocol.io/docs/getting-started/intro&quot;&gt;https://modelcontextprotocol.io/docs/getting-started/intro&lt;/a&gt;&lt;/p&gt;
&lt;p data-end=&quot;7486&quot; data-start=&quot;7411&quot; data-ke-size=&quot;size16&quot;&gt;MCP TypeScript SDK&lt;br /&gt;&lt;a href=&quot;https://github.com/modelcontextprotocol/typescript-sdk&quot;&gt;https://github.com/modelcontextprotocol/typescript-sdk&lt;/a&gt;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;7560&quot; data-start=&quot;7488&quot; data-ke-size=&quot;size16&quot;&gt;Anthropic MCP 소개&lt;br /&gt;&lt;a href=&quot;https://www.anthropic.com/news/model-context-protocol&quot;&gt;https://www.anthropic.com/news/model-context-protocol&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>AI</category>
      <category>API</category>
      <category>MCP</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/45</guid>
      <comments>https://mishka.tistory.com/45#entry45comment</comments>
      <pubDate>Tue, 28 Apr 2026 15:19:02 +0900</pubDate>
    </item>
    <item>
      <title>AI 코딩 에이전트 제대로 쓰기: AGENTS.md 작성법</title>
      <link>https://mishka.tistory.com/44</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 개발 방식은 빠르게 바뀌고 있다.&amp;nbsp;이전에는 ChatGPT에게 코드를 물어보고, 답변을 복사해서 프로젝트에 붙여 넣는 방식이 많았다.&lt;br /&gt;하지만 이제는 Cursor, Claude Code, GitHub Copilot, Codex 같은 AI 코딩 에이전트가 실제 코드베이스를 읽고, 파일을 수정하고, 테스트를 실행하고, PR 단위 작업까지 수행하는 방향으로 발전하고 있다. 이때 중요한 문제가 생긴다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;AI는 우리 프로젝트의 규칙을 어떻게 알 수 있을까?&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;사람 개발자는 README를 읽고, 기존 코드를 보고, 팀 컨벤션을 파악하면서 작업한다. &lt;/span&gt;&lt;br /&gt;&lt;span&gt;하지만 AI 에이전트에게는 프로젝트 규칙을 더 명확하게 알려줘야 한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;이때 사용하는 파일이 바로 `&lt;b&gt;AGENTS.md`&lt;/b&gt;다. &lt;/span&gt;쉽게 말해 &lt;b&gt;AI 에이전트를 위한 작업 지침서&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;README에는 보통 프로젝트 소개, 설치 방법, 실행 방법이 들어간다.&lt;br /&gt;반면 AGENTS.md에는 AI가 코드를 수정할 때 지켜야 할 규칙을 적는다.&lt;/p&gt;
&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;blockquote data-ke-style=&quot;style3&quot;&gt;- 패키지 매니저는 pnpm을 사용한다.&lt;br /&gt;- 새 파일은 TypeScript로 작성한다.&lt;br /&gt;- any 타입은 되도록 사용하지 않는다.&lt;br /&gt;- 코드 수정 후 pnpm typecheck, pnpm lint, pnpm test를 실행한다.&lt;br /&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;즉, AGENTS.md는 AI에게 &amp;ldquo;이 프로젝트에서는 이렇게 일해줘&amp;rdquo;라고 알려주는 문서다.&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;왜 AGENTS.md가 필요할까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구는 점점 똑똑해지고 있지만, 프로젝트마다 다른 규칙까지 자동으로 정확히 알지는 못한다.&lt;/p&gt;
&lt;p data-end=&quot;1336&quot; data-start=&quot;1294&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 어떤 프로젝트는 npm을 쓰고, 어떤 프로젝트는 pnpm을 쓴다.&lt;/p&gt;
&lt;pre id=&quot;code_1777346854838&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install
// or
pnpm install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 의존성을 설치하는 명령어지만, 프로젝트에 따라 하나만 맞는 경우가 많다.&lt;/p&gt;
&lt;p data-end=&quot;1468&quot; data-start=&quot;1435&quot; data-ke-size=&quot;size16&quot;&gt;또 어떤 팀은 default export를 선호할 수 있고, 다른 팀은 named export만 사용할 수도 있다.&lt;/p&gt;
&lt;p data-end=&quot;1468&quot; data-start=&quot;1435&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1468&quot; data-start=&quot;1435&quot; data-ke-size=&quot;size16&quot;&gt;AI가 이런 규칙을 모르면 기존 코드 스타일과 다른 코드를 만들 수 있다.&amp;nbsp;결과적으로 다음과 같은 문제가 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1722&quot; data-start=&quot;1701&quot;&gt;실행되지 않는 명령어를 사용한다.&lt;/li&gt;
&lt;li data-end=&quot;1722&quot; data-start=&quot;1701&quot;&gt;기존 코드 스타일과 다른 코드를 만든다.&lt;/li&gt;
&lt;li data-end=&quot;1722&quot; data-start=&quot;1701&quot;&gt;테스트 없이 코드를 수정한다.&lt;/li&gt;
&lt;li data-end=&quot;1722&quot; data-start=&quot;1701&quot;&gt;프로젝트 구조와 다른 위치에 파일을 만든다.&lt;/li&gt;
&lt;li data-end=&quot;1722&quot; data-start=&quot;1701&quot;&gt;사용하지 않는 라이브러리를 새로 추가한다.&lt;/li&gt;
&lt;li data-end=&quot;1722&quot; data-start=&quot;1701&quot;&gt;실패하는 테스트를 삭제하려고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AGENTS.md는 이런 실수를 줄이기 위한 최소한의 안전장치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1939&quot; data-start=&quot;1914&quot; data-section-id=&quot;1e6vbzp&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AGENTS.md에 무엇을 적어야 할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 길고 복잡하게 작성할 필요는 없다. 오히려 짧고 구체적인 문서가 더 좋다.&lt;/p&gt;
&lt;p data-end=&quot;2023&quot; data-start=&quot;1991&quot; data-ke-size=&quot;size16&quot;&gt;AGENTS.md에는 최소한 다음 항목을 넣는 것이 좋다.&lt;/p&gt;
&lt;blockquote data-end=&quot;2023&quot; data-start=&quot;1991&quot; data-ke-style=&quot;style2&quot;&gt;1. 프로젝트 개요&lt;br /&gt;2. 기술 스택&lt;br /&gt;3. 자주 사용하는 명령어&lt;br /&gt;4. 코드 작성 규칙&lt;br /&gt;5. 테스트 규칙&lt;br /&gt;6. 금지 사항&lt;br /&gt;7. 작업 완료 전 체크리스트&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 항목은 AI가 실제로 작업할 때 판단 기준으로 사용할 수 있어야 한다.&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;기본 AGENTS.md 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 루트에 AGENTS.md 파일을 만든다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;my-project&lt;br /&gt;├─ src&lt;br /&gt;├─ package.json&lt;br /&gt;├─ README.md&lt;br /&gt;└─ AGENTS.md&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다음처럼 작성할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;# AGENTS.md &lt;br /&gt;## Project Overview &lt;br /&gt;This project is a TypeScript-based web application. &lt;br /&gt;&lt;br /&gt;## Tech Stack &lt;br /&gt;- TypeScript &lt;br /&gt;- React &lt;br /&gt;- pnpm &lt;br /&gt;- Vitest &lt;br /&gt;&lt;br /&gt;## Commands &lt;br /&gt;- Install dependencies: `pnpm install` &lt;br /&gt;- Start dev server: `pnpm dev` &lt;br /&gt;- Type check: `pnpm typecheck` &lt;br /&gt;- Lint: `pnpm lint` &lt;br /&gt;- Test: `pnpm test` &lt;br /&gt;&lt;br /&gt;## Coding Rules &lt;br /&gt;- Use TypeScript for all new files. &lt;br /&gt;- Do not use `any` unless necessary. &lt;br /&gt;- Follow the existing folder structure. &lt;br /&gt;- Reuse existing components before creating new ones. &lt;br /&gt;- Keep changes focused on the requested task. &lt;br /&gt;&lt;br /&gt;## Testing Rules &lt;br /&gt;- Add tests when changing business logic. &lt;br /&gt;- Update tests when behavior changes. &lt;br /&gt;- Do not remove tests just to make the test suite pass. &lt;br /&gt;&lt;br /&gt;## Do Not &lt;br /&gt;- Do not use `npm install` or `yarn add`. &lt;br /&gt;- Do not add new dependencies without approval. &lt;br /&gt;- Do not modify generated files. &lt;br /&gt;- Do not change public API contracts without updating tests.&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;좋은 AGENTS.md는 길지 않아도 된다. 대신 &lt;b&gt;구체적이어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;작성시 주의할점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;첫 번째, 너무 길게 쓰지 않는다. &lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;span&gt;처음에는 명령어, 코드 스타일, 테스트 규칙, 금지 사항 정도만 작성해도 충분하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두 번째, 추상적인 표현을 피한다.&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;좋은 코드를 작성하세요. ❌&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 이렇게 적는다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Do not use `any` unless necessary. ✅&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;b&gt;세 번째, AI가 마음대로 판단하면 안 되는 부분을 명시한다.&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 앞으로 개발 과정에서 더 많이 사용될 가능성이 높다.&lt;/p&gt;
&lt;p data-end=&quot;6380&quot; data-start=&quot;6326&quot; data-ke-size=&quot;size16&quot;&gt;하지만 AI에게 아무런 지침 없이 작업을 맡기면 프로젝트 규칙과 맞지 않는 코드가 나올 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;6425&quot; data-start=&quot;6382&quot; data-ke-size=&quot;size16&quot;&gt;AGENTS.md는 이런 문제를 줄이기 위한 간단하지만 효과적인 방법이다.&lt;/p&gt;
&lt;p data-end=&quot;6425&quot; data-start=&quot;6382&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같다.&lt;/p&gt;
&lt;blockquote data-end=&quot;6425&quot; data-start=&quot;6382&quot; data-ke-style=&quot;style3&quot;&gt;- AGENTS.md는 AI 에이전트를 위한 작업 지침서다. &lt;br /&gt;- README.md와 역할이 다르다. &lt;br /&gt;- 명령어, 코드 스타일, 테스트 규칙, 금지 사항을 구체적으로 적어야 한다. &lt;br /&gt;- 너무 길게 쓰기보다 짧고 명확하게 작성하는 것이 좋다. &lt;br /&gt;- AGENTS.md는 테스트, 린트, CI와 함께 사용할 때 더 효과적이다.&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;AI 코딩 도구를 제대로 쓰고 싶다면, 먼저 프로젝트 루트에 AGENTS.md 파일 하나를 추가해보자.&lt;/p&gt;
&lt;p data-end=&quot;6731&quot; data-start=&quot;6691&quot; data-ke-size=&quot;size16&quot;&gt;작은 문서 하나가 AI가 작성하는 코드의 품질을 꽤 많이 바꿀 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;6731&quot; data-start=&quot;6691&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;6731&quot; data-start=&quot;6691&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h4&gt;
&lt;p data-end=&quot;6731&quot; data-start=&quot;6691&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6763&quot; data-start=&quot;6743&quot; data-section-id=&quot;1gmyv7f&quot;&gt;&lt;a href=&quot;https://agents.md/&quot;&gt;https://agents.md/&lt;/a&gt;&lt;/li&gt;
&lt;li data-end=&quot;6818&quot; data-start=&quot;6764&quot; data-section-id=&quot;1jdvqak&quot;&gt;&lt;a href=&quot;https://developers.openai.com/codex/guides/agents-md&quot;&gt;https://developers.openai.com/codex/guides/agents-md&lt;/a&gt;&lt;/li&gt;
&lt;li data-end=&quot;6918&quot; data-start=&quot;6819&quot; data-section-id=&quot;1lm8qzw&quot;&gt;&lt;a href=&quot;https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot&quot;&gt;https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot&lt;/a&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/44</guid>
      <comments>https://mishka.tistory.com/44#entry44comment</comments>
      <pubDate>Tue, 28 Apr 2026 12:39:30 +0900</pubDate>
    </item>
    <item>
      <title>모던 CSS 완벽 가이드: JS 없이 구현하는 마법, Nesting과 :has()</title>
      <link>https://mishka.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;UI 상태 하나 바꾸겠다고 오늘도 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;useState&lt;/span&gt;와 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;onMouseEnter&lt;/span&gt;를 번갈아 가며 자바스크립트 코드를 짜셨나요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 복잡한 CSS 계층 구조를 잡기 위해 프로젝트 시작부터 습관적으로 Sass(SCSS) 환경부터 세팅하고 계시진 않나요?&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;이제 무거운 자바스크립트 연산과 외부 전처리기에서 벗어날 때가 되었습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;2026년 현재, 순수 네이티브 CSS만으로도 우리가 JS로 힘들게 구현했던 수많은 로직들을 단 몇 줄의 코드로 우아하게 처리할 수 있는 시대가 열렸습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;불필요한 렌더링을 줄이고 프론트엔드의 성능과 가독성을 한 단계 끌어올려 줄 모던 CSS의 두 가지 마법.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;이제는 선택이 아닌 필수가 될 수도 있는&amp;nbsp;&lt;b data-index-in-node=&quot;76&quot; data-path-to-node=&quot;5&quot;&gt;Nesting&lt;/b&gt;과 &lt;b data-index-in-node=&quot;85&quot; data-path-to-node=&quot;5&quot;&gt;:has()&lt;/b&gt; 선택자의 진짜 위력을 실무 예제와 함께 살펴보겠습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size23&quot;&gt;CSS Nesting 심화: 명시도(Specificity)의 이해&lt;/h3&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;과거에는 중첩된 HTML 구조를 스타일링하기 위해 클래스명을 길게 나열하거나 전처리기를 꼭 써야 했습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;이제는 네이티브 CSS만으로 계층 구조를 직관적으로 표현할 수 있죠.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;하지만 단순히 코드가 깔끔해지는 것을 넘어, 실무에 적용할 때 반드시 알아야 할 점은 바로 &lt;b&gt;명시도(Specificity)&lt;/b&gt;의 작동 방식입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;내부적으로 &lt;b&gt;Nesting&lt;/b&gt;은 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;:is()&lt;/span&gt; 의사 클래스로 감싸진 것처럼 동작합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1771812218036&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 작성한 CSS */
.card {
  h2, .title {
    color: red;
  }
}

/* 브라우저가 해석하는 방식 */
.card :is(h2, .title) {
  color: red;
}&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;span style=&quot;background-color: #dddddd;&quot;&gt;:is()&lt;/span&gt;는 괄호 안의 선택자 중 &lt;b data-index-in-node=&quot;19&quot; data-path-to-node=&quot;10&quot;&gt;가장 높은 명시도&lt;/b&gt;를 따릅니다. 따라서 위 예시에서 h2에만 스타일을 주더라도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;.title&lt;/span&gt;의 명시도(클래스 선택자)가 반영되어 예상보다 우선순위가 높아질 수 있으므로 구조 설계 시 이 점을 반드시 유의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size23&quot;&gt;:has() 선택자 심화: 관계형 선택자의 진가&lt;/h3&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;그동안 CSS의 가장 큰 한계 중 하나는 자식 요소의 상태에 따라 부모 요소의 스타일을 변경할 수 없다는 것이었습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;:has()&lt;/span&gt; 의사 클래스는 이 문제를 완벽하게 해결합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;하지만 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;:has()&lt;/span&gt;를 '부모 선택자'로만 부르는 것은 이 기능의 절반만 이해한 것입니다. 부모뿐만 아니라 &lt;b data-index-in-node=&quot;59&quot; data-path-to-node=&quot;13&quot;&gt;이전 형제(Previous Sibling) 요소&lt;/b&gt;나 &lt;b data-index-in-node=&quot;87&quot; data-path-to-node=&quot;13&quot;&gt;전체 컨텍스트의 상태&lt;/b&gt;를 스타일링할 수 있는 진정한 의미의 '관계형 선택자'입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14&quot;&gt;실전 예제: JS 없는 형제 요소 하이라이트 효과 (Hover Spotlight)&lt;/b&gt; 특정 카드에 마우스를 올렸을 때, 주변의 다른 카드들은 흐리게 만드는 효과를 오직 CSS만으로 구현할 수 있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1771812300457&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* .grid 컨테이너 안에 마우스가 올라간 .card가 '하나라도 있다면' */
.grid:has(.card:hover) .card:not(:hover) {
  opacity: 0.5;
  filter: blur(2px);
  transition: all 0.3s ease;
}

/* 마우스가 올라간 본인 요소 */
.card:hover {
  transform: scale(1.05);
  box-shadow: 0 10px 20px rgba(0,0,0,0.2);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거라면 요소마다 마우스 이벤트를 걸고 React 상태로 관리하여 전체 리렌더링을 유발해야 했던 효과를 이렇게 단 몇 줄로 끝낼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size23&quot;&gt;성능 고려사항 (Performance Considerations)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;'DOM 트리를 역추적하거나 복잡하게 탐색하면 렌더링 성능이 떨어지지 않을까?' 하는 걱정이 드실 수 있습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;하지만 최신 브라우저 엔진은 캐싱 및 무효화(Invalidation) 휴리스틱을 극도로 고도화하여 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;:has()&lt;/span&gt;의 성능을 매우 최적화했습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;극단적으로 깊고 복잡한 DOM 구조가 아니라면, 오히려 JS로 상태를 관리하며 리렌더링 파이프라인을 가동하는 것보다&lt;/p&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;브라우저 네이티브 단에서 처리하는 것이 성능 면에서 훨씬 효율적입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;21&quot; data-ke-size=&quot;size23&quot;&gt;마무리하며: 이제는 다시 '바닐라 CSS'를 돌아볼 때&lt;/h3&gt;
&lt;p data-path-to-node=&quot;22&quot; data-ke-size=&quot;size16&quot;&gt;수많은 프레임워크와 CSS-in-JS 라이브러리들이 쏟아지는 와중에도, 웹 표준은 묵묵히, 그리고 아주 강력하게 발전해 왔습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;22&quot; data-ke-size=&quot;size16&quot;&gt;Nesting과&lt;span style=&quot;background-color: #dddddd;&quot;&gt; :has()&lt;/span&gt;는 그 발전의 정점이라 할 수 있죠. 이 두 가지만 잘 활용해도 우리가 습관적으로 작성하던 불필요한 자바스크립트 코드의 절반은 덜어낼 수 있습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23&quot;&gt;여러분은 현재 프로젝트에서 어떤 CSS 방식을 주로 사용하고 계신가요?&lt;/b&gt; 여전히 Sass나 Styled-components의 강력함에 기대고 계신가요, 아니면 점진적으로 모던 순수 CSS나 Tailwind 같은 유틸리티 클래스로 넘어가고 계신가요?&lt;/p&gt;</description>
      <category>Programming</category>
      <category>css</category>
      <category>CSSNesting</category>
      <category>has선택자</category>
      <category>ModernCSS</category>
      <category>UI개발</category>
      <category>웹표준</category>
      <category>프론트엔드최적화</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/43</guid>
      <comments>https://mishka.tistory.com/43#entry43comment</comments>
      <pubDate>Mon, 23 Feb 2026 11:16:28 +0900</pubDate>
    </item>
    <item>
      <title>React Server Components (RSC) 완벽 이해하기: 렌더링 아키텍처의 혁신</title>
      <link>https://mishka.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;React 개발자라면 요즘 매일같이 듣는 단어가 있을 겁니다. 바로 &lt;b&gt;'React Server Components(RSC)'&lt;/b&gt;죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&quot;Next.js 13 이후로 그냥 기본으로 쓰는 거 아니야?&quot; 혹은 &quot;기존 SSR(서버 사이드 렌더링)이랑 이름만 다르고 비슷한 거겠지&quot;라고 생각하며 무심코 넘어가셨나요? 만약 여전히 단순한 텍스트나 정적 데이터를 띄우기 위해 수백 KB의 자바스크립트 번들을 통째로 브라우저에 내려보내고 있다면, 여러분은 지금 React 렌더링 패러다임의 가장 거대한 변화를 놓치고 있는지도 모릅니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;'클라이언트'와 '서버'의 경계를 허물고, 번들 사이즈 0(Zero)의 기적을 만들어내는 &lt;b&gt;RSC&lt;/b&gt;. 오늘은 매번 헷갈리던 RSC의 진짜 정체와, 왜 이것이 단순한 기능 추가를 넘어 프론트엔드 아키텍처의 완전한 혁신인지 살펴보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기존 SSR과 RSC의 결정적 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발자가 SSR과 RSC를 혼동합니다. Next.js의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;getServerSideProps&lt;/span&gt; 같은 기존 SSR은 서버에서 HTML을 완성하여 내려주지만, 상호작용을 위해서는 결국 전체 자바스크립트 번들을 다운로드하고 하이드레이션(Hydration) 과정을 거쳐야 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, RSC는 렌더링 결과를 HTML이 아닌 &lt;b&gt;직렬화된 특별한 JSON 형태(React Flight 포맷)&lt;/b&gt;로 스트리밍합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;- SSR: 완성된 '그림(HTML)'을 주고, 나중에 '생명(JS)'을 불어넣음.&lt;br /&gt;- RSC: 서버에서 연산이 끝난 정적인 부분은 JS 코드를 아예 클라이언트로 보내지 않음. 오직 인터랙션이 필요한 부분(Client Component)만 분리하여 로드.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size23&quot;&gt;핵심 개념: &quot;use client&quot;와 렌더링 경계 (Boundary)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&quot;use client&quot;&lt;/span&gt; 지시어는 컴포넌트를 클라이언트에서 렌더링하라는 뜻이 아닙니다. 정확히는 &lt;b&gt;&quot;이 지점부터 서버와 클라이언트의 경계를 나눈다&quot;&lt;/b&gt;는 선언입니다.&lt;/p&gt;
&lt;blockquote data-path-to-node=&quot;9&quot; data-ke-style=&quot;style2&quot;&gt;- 서버 컴포넌트는 클라이언트 컴포넌트를 import 할 수 있습니다. (트리의 잎사귀 역할)&lt;br /&gt;- 주의할 점: 클라이언트 컴포넌트 내부에서는 서버 컴포넌트를 직접 import 할 수 없습니다. 단, &lt;b&gt;children prop&lt;/b&gt;을 통해 서버 컴포넌트를 전달받아 렌더링하는 것은 가능합니다. (Composition 패턴)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size23&quot;&gt;실무 활용: 데이터 페칭(Data Fetching)과 Streaming&lt;/h3&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;RSC의 진가는 비동기 데이터 처리에서 나타납니다. &lt;span style=&quot;background-color: #dddddd;&quot;&gt;useEffect&lt;/span&gt;나 React Query 없이, 컴포넌트 자체를 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;async/await&lt;/span&gt;로 선언하여 서버에서 직접 DB나 API를 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1771811526595&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// app/users/page.tsx (Server Component)
import { Suspense } from 'react';
import UserList from './UserList'; //   Client Component
import Loading from './Loading';

// 비동기 컴포넌트
export default async function UsersPage() {
  // 브라우저가 아닌 서버 환경에서 직접 DB 쿼리 실행
  const users = await db.query('SELECT * FROM users'); 

  return (
    &amp;lt;main&amp;gt;
      &amp;lt;h1&amp;gt;사용자 목록&amp;lt;/h1&amp;gt;
      {/* 데이터가 로딩되는 동안 UI를 먼저 스트리밍 */}
      &amp;lt;Suspense fallback={&amp;lt;Loading /&amp;gt;}&amp;gt;
        {/* 직렬화 가능한 데이터(users)만 Client Component로 전달 */}
        &amp;lt;UserList initialData={users} /&amp;gt;
      &amp;lt;/Suspense&amp;gt;
    &amp;lt;/main&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;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;lt;Suspense&amp;gt;&lt;/span&gt;와 결합된 RSC는 데이터가 준비되는 대로 UI 조각을 클라이언트에 점진적으로 스트리밍하므로, TTFB(Time To First Byte)와 FCP(First Contentful Paint) 지표를 획기적으로 개선합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;마무리하며: RSC, 프론트엔드의 새로운 표준이 될까?&lt;/h3&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;React Server Components가 가져다주는 이점&amp;mdash;&lt;b data-index-in-node=&quot;34&quot; data-path-to-node=&quot;3&quot;&gt;제로 번들 사이즈, 직관적인 서버 데이터 페칭, 그리고 빠른 초기 로딩&lt;/b&gt;&amp;mdash;은 분명 강력합니다. 단순히 컴포넌트를 그리는 방식을 넘어, 브라우저와 서버가 어떻게 협력해야 하는지에 대한 근본적인 패러다임 전환이죠.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;물론 은탄환은 아닙니다. RSC를 실무에 도입하다 보면 서버와 클라이언트의 렌더링 경계(Boundary)를 나누는 설계 고민부터, 머리를 아프게 하는 복잡한 캐싱(Caching) 전략까지 기존과는 전혀 다른 낯선 문제들을 마주하게 됩니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이 초기 러닝 커브를 넘어서고 나면, 이전의 렌더링 방식으로 돌아가기 힘들 만큼 효율적인 아키텍처를 경험하실 수 있을 것입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;여러분의 프로젝트는 지금 어디쯤 있나요?&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;이미 Next.js App Router와 RSC를 실무에 적극적으로 도입해 보셨나요, 아니면 아직은 기존 Page Router나 순수 SPA에 머물며 도입 시기를 재고 계신가요?&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;RSC를 적용하며 겪었던 뼈아픈 트러블슈팅 경험이나(특히 캐싱 이슈  !), 반대로 &quot;이건 정말 신세계다!&quot; 싶었던 점이 있다면 &lt;b data-index-in-node=&quot;73&quot; data-path-to-node=&quot;6&quot;&gt;아래 댓글로 자유롭게 공유해 주세요.&lt;/b&gt; 다양한 환경에서 고민하는 개발자분들의 생생한 이야기가 궁금합니다!&lt;/p&gt;</description>
      <category>Programming</category>
      <category>nextjs</category>
      <category>React</category>
      <category>ReactServerComponents</category>
      <category>rsc</category>
      <category>SSR</category>
      <category>렌더링패러다임</category>
      <category>프론트엔드아키텍처</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/42</guid>
      <comments>https://mishka.tistory.com/42#entry42comment</comments>
      <pubDate>Mon, 23 Feb 2026 10:58:55 +0900</pubDate>
    </item>
    <item>
      <title>Storybook으로 React 컴포넌트 문서화하기 - 실무 가이드</title>
      <link>https://mishka.tistory.com/41</link>
      <description>&lt;h1&gt;Storybook으로 React 컴포넌트 문서화하기 - 실무 가이드&lt;/h1&gt;
&lt;h2&gt;  왜 Storybook인가?&lt;/h2&gt;
&lt;p&gt;프론트엔드 개발을 하다 보면 이런 상황을 겪게 됩니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;이 버튼 컴포넌트 어떻게 쓰는 거지?&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;props가 뭐가 있더라?&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;디자이너님, 이 상태 확인해 주세요&amp;quot; (매번 로컬 서버 켜기...)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Storybook&lt;/strong&gt;은 이 모든 문제를 해결합니다. 컴포넌트를 독립적으로 개발하고, 문서화하고, 테스트할 수 있는 워크샵이죠.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  5분 만에 시작하기&lt;/h2&gt;
&lt;h3&gt;설치&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 기존 React 프로젝트에 추가
npx storybook@latest init
설치가 끝나면 자동으로 .storybook 폴더와 예제 스토리가 생성됩니다.

실행

npm run storybook
# http://localhost:6006 에서 확인&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;첫 번째 Story 작성하기&lt;/h3&gt;
&lt;h4&gt;Button 컴포넌트 예제&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Button.tsx

interface ButtonProps {
  variant: &amp;#39;primary&amp;#39; | &amp;#39;secondary&amp;#39; | &amp;#39;danger&amp;#39;;
  size: &amp;#39;sm&amp;#39; | &amp;#39;md&amp;#39; | &amp;#39;lg&amp;#39;;
  children: React.ReactNode;
  disabled?: boolean;
  onClick?: () =&amp;gt; void;
}

export const Button = ({ 
  variant = &amp;#39;primary&amp;#39;, 
  size = &amp;#39;md&amp;#39;, 
  children, 
  disabled,
  onClick 
}: ButtonProps) =&amp;gt; {
  return (
    &amp;lt;button
      className={`btn btn-${variant} btn-${size}`}
      disabled={disabled}
      onClick={onClick}
    &amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Button.stories.tsx

import type { Meta, StoryObj } from &amp;#39;@storybook/react&amp;#39;;
import { Button } from &amp;#39;./Button&amp;#39;;

const meta: Meta&amp;lt;typeof Button&amp;gt; = {
  title: &amp;#39;Components/Button&amp;#39;,
  component: Button,
  tags: [&amp;#39;autodocs&amp;#39;], // 자동 문서 생성!
  argTypes: {
    variant: {
      control: &amp;#39;select&amp;#39;,
      options: [&amp;#39;primary&amp;#39;, &amp;#39;secondary&amp;#39;, &amp;#39;danger&amp;#39;],
      description: &amp;#39;버튼 스타일&amp;#39;,
    },
    size: {
      control: &amp;#39;radio&amp;#39;,
      options: [&amp;#39;sm&amp;#39;, &amp;#39;md&amp;#39;, &amp;#39;lg&amp;#39;],
      description: &amp;#39;버튼 크기&amp;#39;,
    },
    onClick: { action: &amp;#39;clicked&amp;#39; }, // 클릭 이벤트 로깅
  },
};

export default meta;
type Story = StoryObj&amp;lt;typeof Button&amp;gt;;

// 기본 버튼
export const Primary: Story = {
  args: {
    variant: &amp;#39;primary&amp;#39;,
    children: &amp;#39;확인&amp;#39;,
  },
};

// 위험 버튼
export const Danger: Story = {
  args: {
    variant: &amp;#39;danger&amp;#39;,
    children: &amp;#39;삭제&amp;#39;,
  },
};

// 비활성화 상태
export const Disabled: Story = {
  args: {
    variant: &amp;#39;primary&amp;#39;,
    children: &amp;#39;비활성화&amp;#39;,
    disabled: true,
  },
};

// 모든 사이즈 한눈에
export const AllSizes: Story = {
  render: () =&amp;gt; (
    &amp;lt;div style={{ display: &amp;#39;flex&amp;#39;, gap: &amp;#39;1rem&amp;#39;, alignItems: &amp;#39;center&amp;#39; }}&amp;gt;
      &amp;lt;Button variant=&amp;quot;primary&amp;quot; size=&amp;quot;sm&amp;quot;&amp;gt;Small&amp;lt;/Button&amp;gt;
      &amp;lt;Button variant=&amp;quot;primary&amp;quot; size=&amp;quot;md&amp;quot;&amp;gt;Medium&amp;lt;/Button&amp;gt;
      &amp;lt;Button variant=&amp;quot;primary&amp;quot; size=&amp;quot;lg&amp;quot;&amp;gt;Large&amp;lt;/Button&amp;gt;
    &amp;lt;/div&amp;gt;
  ),
};&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;실무 활용 팁&lt;/h1&gt;
&lt;h2&gt;1. MDX로 풍부한 문서 작성&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;Button.mdx

import { Meta, Story, Canvas, Controls } from &amp;#39;@storybook/blocks&amp;#39;;
import * as ButtonStories from &amp;#39;./Button.stories&amp;#39;;

&amp;lt;Meta of={ButtonStories} /&amp;gt;

# Button 컴포넌트

사용자 인터랙션을 위한 기본 버튼 컴포넌트입니다.

## 사용법

import { Button } from &amp;#39;@/components/Button&amp;#39;;

&amp;lt;Button variant=&amp;quot;primary&amp;quot; size=&amp;quot;md&amp;quot;&amp;gt;
  클릭하세요
&amp;lt;/Button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;예제&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;&amp;lt;Canvas of={ButtonStories.Primary} /&amp;gt;

Props

&amp;lt;Controls /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;디자인 가이드라인&lt;/h3&gt;
&lt;p&gt;• Primary: 주요 액션에 사용 (저장, 확인, 제출)&lt;br&gt;• Secondary: 보조 액션에 사용 (취소, 이전)&lt;br&gt;• Danger: 삭제, 경고 등 주의가 필요한 액션&lt;/p&gt;
&lt;h3&gt;2. 폴더 구조 추천&lt;/h3&gt;
&lt;p&gt;src/&lt;br&gt;├── components/&lt;br&gt;│   ├── Button/&lt;br&gt;│   │   ├── Button.tsx&lt;br&gt;│   │   ├── Button.stories.tsx&lt;br&gt;│   │   ├── Button.test.tsx&lt;br&gt;│   │   └── index.ts&lt;br&gt;│   ├── Input/&lt;br&gt;│   └── Card/&lt;/p&gt;
&lt;h3&gt;3. 자동 배포 (GitHub Pages)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# .github/workflows/storybook.yml
name: Deploy Storybook

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: &amp;#39;20&amp;#39;
      - run: npm ci
      - run: npm run build-storybook
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./storybook-static&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Storybook은 단순한 문서화 도구를 넘어, &lt;em&gt;컴포넌트 기반 개발의 핵심 인프라&lt;/em&gt;입니다.&lt;br&gt;오늘 바로 시작해 보세요!&lt;/p&gt;</description>
      <category>Programming</category>
      <category>개발</category>
      <category>스토리북</category>
      <category>실무활용</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/41</guid>
      <comments>https://mishka.tistory.com/41#entry41comment</comments>
      <pubDate>Wed, 11 Feb 2026 14:15:26 +0900</pubDate>
    </item>
    <item>
      <title>Monorepo에서 Multi-stage Docker Builds 활용하여 NodeJS microservices 최적화된 이미지 만들기 (with. TurboRepo와 PNPM)</title>
      <link>https://mishka.tistory.com/40</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;원문: &lt;a href=&quot;https://fintlabs.medium.com/optimized-multi-stage-docker-builds-with-turborepo-and-pnpm-for-nodejs-microservices-in-a-monorepo-c686fdcf051f&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://fintlabs.medium.com/optimized-multi-stage-docker-builds-with-turborepo-and-pnpm-for-nodejs-microservices-in-a-monorepo-c686fdcf051f&lt;/a&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1E0d1/btsHpsItDaL/gSKxx4ydEEFGMSWKMjgIgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1E0d1/btsHpsItDaL/gSKxx4ydEEFGMSWKMjgIgK/img.png&quot; data-alt=&quot;이미지출처: https://fintlabs.medium.com/optimized-multi-stage-docker-builds-with-turborepo-and-pnpm-for-nodejs-microservices-in-a-monorepo-c686fdcf051f&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1E0d1/btsHpsItDaL/gSKxx4ydEEFGMSWKMjgIgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1E0d1%2FbtsHpsItDaL%2FgSKxx4ydEEFGMSWKMjgIgK%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;394&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지출처: https://fintlabs.medium.com/optimized-multi-stage-docker-builds-with-turborepo-and-pnpm-for-nodejs-microservices-in-a-monorepo-c686fdcf051f&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;microservices와 monorepo의 세계에서 Docker 빌드를 최적화 하는 것은 효율성과 성능에 중요합니다.&lt;/blockquote&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;소개 (Introduction)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.docker.com/build/building/multi-stage/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Multi-stage Docker builds&lt;/a&gt;는 가벼우면서 효율적인 컨테이너를 생성하는 기술입니다. 그러나 Turborepo와 PNPM를 함께 사용할 때 &lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;microservice&lt;/span&gt;용 최적화된 Dockerfile을 얻는 것이 어려울 수 있습니다. &lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이 글에서는 &lt;b&gt;TurboRepo&lt;/b&gt;와 &lt;b&gt;PNPM&lt;/b&gt;을 활용하여 &lt;span style=&quot;text-align: left;&quot;&gt;monorepo&lt;/span&gt; 내 &lt;/span&gt;NodeJS microservices의&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;빌드 프로세스를 &lt;/span&gt;Multi-stage builds 기술을 사용하여 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;최적화하는 방법을 살펴보겠습니다.&lt;/span&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;h2 id=&quot;432d&quot; style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;목표(Goal)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;monorepo 내의 모든 NodeJS 기반 microservices를 빌드하기 위해 사용할 수 있는 단일 &lt;b&gt;Dockerfile&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;빌드를 사용자 정의할 수 있도록 빌드인자를 사용합니다.(NodeJS 버전, 빌드할 프로젝트, 노출한 포트)&lt;/li&gt;
&lt;li&gt;특정 마이크로서비스에 필요한 종속성만 설치합니다.&lt;/li&gt;
&lt;li&gt;특정 마이크로서비스의 소스코드(및 종속성 패키지)만 빌드합니다.&lt;/li&gt;
&lt;li&gt;최소한의 필수 종속성 및 번들이 포함된 가벼운 Docker 이미지를 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전제조건(Prerequisites)&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;Docker가 설치되어 있고 &lt;a href=&quot;https://docs.docker.com/build/buildkit/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;BuildKit&lt;/a&gt;이 활성화되어 있습니다.&lt;/li&gt;
&lt;li&gt;Turborepo로 관리되는 monorepo 또는 새로운 것을 설정하는 방법을 알고 있습니다. 그렇지 않은 경우 &lt;a href=&quot;https://turbo.build/repo/docs/getting-started/create-new&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;를 참고하세요&lt;/li&gt;
&lt;li&gt;PNPM Workspace를 이해하고 있습니다. 그렇지 않은경우 다음 링크를 참고하세요 (&lt;a href=&quot;https://pnpm.io/workspaces&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://pnpm.io/workspaces&lt;/a&gt;, &lt;a href=&quot;https://turbo.build/repo/docs/handbook/workspaces&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://turbo.build/repo/docs/handbook/workspaces&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;최종 multi-stage Dockerfile&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi-stage builds는 빌드 프로세스를 여러 스테이지로 분리하여 최적화된 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 최종 Dockerfile을 보여드리겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715838331171&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ARG NODE_VERSION=18.18.0

# Alpine 이미지
FROM node:${NODE_VERSION}-alpine AS alpine
RUN apk update
RUN apk add --no-cache libc6-compat

# Alpine 베이스에 pnpm 및 turbo 설정
FROM alpine as base
RUN npm install pnpm turbo --global
RUN pnpm config set store-dir ~/.pnpm-store

# Prune projects
FROM base AS pruner
ARG PROJECT

WORKDIR /app
COPY . .
RUN turbo prune --scope=${PROJECT} --docker

# 프로젝트 빌드
FROM base AS builder
ARG PROJECT

WORKDIR /app

# 프로젝트의 pnpm-lock.yaml 및 package.json 복사
COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=pruner /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml
COPY --from=pruner /app/out/json/ .

# 종속성 먼저 설치
RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm install --frozen-lockfile

# 프로젝트 소스 코드 복사
COPY --from=pruner /app/out/full/ .

RUN turbo build --filter=${PROJECT}
RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm prune --prod --no-optional
RUN rm -rf ./**/*/src

# Final image
FROM alpine AS runner
ARG PROJECT

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs
USER nodejs

WORKDIR /app
COPY --from=builder --chown=nodejs:nodejs /app .
WORKDIR /app/apps/${PROJECT}

ARG PORT=8080
ENV PORT=${PORT}
ENV NODE_ENV=production
EXPOSE ${PORT}

CMD node dist/main&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;&lt;b&gt;주요 단계 설명(Explanation of significant steps)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 Dockerfile을 이해하기 위해 각 단계가 무엇을 하는지 살펴보겠습니다.&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;빌드인자(Build arguments)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 여러 프로젝트를 빌드하는 데 사용할 수 있는 단일 Dockerfile을 갖기 위해 Docker 빌드 인자를 활용해야 합니다.&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;NODE_VERSION&lt;/b&gt;(선택) - 베이스 이미지 버전을 지정합니다. 여기서는 &lt;b&gt;alpine&lt;/b&gt; 이미지를 사용하여 가능한 최소한의 최종 이미지를 얻습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PROJECT&lt;/b&gt;(필수) - 빌드할 프로젝트를 지정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PORT&lt;/b&gt;(선택) - Docker 이미지를 노출하는 포트를 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;빌드를 위한 베이스 이미지 (Base image for build)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1715838661677&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Alpine 베이스에 pnpm 및 turbo 설정
FROM alpine as base
RUN npm install pnpm turbo --global
RUN pnpm config set store-dir ~/.pnpm-store&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;alpine&lt;/b&gt; 이미지를 베이스로 합니다. npm을 사용하여 전역으로 &lt;b&gt;turbo&lt;/b&gt;와 &lt;b&gt;pnpm&lt;/b&gt;을 설치합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 &lt;b&gt;pnpm&lt;/b&gt;의 저장소 디렉토리를 캐싱에 사용하기 위해 설정합니다.&lt;/p&gt;
&lt;h4 id=&quot;2549&quot; style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Pruning&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1715838809321&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;RUN turbo prune --scope=${PROJECT} --docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령에 대한 설명은 Turborepo의 &lt;a href=&quot;https://turbo.build/repo/docs/reference/command-line-reference/prune&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 문서&lt;/a&gt;를 참고 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;prune&lt;/b&gt; 명령은 다음을 포함하는 &lt;b&gt;out&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;prune 된 &lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;workspace&lt;/span&gt;의 &lt;b&gt;package.json&lt;/b&gt; 파일이 있는 &lt;b&gt;json&lt;/b&gt; 폴더&lt;/li&gt;
&lt;li&gt;prune 된 &lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;workspace&lt;/span&gt;의 전체 소스코드가 포함된 &lt;b&gt;full&lt;/b&gt; 폴더, 하지만 빌드 대상에 필요한 내부 패키지만 포함됩니다.&lt;/li&gt;
&lt;li&gt;실제로 사용되는 &lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;workspace&lt;/span&gt;의 패키지에서 사용되는 원래 루트 lockfile의 prune된 하위 집합만 포함하는 새로 prune 된 &lt;b&gt;lockfile&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;종속성 설치(Installing dependencies)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1715839274330&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 프로젝트의 pnpm-lock.yaml 및 package.json 복사
COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=pruner /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml
COPY --from=pruner /app/out/json/ .

# 종속성 먼저 설치
RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm install --frozen-lockfile&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 prune 된 lockfile 및 prune된 &lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;workspace의 &lt;b&gt;package.json&lt;/b&gt; 파일을 복사하고 &lt;b&gt;devDependencies&lt;/b&gt; 및 &lt;b&gt;dependencies&lt;/b&gt;를 모두 설치합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;&lt;b&gt;RUN&lt;/b&gt; 명령은 &lt;b&gt;BuildKit&lt;/b&gt;의 특수한 캐시 마운트 기능을 사용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;빌드(&lt;/span&gt;Building&lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1715839464015&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 프로젝트 소스 코드 복사
COPY --from=pruner /app/out/full/ .

RUN turbo build --filter=${PROJECT}
RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm prune --prod --no-optional
RUN rm -rf ./**/*/src&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 &lt;b&gt;pruner&lt;/b&gt;이미지에서 prune된 소스 코드를 &lt;b&gt;COPY&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;RUN&lt;/b&gt; 명령에서는 turbo &lt;b&gt;build&lt;/b&gt;를 사용하여 대상 프로젝트를 지정하고 먼저 모든 종속성을 빌드한 다음, 대상 프로젝트를 자체를 빌드합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 &lt;b&gt;RUN&lt;/b&gt; 명령은 &lt;b&gt;pnpm prune --prod&lt;/b&gt;를 사용하여 더 이상 필요하지 않는 &lt;b&gt;devDependencies&lt;/b&gt;를 &lt;b&gt;node_modules&lt;/b&gt;에서 제거합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 &lt;b&gt;RUN&lt;/b&gt; 명령은 모든 &lt;b&gt;src&lt;/b&gt; 폴더를 제거하여 소스 파일이 최종 이미지로 복사되지 않도록 합니다.&lt;/p&gt;
&lt;h4 id=&quot;f853&quot; style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Final production image&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1715839754791&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Final image
FROM alpine AS runner
ARG PROJECT

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs
USER nodejs

WORKDIR /app
COPY --from=builder --chown=nodejs:nodejs /app .
WORKDIR /app/apps/${PROJECT}

ARG PORT=8080
ENV PORT=${PORT}
ENV NODE_ENV=production
EXPOSE ${PORT}

CMD node dist/main&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 이미지에는 최소한의 종속성이 설치된 node-alipne 베이스 이미지가 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 컨테이너를 root로 실행하는 것은 좋지 않으므로 비루트 사용자를 만듭니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;COPY --from=builder --chown=nodejs:nodejs /app .&lt;br /&gt;WORKDIR /app/apps/${PROJECT}&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이부분은 이전 스테이지에서 번들화된 앱 및 해당 종속성을 복사하고 현재 작업 디렉토리를 &lt;b&gt;PROJECT&lt;/b&gt;로 지정된 대상 마이크로서비스의 폴더로 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 역방향 프로시 도구는 포트를 노출하는 정보에 의존하기 때문에 포트를 &lt;b&gt;EXPOSE&lt;/b&gt;하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 방법은 프로적션 이미지에&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt; NODE_ENV=production&lt;/b&gt; 을 설정하는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;다음 명령을 사용하여 최종 이미지를 빌드할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1715840010767&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker build -t api:latest --build-arg PROJECT=api .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;api&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;&lt;b&gt;결론(Conclusion)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi-stage Docker builds는 &lt;b&gt;Turborepo&lt;/b&gt;와 &lt;b&gt;PNPM&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;저장 공간 및 네트워크 비용을 줄이는 더 작은 Docker 이미지&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;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;b&gt;추가&lt;/b&gt;&lt;/h2&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;1. &lt;a href=&quot;https://nextjs.org/docs/advanced-features/output-file-tracing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Next.js의 standalone&lt;/a&gt; 사용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 기능을 사용하려면 먼저 &lt;b&gt;next.config.js&lt;/b&gt;에 다음과 같이 옵션을 적용시킵니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715842593849&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// next.config.js
module.exports = {
  output: 'standalone'
}&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;위의 번역글에서는 standalone 옵션을 사용하지 않아서 간단하게 /app 폴더를 전부 복사한것을 아래과 같이 변경하여 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715842896606&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;COPY --from=builder /app/apps/${PROJECT}/next.config.js .
COPY --from=builder /app/apps/${PROJECT}/package.json .

# Automatically leverage output traces to reduce image size
COPY --from=builder --chown=nextjs:nodejs /app/apps/${PROJECT}/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/${PROJECT}/.next/static ./apps/${PROJECT}/.next/static
COPY --from=builder --chown=nextjs:nodejs /app/apps/${PROJECT}/public ./apps/${PROJECT}/public&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;standalone에 포함되지 않는 next.config와 package.json은 따로 복사하여 주고 standalone 과 static. public 폴더를 복사하여 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 실행 명령어를 next.js 에 맞게 변경합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715843039309&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CMD [&quot;node&quot;, &quot;server.js&quot;]&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;2. .env 파일을 사용하여 여러가지 개발 환경 구성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;pruner &lt;/b&gt;이미지에서 전체 이미지를 COPY하고 turbo prune을 하기 전에 아래 내용을 추가해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715843224381&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Prune projects
FROM base AS pruner
ARG PROJECT
ARG ENV_FILE

WORKDIR /app
COPY . .
# ENV 환경에 맞게 세팅
RUN find . -name &quot;.env.*&quot; -exec rm {} \;
COPY apps/${PROJECT}/${ENV_FILE} apps/${PROJECT}
RUN mv apps/${PROJECT}/${ENV_FILE} apps/${PROJECT}/.env.production

RUN turbo prune --scope=${PROJECT} --docker&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;해당 설정은 프로젝트를 build했을때 production 환경으로만 빌드되는 것을 이용하여 build전에 사용자가 원하는 환경으로 build할수 있게 다른 .env 설정을 덮어 씌워 주는 역할을 합니다.&lt;/p&gt;
&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;최종 Dockerfile&lt;/b&gt;입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715843700994&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ARG NODE_VERSION=20.11.0

# Alpine image
FROM node:${NODE_VERSION}-alpine AS alpine
RUN apk update
RUN apk add --no-cache libc6-compat

# Alpine Base에서 pnpm과 turbo 설정
FROM alpine as base
RUN npm install pnpm turbo --global
RUN pnpm config set store-dir ~/.pnpm-store

# Prune projects
FROM base AS pruner
ARG PROJECT

WORKDIR /app
COPY . .
# ENV 환경에 맞게 세팅
RUN find . -name &quot;.env.*&quot; -exec rm {} \;
COPY apps/${PROJECT}/${ENV_FILE} apps/${PROJECT}
RUN mv apps/${PROJECT}/${ENV_FILE} apps/${PROJECT}/.env.production

RUN turbo prune --scope=${PROJECT} --docker

# 프로젝트 빌드
FROM base AS builder
ARG PROJECT

WORKDIR /app

# 프로젝트의 pnpm-lock.yaml 및 package.json 복사
COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=pruner /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml
COPY --from=pruner /app/out/json/ .

# 종속성 먼저 설치
RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm install --frozen-lockfile

# 프로젝트 소스 코드 복사
COPY --from=pruner /app/out/full/ .

RUN turbo build --filter=${PROJECT}
RUN --mount=type=cache,id=pnpm,target=~/.pnpm-store pnpm prune --prod --no-optional
RUN rm -rf ./**/*/src

# Final image
FROM alpine AS runner
ARG PROJECT

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs
USER nodejs

WORKDIR /app

COPY --from=builder /app/apps/${PROJECT}/next.config.mjs .
COPY --from=builder /app/apps/${PROJECT}/package.json .

# Automatically leverage output traces to reduce image size
COPY --from=builder --chown=nextjs:nodejs /app/apps/${PROJECT}/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/${PROJECT}/.next/static ./apps/${PROJECT}/.next/static
COPY --from=builder --chown=nextjs:nodejs /app/apps/${PROJECT}/public ./apps/${PROJECT}/public

WORKDIR /app/apps/${PROJECT}

ARG PORT=3000
ENV PORT=${PORT}
ARG NODE_ENV
ENV NODE_ENV=${NODE_ENV}
EXPOSE ${PORT}

CMD [&quot;node&quot;, &quot;server.js&quot;]&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Programming</category>
      <category>doccker</category>
      <category>monorepo</category>
      <category>nodeJS</category>
      <category>pnpm</category>
      <category>turborepo</category>
      <category>도커</category>
      <category>모노레포</category>
      <category>터보레포</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/40</guid>
      <comments>https://mishka.tistory.com/40#entry40comment</comments>
      <pubDate>Thu, 16 May 2024 16:28:34 +0900</pubDate>
    </item>
    <item>
      <title>프론트엔드에서 타임존 다루기 (Day.js)</title>
      <link>https://mishka.tistory.com/39</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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;타임존(Timezone) ?&lt;/b&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;GMT? UTC ? 오프셋?&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;GMT&lt;/b&gt;는 &lt;b&gt;G&lt;/b&gt;reenwuch&lt;b&gt; M&lt;/b&gt;ean&lt;b&gt; T&lt;/b&gt;ime의 약자로 그리니치 평균시를 의미하는데 경도 0도에 위치한 영구 그리니치 천문대를 기준으로 하는 태양 시간을 의미한다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;UTC&lt;/b&gt;는 &lt;b&gt;C&lt;/b&gt;oordinated &lt;b&gt;U&lt;/b&gt;niversal &lt;b&gt;T&lt;/b&gt;ime/&lt;b&gt;U&lt;/b&gt;niversal &lt;b&gt;T&lt;/b&gt;ime &lt;b&gt;C&lt;/b&gt;oordinated 의 약자로 지구 자전 주기의 흐름이 늦어지고 있는 문제를 해결하기 위해 세슘 원자의 진동수에 기반한 국제 원자시 기준시간을 의미한다.&lt;/blockquote&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;b&gt;오프셋&lt;/b&gt;이라고 하는데 UTC+09:00 의 의미는 UTC기준시간보다 9시간이 빠르다는 것을 의미합니다. 이 오프셋을 기준시로 하여 국가별로 자신들이 사용하는 타임존에 고유한 이름을 부여합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 대한민국의 타임존은 &lt;b&gt;KST&lt;/b&gt;(&lt;b&gt;K&lt;/b&gt;orea &lt;b&gt;S&lt;/b&gt;tandard &lt;b&gt;T&lt;/b&gt;ime)라고 부릅니다. 오프셋과 타임존의 이름은 1:1로 매칭되는 것이 아닙니다. +09:00 오프셋의 경우 한국 뿐 아니라 일보, 인도네시아 같은 지역에서도 사용하고 있으므로 1:N의 관계입니다.&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;b&gt;타임존 다루기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발에서 타임존을 다룰때 고려해야하는 상황은 크게 2가지 입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;파싱(Parsing)&lt;/b&gt;: &lt;b&gt;클라이언트의 날짜 데이터&lt;/b&gt;(사용자의 날짜 입력 값)를 &lt;b&gt;타임존 데이터 형태&lt;/b&gt;로 &lt;b&gt;변환&lt;/b&gt;하는 상황&lt;br /&gt;&lt;b&gt;포매팅(Formatting)&lt;/b&gt;: &lt;b&gt;서버에서 저장된 날짜 데이터&lt;/b&gt;를 &lt;b&gt;사용자의 타임존&lt;/b&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;서버에 저장되는 데이터는 다양한 클라이언트 환경을 지원하기위해 저장되는 데이터가 타임존에 영향을 받지 않는 절대값이어야 합니다. 서버 환경에 따라 다르지만 데이터는 날짜 및 시간 정보가 동일한 오프셋(UTC)에 맞춰져 있거나 해당 클라이언트 환경의 타임존 정보까지 포함된 값이어야 합니다.&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;b&gt;자바스크립트에서 Date&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 날짜나 시간과 관련된 작업은 Date 객체를 사용합니다. Array나 Function과 같이 ECMAScript스펙에 정의 되어있는 네이티브 객체이며, 주로 C++과 같은 네이티브 코드로 구현되어 있다고 합니다. Java의 영향을 받아서인지 불변데이터(Immutable)가 아니라는 점, Month가 0으로 부터 시작하는 등의 안좋은 특징도 그대로 가지고 있다고 합니다. 자세한 내용은 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;API문서&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Date 객체는 내부적으로 유닉스 시간과 같은 절대값으로 시간 데이터를 관리합니다. 그러나 생성자 Date()나 함수 Parse()나 메소드들 getHour(), setHour()은 모두 클라이언트의 로컬타임존(브라우저가 실행되는 운영체제에 설정된 타임존)에 영향을 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 타임존이 서울로 설정된 기기에서 2024년 3월 25일 오전 11시 30분을 입력했고 이 입력값을 년도, 월, 일, 시&amp;nbsp; 분 단위로 각각의 숫자 형태의 2024, 2, 25, 11, 30으로 저장했다고 가정하고 생성자를 이용해서 Date 객체를 생성한다면&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;const time = new Date(2024, 2, 25, 11, 30)&lt;br /&gt;time.toString();&amp;nbsp; // Mon Mar 25 2024 11:30:00 GMT+0900 (한국 표준시)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 반환되는 값은 KST를 기준으로 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 로컬 타임존을 직접 변경하는 것은 어렵습니다. 오프셋을 이용하여 날짜를 계산할 수 있지만, 정확한 타임존 변경을 위해서는 상세한 데이터베이스가 필요합니다.(서머타임등의 계산이 필요) 또는 데이터베이스에 전체 타임존을 저장하고 있다가 Date객체에서 날짜나 시간데이터를 가져올 때마다 데이터베이스에서 해당 날짜와 타임존에 맞는 오프셋을 알아낸 다음, 연산을 통해 결과를 반환하는 과정을 거쳐야합니다. 또한 이를 검증하기 위한 테스트도 정확히 작성해야합니다. 이를 대체하기 위해 잘 만들어진 라이브러리들을 사용하기를 권장합니다.&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;b&gt;Day.js&lt;/b&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;a href=&quot;https://mishka.kr/21&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Moment.js&lt;/a&gt;의 관한 포스트를 작성하였었는데 이제는 &lt;a href=&quot;https://momentjs.com/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Deprecated&lt;/a&gt; 되었습니다 ㅠㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그로인에 새로운 라이브러리를 찾던중 Day.js 를 발견하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 사용하던 Moment.js와 거의 사용법이 유사하고 번들크기도 작으며&lt;span style=&quot;color: #212529; text-align: start; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt; immutable 하다는 장점도 가지고 있습니다.&lt;/span&gt;&lt;span style=&quot;color: #ececec; text-align: start;&quot;&gt;&lt;/span&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;pre id=&quot;code_1711294128368&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install dayjs
// or
yarn add dayjs&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;b&gt;현재 날짜 또는 특정날짜 시간 객체 생성&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1711294219576&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import dayjs from &quot;dayjs&quot;;

dayjs(); // 현재시간
dayjs(&quot;2024-03-25&quot;); // 2024-03-24 T00:00:00+09:00&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;b&gt;날짜 형태 지정 (Formatting)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1711294680607&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const date = dayjs(&quot;2023-12-01 10:30:25&quot;)

date.format(); // 2023-12-01T10:30:25+09:00
date.format(&quot;YY-MM-DD&quot;); // 23-12-01
date.format(&quot;DD/MM/YY&quot;); // 01/12/23
date.format(&quot;YYYY.MM.DD HH:mm:ss&quot;); // 2023.12.01 10:30:25&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;a href=&quot;https://day.js.org/docs/en/display/format&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;포맷 형식&lt;/a&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;날짜 더하기/빼기 (add/subtact)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1711294987720&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 년도 
dayjs().add(1, 'year');
dayjs().subtract(1, 'year');

// 월 
dayjs().add(1, 'month');
dayjs().subtract(1, 'month');

// 일
dayjs().add(1, 'day');
dayjs().subtract(1, 'day');

// 시간
dayjs().add(1, 'hour');
dayjs().subtract(1, 'hour');

// 분 
dayjs().add(1, 'minute');
dayjs().subtract(1, 'minute');

// 초
dayjs().add(1, 'second');
dayjs().subtract(1, 'second');&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;b&gt;타임스탬프 변환&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dayjs()에 타임스탬프 값을 넣고 format을 하여 날짜로 변화할수 있고, &lt;span style=&quot;background-color: #f8f8f8; color: #333333; text-align: left;&quot;&gt;unix()&lt;/span&gt; 함수를 사용하여 타임스탬프로 변환 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1711295162724&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dayjs(1548381600).format(); // 1970-01-19T07:06:21+09:00
dayjs('2019-01-25').unix(); // 1548381600&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study</category>
      <category>timezone</category>
      <category>UTC</category>
      <category>타임존</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/39</guid>
      <comments>https://mishka.tistory.com/39#entry39comment</comments>
      <pubDate>Mon, 25 Mar 2024 00:50:03 +0900</pubDate>
    </item>
    <item>
      <title>nvm을 사용하여 프로젝트별 node버전 자동설정하기</title>
      <link>https://mishka.tistory.com/38</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;nvm?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nvm이란 &lt;b&gt;N&lt;/b&gt;ode &lt;b&gt;V&lt;/b&gt;ersion &lt;b&gt;M&lt;/b&gt;anager로, Node.js의 버전을 관리하는 도구입니다. 회사에서 프로젝트를 하다보면 프로젝트별로 다른 Node 버전을 사용하고 있는 경우가 발생하는데 이럴때 사용할 버전을 쉽게 전환 할수 있도록 해줍니다.&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;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/nvm-sh/nvm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NVM문서&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1704349524305&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;homebrew 사용&lt;/p&gt;
&lt;pre id=&quot;code_1704349583817&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew install nvm&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 안정화된 노드 최신 버전도 설치해볼께요&lt;/p&gt;
&lt;pre id=&quot;code_1704349644671&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nvm install stable&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;b&gt;사용방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 버전의 node 설치&lt;/p&gt;
&lt;pre id=&quot;code_1704349752405&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nvm install [node version]

# 예시
nvm install v18&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;내컴퓨터에 설치된 node버전 확인&lt;/p&gt;
&lt;pre id=&quot;code_1704349800018&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nvm ls
# or
nvm 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;/p&gt;
&lt;pre id=&quot;code_1704349844908&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nvm use [node version]&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;b&gt;자동설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 기본 설정만을 해서 사용할경우 프로젝트별로 다른 node버전을 사용 할 경우가 생기는데 nvm은 &lt;b&gt;shell별&lt;/b&gt;로 다른 node버전을 가져가도록 설정이 되어있어서 터미널을 새로 열때 &lt;b&gt;default&lt;/b&gt;로 되어 있는 node 버전이 선택된 상태로 실행이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 매번 nvm use 명령어를 사용해서 node버전을 변경 해줘야 합니다. 이러한 과정들을 자동설정을 통해서 줄여보겠습니다.&lt;/p&gt;
&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;.nvmrc&lt;/b&gt; 파일을 생성해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당파일에 사용할 node 버전을 적어줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1704350288770&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; v18.19.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설정을 하게 되면 &lt;b&gt;nvm use&lt;/b&gt; 명령어만으로 node version없이 해당 버전이 적용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 해당 node 버전을 일일히 기억하지 않아도 되서 편하지만 번거로운 것은 사실입니다. nvm use 명령어를 매번 입력해 주어야 하기 때문입니다. 많은 분들도 이러한 것에 불편함을 느끼셨는지 shell에 설정을 추가하여 사용할수 있도록 하는 방법이 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자 사용하는 shell에 맞춰서 아래 가이드대로 실행하시면됩니다.&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;bash&lt;/b&gt;를 사용하는 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;vi ~/.bachrc&lt;/b&gt; 를 실행하여 설정파일을 열고 아래의 내용을 입력후에 저장해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1704350940585&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cdnvm() {
    command cd &quot;$@&quot; || return $?
    nvm_path=&quot;$(nvm_find_up .nvmrc | command tr -d '\n')&quot;

    # If there are no .nvmrc file, use the default nvm version
    if [[ ! $nvm_path = *[^[:space:]]* ]]; then

        declare default_version
        default_version=&quot;$(nvm version default)&quot;

        # If there is no default version, set it to `node`
        # This will use the latest version on your machine
        if [ $default_version = 'N/A' ]; then
            nvm alias default node
            default_version=$(nvm version default)
        fi

        # If the current version is not the default version, set it to use the default version
        if [ &quot;$(nvm current)&quot; != &quot;${default_version}&quot; ]; then
            nvm use default
        fi
    elif [[ -s &quot;${nvm_path}/.nvmrc&quot; &amp;amp;&amp;amp; -r &quot;${nvm_path}/.nvmrc&quot; ]]; then
        declare nvm_version
        nvm_version=$(&amp;lt;&quot;${nvm_path}&quot;/.nvmrc)

        declare locally_resolved_nvm_version
        # `nvm ls` will check all locally-available versions
        # If there are multiple matching versions, take the latest one
        # Remove the `-&amp;gt;` and `*` characters and spaces
        # `locally_resolved_nvm_version` will be `N/A` if no local versions are found
        locally_resolved_nvm_version=$(nvm ls --no-colors &quot;${nvm_version}&quot; | command tail -1 | command tr -d '\-&amp;gt;*' | command tr -d '[:space:]')

        # If it is not already installed, install it
        # `nvm install` will implicitly use the newly-installed version
        if [ &quot;${locally_resolved_nvm_version}&quot; = 'N/A' ]; then
            nvm install &quot;${nvm_version}&quot;;
        elif [ &quot;$(nvm current)&quot; != &quot;${locally_resolved_nvm_version}&quot; ]; then
            nvm use &quot;${nvm_version}&quot;;
        fi
    fi
}

alias cd='cdnvm'
cdnvm &quot;$PWD&quot; || exit&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;zsh&lt;/b&gt;를 사용하는 경우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vi ~/.zshrc 를 실행하여 설정파일을 열고 아래의 내용을 입력후에 저장해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1704351024054&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# place this after nvm initialization!
autoload -U add-zsh-hook

load-nvmrc() {
  local nvmrc_path
  nvmrc_path=&quot;$(nvm_find_nvmrc)&quot;

  if [ -n &quot;$nvmrc_path&quot; ]; then
    local nvmrc_node_version
    nvmrc_node_version=$(nvm version &quot;$(cat &quot;${nvmrc_path}&quot;)&quot;)

    if [ &quot;$nvmrc_node_version&quot; = &quot;N/A&quot; ]; then
      nvm install
    elif [ &quot;$nvmrc_node_version&quot; != &quot;$(nvm version)&quot; ]; then
      nvm use
    fi
  elif [ -n &quot;$(PWD=$OLDPWD nvm_find_nvmrc)&quot; ] &amp;amp;&amp;amp; [ &quot;$(nvm version)&quot; != &quot;$(nvm version default)&quot; ]; then
    echo &quot;Reverting to nvm default version&quot;
    nvm use default
  fi
}

add-zsh-hook chpwd load-nvmrc
load-nvmrc&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;위와 같이 설정을 해주면 프로젝트로 이동했을때 nvm use 명령어를 실행하지 않아도 자동으로 &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;</description>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/38</guid>
      <comments>https://mishka.tistory.com/38#entry38comment</comments>
      <pubDate>Thu, 4 Jan 2024 16:03:00 +0900</pubDate>
    </item>
    <item>
      <title>프론트엔드 개발자도 알면 좋은 Docker</title>
      <link>https://mishka.tistory.com/37</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;도커 (Docker)&lt;/b&gt;&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;b&gt;가상화 플랫폼&lt;/b&gt;입니다. 도커는 2013년 3월 산타클라라에서 열린 Pycon Conference에서 Solomon Hykes가 발표한 &lt;a href=&quot;https://www.youtube.com/watch?v=wW9CAH9nSLs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;The future of Linux Containers&lt;/a&gt;라는 세션에서 처음 세상에 알려지게 되었고, 고(go)언어로 개발되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 기반이라고 했는데 무엇일까요?&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;컨테이너 (Container)&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드 (1).jpeg&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crDRdF/btsoz9fbIQa/vaWAuEcLjD3MK3qPyeiEZ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crDRdF/btsoz9fbIQa/vaWAuEcLjD3MK3qPyeiEZ0/img.jpg&quot; data-alt=&quot;컨테이너&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crDRdF/btsoz9fbIQa/vaWAuEcLjD3MK3qPyeiEZ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrDRdF%2Fbtsoz9fbIQa%2FvaWAuEcLjD3MK3qPyeiEZ0%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;403&quot; height=&quot;226&quot; data-filename=&quot;다운로드 (1).jpeg&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&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;오늘날 컨테이너라고 하면 주로 선박 운송용 컨테이너를 지칭합니다. 선박 운송시 모든 선적물은 거대한 상자모양의 &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-filename=&quot;docker.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;389&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhEgii/btsoyVBlS7n/hHlSHSotAefHyniFJlHb9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhEgii/btsoyVBlS7n/hHlSHSotAefHyniFJlHb9K/img.png&quot; data-alt=&quot;도커 컨테이너&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhEgii/btsoyVBlS7n/hHlSHSotAefHyniFJlHb9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhEgii%2FbtsoyVBlS7n%2FhHlSHSotAefHyniFJlHb9K%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;423&quot; height=&quot;329&quot; data-filename=&quot;docker.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;389&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;도커의 컨테이너도 이와 같은 컨셉을 가지고 있습니다. 도커는 서비스를 운용하는데 필요한 실행환경, 라이브러리, 소프트웨어, 시스템 도구, 코드 등을 &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;실제 컨테이너의 특성처럼 서비스 관리자는 도커라이징(Dockerizing)된 컨테이너가 어떤 런타임을 필요로 하는지, 어떤 라이브러리와 코드를 필요로 하는지 전혀 알 필요가 없고, 그저 컨테이너를 어딘가에서 가져와서 서비스를 운영한 컴퓨팅 환경에서 실행하기만 하면 됩니다. 실행된 서비스는 컴퓨팅 환경과 &lt;b&gt;독립된 가상의 환경&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;도커 이미지(Docker Image)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 이미지는 컨테이너 실행에 필요한 파일과 설정값등을 포함하고 있는 것으로 상태값을 가지지 않고 변하지 않습니다(Immutable).&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;쉽게 설명하면 도커가 찍어놓은(빌드한) 사진(Image)을 인화(실행)하면 컨테이너가 나오는 것입니다. 사진은 여러번 인화 할 수 있고, 똑같은 사진이 인화 되는 것처럼 다른 서버에서 도커가 이미지를 사용해 컨테이너를 만들면 똑같은 환경을 구축 할 수 있습니다.&lt;/p&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 File)로 만들 수 있습니다. 도커 파일은 도커가 어떻게 이미지를 만들지 이해하도록 적은 파일 입니다. 도커 파일에 Image를 어떻게 빌드할지 적어놓으면 도커가 그것을 읽고 이미지를 생성하게 됩니다(Dockerizing).&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;b&gt;Docker로 애플리케이션 배포하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 Homebrew를 사용해서 Docker를 설치 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--cask 옵션을 사용해서 설치를 해주면 Docker Desktop on Mac을 설치하고 docker-compose, docker-machine도 같이 설치해주어 편하게 사용할수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: &lt;a href=&quot;https://velog.io/@jaryeonge/Docker-Mac%EC%97%90-Homebrew%EB%A1%9C-docker-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker - Mac에 Homebrew로 docker 설치&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1690175329016&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 설치
brew install --cask docker

// 버전확인
docker --version
docker-compose --version&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Dockerfile&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커파일은 컨테이너 이미지는 만드는데 사용되는 단순한 &lt;b&gt;텍스트 기반 지침 스크립트&lt;/b&gt;입니다. 이미지를 생성하기 위한 컨테이너에 설치해야하는 패키지, 추가해야하는 소스코드, 실행해야 하는 명령어와 셸 스크립트등 을 Dockerfile에 기록해줍니다. 도커는 Dockerfile을 읽어 컨테이너에서 작업을 수행한 뒤 이미지로 만들어냅니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Dockerfile 작성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root위치에 Dockerfile이라는 이름을 파일을 만들어줍니다. 만약 VsCode를 사용하신다면 Docker extension을 설치하여 자동완성 기능을 사용할 수도 있습니다. Docker 파일은 &lt;b&gt;한 줄이 하나의 명령어&lt;/b&gt;가 됩니다(Layer 형식). 명령어는 소문자로 표기해도 상관없지만 &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;간단한 예시파일을 통해 명령어들을 살펴보겠습니다. (아래 예시들은 next.js, yarn 환경입니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1690178259185&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM node:16

WORKDIR /app

COPY . .

RUN yarn install
RUN yarn build

EXPOSE 3000

CMD [&quot;yarn&quot;, &quot;start&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Dockerfile 기본 명령어&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;FROM&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1690178362336&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM &amp;lt;image&amp;gt;:&amp;lt;tag&amp;gt;
FROM node:16&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;베이스 이미지를 지정합니다. 반드지 지정해야 하며 어떤 이미지도 베이스 이미지가 될 수 있습니다. tag는 가능하면 lastest보다 구체적인 버전을 지정하는 것이 좋습니다.&lt;/p&gt;
&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;WORKDIR&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1690178935161&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WORKDIR &amp;lt;path&amp;gt;
WORKDIR /app&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RUN, CMD, ADD, COPY등이 이루어질 기본 디렉토리를 설정합니다. 각 명령어의 현재 디렉토리는 한 줄 한 줄 마다 초기화 되기 때문에 RUN cd /parh 를 하더라고 다음 줄에서는 위치가 초기화 됩니다. 같은 디렉토리에서 작업하기위해 &lt;b&gt;WORKDIR&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;COPY&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1690178985270&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;COPY &amp;lt;src&amp;gt;... &amp;lt;dest&amp;gt;
COPY . .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일이나 디렉토리를 이미지로 복사합니다. 일반적으로 소스를 복사하는데 사용합니다. target 디렉토리가 없다면 자동으로 생성합니다.&lt;/p&gt;
&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;RUN&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1690179063253&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;RUN &amp;lt;command&amp;gt;
RUN [&quot;executable&quot;. &quot;param1&quot;, &quot;param2&quot;]
RUN yarn install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어를 그대로 실행합니다. 내부적으로 &lt;b&gt;/bin/sh -c&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;EXPOSE&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1690179222814&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EXPOSE &amp;lt;port&amp;gt; [&amp;lt;port&amp;gt;...]
EXPOSE 3000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 컨테이너가 실행되었을 때 요청을 기다리고 있는(Listen) 포트를 지정합니다. 여러개의 포트를 지정할 수 있습니다.&lt;/p&gt;
&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;CMD&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1690179402931&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CMD [&quot;executable&quot;, &quot;param1&quot;, &quot;param2&quot;]
CMD command param1 param2
CMD [&quot;yarn&quot;, &quot;start&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 컨테이너가 실행되었을때 실행되는 명령어를 정의합니다. 빌드할 때는 실행되지 않으며 여러개의 CMD 가 존재할 경우 가장 마지막 CMD만 실행됩니다. 한꺼번에 여러 개의 프로그램을 실행하고 싶은 경우 run.sh 파일을 작성하여 데몬을 실행하거나 별도의 프로그램을 사용해야 합니다.&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;b&gt;이미지 생성(&lt;b&gt;Dockerfile 빌드&lt;/b&gt;)&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1690179745692&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker build &amp;lt;option&amp;gt; .
docker build -t &amp;lt;태그명&amp;gt; .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-t : 생성될 이미지의 이름을 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. : build 명령어 끝에는 Dockerfile이 저장된 경로를 입력합니다.&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;b&gt;이미지로 컨테이너 실행&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1690186186688&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run &amp;lt;옵션&amp;gt; &amp;lt;이미지명:태그&amp;gt; &amp;lt;명령어&amp;gt; &amp;lt;인자&amp;gt;
docker run -d --name &amp;lt;컨테이너명&amp;gt; -p &amp;lt;호스트포트&amp;gt;:&amp;lt;컨테이너포트&amp;gt; &amp;lt;이미지명:태그&amp;gt; 
docker run -d --name dockerContainer -p 3000:3000 nextjs-docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-d : 컨테이너를 백그라운드에서 실행합니다(Detached Mode).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--name &amp;lt;컨테이너명&amp;gt; : 컨테이너의 이름을 설정 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-p &amp;lt;호스트포트&amp;gt;:&amp;lt;컨테이너포트&amp;gt; : 호스트포트와 컨테이너 내부의 포트를 바인드 합니다(포트포워딩).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(컨테이너는 호스트 환경과 격리된 파일 시스템과 네트워크를 가지기 때문에 호스트에서 컨테이너로 접근 가능하도록 포트포워딩을 시켜줘야 합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: &lt;a href=&quot;https://velog.io/@oneook/Docker%EB%A1%9C-React-%EA%B0%9C%EB%B0%9C-%EB%B0%8F-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0?utm_source=oneoneone&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프론트엔드 개발자를 위한 Docker로 React 개발 및 배포하기&lt;/a&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;b&gt;Docker Multi stage&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Multi stage란? 컨테이너 이미지를 만들면서 빌드 등에는 필요하지만 최종 컨테이너 이미지에는 필요없는 환경을 제거할 수 있도록 &lt;b&gt;단계를 나누어서 기반 이미지는 만드는 방법&lt;/b&gt;입니다. 하나의 Dockerfile안에 &lt;b&gt;여러개의 FROM 이미지&lt;/b&gt;를 정의하여 최종 생성될 &lt;b&gt;이미지의 크기를 줄일수 있습니다.&lt;/b&gt; (17.05 버전 이상)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi state 방식이 나오기 전에는 하나의 Dockerfile이 아닌 두 가지의 Dockerfile을 유지 하는 builder-patten이나 여러 명령어를 실행하는데 분리하지 않고 &amp;amp;&amp;amp; \ 를 통해 하나의 Layer에서 처리 하는 방식등을 사용하였다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예제를 통해 살펴보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1690189063628&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM node:16-alpine AS builder
WORKDIR /app

COPY . .
RUN yarn install
RUN yarn build

FROM node:16-alpine AS runner
WORKDIR /app

COPY --from=builder /app ./

EXPOSE 3000
CMD [&quot;yarn&quot;, &quot;start&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 Dockerfile과 다르게 2개의 FROM을 통해 2개의 이미지가 명시되어 있습니다. 두번째 FROM아래에서 사용된 COPY 명령어는첫 번째 FROM에서 사용된 이미지의 최종 상태에 존재하는 /app 의 파일을 복사해서 사용합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;COPY --from의 역할&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;COPY&lt;/b&gt; instruction에서 &lt;b&gt;--from&lt;/b&gt;옵션을 사용하면 이전 스테이지에서 Build를 통해 생성된 결과만을 복사할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;AS &amp;lt;Alias-Name&amp;gt;의 역할&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 빌드 스테이지의 &lt;b&gt;별칭(alias)&lt;/b&gt;을 정할 수 있습니다. &lt;b&gt;FROM&lt;/b&gt; instruction 레이어에서 사용 가능하고, &lt;b&gt;COPY&lt;/b&gt;의 &lt;b&gt;--from=&amp;lt;name&amp;gt;&lt;/b&gt;옵션으로 참조합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yozm.wishket.com/magazine/detail/732/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;도커(Docker)란 무엇이고, 왜 사용하나요?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hudi.blog/about-docker/?fbclid=IwAR2BDz4QPdTsslq9uv9Mv48eIbdBCyXRA2B28T-ClGwi-Hrfy9wSjhXNqHk&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이론과 실습을 통해 이해하는 Docker 기초&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;초보를 위한 도커 안내서 - 도커란 무엇인가?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@jaryeonge/Docker-Mac%EC%97%90-Homebrew%EB%A1%9C-docker-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker - Mac에 Homebrew로 docker 설치&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://code-masterjung.tistory.com/133&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Docker] 도커 입문하기 4 -도커 이미지만들기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://e-juhee.tistory.com/entry/Dockerfile?category=1095271&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Docker] Dockerfile로 이미지 생성하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://subicura.com/2017/02/10/docker-guide-for-beginners-create-image-and-deploy.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;초보를 위한 도커 안내서 -이미지 만들고 배포하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gptjs409.github.io/infra/2019/10/23/image-build.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Docker] Dockerfile로 이미지 빌드하고 컨테이너 띄워보기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nesoy.github.io/articles/2020-11/Docker-multi-stage-build&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker Multi Stage란?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://e-juhee.tistory.com/entry/docker-multi-stage-build?category=1095271&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Docker] 멀티 스테이지 빌드로 이미지 크기 줄이기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>container</category>
      <category>docker</category>
      <category>image</category>
      <category>Multi-stage</category>
      <category>도커이미지</category>
      <category>도커파일</category>
      <category>컨테이너</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/37</guid>
      <comments>https://mishka.tistory.com/37#entry37comment</comments>
      <pubDate>Tue, 25 Jul 2023 13:47:43 +0900</pubDate>
    </item>
    <item>
      <title>Zod를 사용한 유효성 검증</title>
      <link>https://mishka.tistory.com/36</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Zod는 TypeScript를 주로 사용하는 스키마 선언 및 유효성 검사 라이브러리 중 하나입니다. 비슷한 걸로는 &lt;a href=&quot;https://www.npmjs.com/package/yup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Yup&lt;/a&gt;, &lt;a href=&quot;https://joi.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Joi&lt;/a&gt;&amp;nbsp;등의 라이브러리가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Zod에서 &quot;Schema(스키마)&quot; 라고 하는 용어는 단순한 문자열부터 복잡하게 중청된 객체까지 모든 데이터 유형을 포괄적으로 나타내기 위해 사용됩니다.&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;pre id=&quot;code_1687180059407&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ yarn add zod&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Zod 패키지에서 z 를 불러와 사용 할 수 있으며, z 하나로 Zod의 모든 기능을 활용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687180146512&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { z } from &quot;zod&quot;&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;b&gt;스키마 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Zod를 사용하기 위해서는 먼저 스키마를 정의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이름, 나이, 나이공개여부로 이루어진 사용자 객체를 나타내는 스키마를 만들어보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687180487833&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const User = z.object({
  name: z.string(),
  age: z.number(),
  open: z.boolean()
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;z.object()를 사용해서 User 스키마가 객체의 형태이고 z.string()으로 name의 속성을 문자열, z.number()를 사용해서 age 속성을 숫자, open의 속성은 z.boolean()을 사용해서 불리언 형태라고 나타낼 수 있습니다.&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;p data-ke-size=&quot;size16&quot;&gt;스키마를 활용해서 다양한 작업을 할 수 있는데 유효성 검증을 먼저 해보겠습니다. 스키마의 parde() 함수를 사용해서 검증하고 싶은 값을 넘겨서 호출 하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687180814233&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;User.parse({
  name: &quot;홍길동&quot;,
  age: 20,
  open: true
}) // 유효성 검증 통과

User.parse({
  email: &quot;홍길동&quot;,
  age: &quot;20&quot;
}) // 유효성 검증 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증이 실패하게되면 parse() 함수는 아래와 같이 error를 발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687180924772&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// console

ZodError: [
  {
    code: &quot;invalid_type&quot;,
    expected: &quot;number&quot;,
    received: &quot;string&quot;,
    path: [&quot;age&quot;],
    message: &quot;Expected number, received string&quot;
  },
  {
    code: &quot;invalid_type&quot;,
    expected: &quot;boolean&quot;,
    received: &quot;undefined&quot;,
    path: [&quot;open&quot;],
    message: &quot;Required&quot;
  },
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 검증이 성공했을 경우 parse() 함수가 반환하는 객체에는 검증이 통과한 속성만 포함됩니다. 스키마에 정의 되지 않은 속성이 입력 객체에 포함된 경우 parse() 함수가 반환한 결과 객체에는 해당 속성이 제외되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 parse() 함수의 반환 타입이 정의된 스키마에 의해서 결정되기 때문에 일어나는 현상입니다.&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;p data-ke-size=&quot;size16&quot;&gt;Zod는 정의한 스키마를 기준으로 TypeScript 타입을 추론 할 수도 있습니다. 이 기능을 잘 활용하면 별도의 타입을 작성할 필요없이 스키마를 활용해서 타입추론이 가능해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 User를 TypeScript로 작성하려면 아래와 같이 타입을 작성해줘야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687181335138&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface User {
  name: string
  age: number
  open: boolean
}

function processUser(user: User) {
  User.parse(user) // 유효성 검증
  // 로직
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Zod의 infer 기능을 활용하면 스키마에서 타입을 가져올 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687181449631&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type User = z.infer&amp;lt;typeof User&amp;gt;

function process(user: User) {
  User.parse(user) // 유효성검증
  // 로직
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 작성한 타입과 스키마가 항상 일치하도록 관리 하는 것은 여간 어려운 일이 아닙니다. infer를 활용하면 항상 스키마와 타입을 일치하게 관리 할 수 있습니다.&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;p data-ke-size=&quot;size16&quot;&gt;Zod 스키마에 포함된 모든 속성을 필수 입력입니다. 이를 선택입력으로 바꾸려면 optional() 검증자를 사용하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687181874635&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const User = z.object({
  name: z.string(),
  age: z.number(),
  open: z.boolean().optional()
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 스키마를 타입추론을 해보면 open속성 뒤에 ? 를 붙이고 타입을 boolean 또는 undifined로 선언한 것과 마찬가지로 표현됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687182048806&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type User = z.infer&amp;lt;typeof User&amp;gt;
// { name:string; age:number; open?: boolean | undefined; }&lt;/code&gt;&lt;/pre&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;유효성 검증에서 값이 누락되어 있는 속성에 기본값을 주고 싶을때는 default() 검증자를 사용하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687182184443&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const User = z.object({
  name: z.string(),
  age: z.number(),
  open: z.boolean().default(false)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 작성하면 open 속성이 없으면 false로 설정할수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Referense&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://zod.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://zod.dev/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/zod/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.daleseo.com/zod/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/zod-schema/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.daleseo.com/zod-schema/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>Schema</category>
      <category>Zod</category>
      <category>스키마</category>
      <category>유효성</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/36</guid>
      <comments>https://mishka.tistory.com/36#entry36comment</comments>
      <pubDate>Mon, 19 Jun 2023 22:57:09 +0900</pubDate>
    </item>
    <item>
      <title>프론트엔드에서 아키텍처 바라보기</title>
      <link>https://mishka.tistory.com/35</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;일단 프론트엔드에서 바라보는 아키텍처를 논하기 전에 우선적으로 아키텍처 자체에 대해 한번 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;영어 단어로 아키텍처는 &lt;br /&gt;&lt;b&gt;&quot;건축학&quot;&lt;/b&gt; &lt;br /&gt;이라는 뜻입니다.&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;br /&gt;Wiki에서는 시스템 아키텍처를&lt;/span&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&quot;시스템 목적을 달성하기 위해 &lt;br /&gt;시스템의 상호작용들의 &lt;br /&gt;시스템디자인에 대한 제약 및 설계이다.&quot;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;라고 정의하고 있습니다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&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;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 data-ke-size=&quot;size16&quot;&gt;위와 같은 문제를 해결하기 위해서는 잘 설계된 프로그램을 만들어야 하는데 잘 설계 되었다는 건 &lt;b&gt;'한 번에 한 부분을 제대로 집중할 수 있게 프로그램을 구성하는 것'&lt;/b&gt; 그리고 &lt;b&gt;'간단하고 이해하기 쉽게 구성하는 것'&lt;/b&gt;을 의미 합니다. 즉 &lt;b&gt;관심사를 잘 분리하고 코드를 잘 이해할 수 있게 만들어야 합니다.&lt;/b&gt; 이렇게 프로그램을 설계하는데 '아키텍처'라는 방법을 사용합니다. 그렇기 때문에 아키텍처는 프로그램을 잘 설계해서 만들고 유지하는데 투입하는 비용을 최소화 하는 목표를 갖습니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; 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;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;좋은 아키텍처는 세부사항을 정책으로부터 신중하게 가려내고, 둘이 결합되지 않도록 엄격하게 분리한다.&lt;br /&gt;좋은 아키텍처는 의존성의 방향이 컴포넌트 수준을 기반으로 연결되도록 만들어야 한다.&lt;br /&gt;좋은 아키텍트는 결정되지 않은 사항의 수를 최대화한다(향후 시스템에 변경이 필요할 때 어떤 방향으로든 쉽게 변경할 수 있도록 한다.)&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프론트엔드 아키텍처의 히스토리&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MVC&amp;nbsp; 아키텍처&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Model&lt;/b&gt; + &lt;b&gt;View&lt;/b&gt; + &lt;b&gt;Controller &lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Model&lt;/b&gt;: 화면에 보여줄 데이터를 담당하는 영역. Model의 정의는 주어진 환경에 따라서 다를수 있습니다. (Javascript의 Object, 서버의 DB, 서버에서 API로 요청한 데이터 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;View&lt;/b&gt;: 실제 사용자에게 보여지는 화면 (HTML + CSS 로 만들어지는 결과물)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Controller&lt;/b&gt;: Model의 데이터를 받아서 화면에 그리고, 화면으로부터 사용자의 동작을 받아서 Model을 변경, Model과 View사이의 중간 역할을 하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 MVC를 나눈 이유는&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. 화면을 다루는 문제와 데이터를 다루는 문제의 성격이 달라서 분리&lt;br /&gt;2. Model과 View 간의 의존 관계를 최소화 해서 화면의 수정이 데이터 수정에 영향을 미치지 않고 데이터 수정이 화면의 수정에 영향을 미치지 않으려고&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시대에 따라 MVC의 개념은 조금씩 바껴왔는데 &lt;b&gt;웹서비스 초창기&lt;/b&gt;에는&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;데이터베이스를 Model&lt;br /&gt;HTML, CSS, Javascript까지 포함한 클라이언트 영역을 View&lt;br /&gt;가운데서 라우터를 통해 데이터를 처리하고 새로운 HTML을 만들어서 보여주는 백엔드 영역을 Controller&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;jQuery 시절의 MVC&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;ajax로 부터 받는 데이터를 Model&lt;br /&gt;HTML과 CSS로 만들어지는 화면을 View&lt;br /&gt;Javascript가 중간에서 서버의 데이터를 받아서 화면을 바꾸고 이벤트를 처리해서 서버에 데이터를 전달하는 Controller&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시의 가장 중요한 패러다임은 관점의 분리로 Model과 View의 종속성을 최대한 분리하는 원칙으로 HTML과 jQuery를 따로 관리하는 것이었습니다.&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;MVVM 아키텍처&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jQuery로 작업을 하다보니 불편한 점을 발견하게 되는데&amp;nbsp; 데이터를 수정하고 이벤트를 연결하고 수정하는 과정이 계속 반복된다는 것입니다. 서버에서 개발을 할 때에는 HTML이 전체적으로 렌더링되다보니 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;{{ }}, &amp;lt;%= %&amp;gt;&lt;/span&gt; 와 같은 치환자의 사용이 가능한 반면 jQuery는 수정해야 할 부분을 일일히 찾아서 수정을 해줘야 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 불편함을 해소하기 위해 &lt;b&gt;Angular&lt;/b&gt;가 등장합니다. 앵귤러에서는 &lt;b&gt;템플릿&lt;/b&gt;과 &lt;b&gt;바인딩&lt;/b&gt;이라하는 중요한 개념들이 등장하였고 이후로 웹 개발하는 방식의 패러다임이 바뀌게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Model이 변하면 View를 수정하고 View에서 이벤트를 받아서 Model을 변경하다는 Controller의 역할은 그대로인데 이를 구현하는 방식이 jQuery와 같은 DOM 조작에서 &lt;b&gt;템플릿&lt;/b&gt;과 &lt;b&gt;바인딩&lt;/b&gt;을 통한 &lt;b&gt;선언적인 방법&lt;/b&gt;으로 변하게 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 DOM을 조작하는 코드가 사라지고 이 기능들은 프레임워크가 담당하게 됩니다. 이제 개발자는 화면에 그려져야할 데이터만 만들어서 프레임워크에 전달해주면 프레임워크가 알아서 그려줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 View를 그리는 Model만 다루게 되었다는 의미로 &lt;b&gt;ViewModel&lt;/b&gt;이라고 부르며 이 방식을 &lt;b&gt;MVVM&lt;/b&gt;이라고 부르게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 등장한 프레임워크인 React, Vue, Angular2, Svelte등 어떤 방식의 템플릿과 바인딩 문법을 쓰느냐 방식만 다를뿐 MVVM 아키텍처는 그대로 유지됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;MVC에 MVVM으로 오면서 달라진 부분&lt;br /&gt;- 컨트롤러의 반복적인 기능이 선언적인 방식으로 개선&lt;br /&gt;- Model과 View의 관점을 분리하려 하지 않고 하나의 템플릿으로 관리하려는 방식을 발전(기존에는 class나 id등으로 간접적으로 HTML에 접근하려고 했다면 이제는 직접적으로 HTML에 접근하는 방법으로 확장)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Component 그리고 Container-Presenter 패턴&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM은 웹의 DOM API를 알지 못하더라고 비즈니스 로직에만 집중하면 서비스를 만들어 줄 수 있게 해주었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 Page 단위가 아니라 Page안에 여러가지 모듈이 있고 Model이나 여러 화면들이 하나의 화면에서 구성 될 수 있도록 발전하게 됩니다. 그래서 MVVM이 화면단위가 아니라 조금 더 작게 재사용 할 수 있는 단위로 만들어서 조립하는 방식으로 발전하게 됩니다.&lt;/p&gt;
&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;Component 패턴&lt;/b&gt;입니다. 하지만 컴포넌트 패턴에 비즈니스 로직이 들어가게 되면 컴포넌트의 재사용성은 떨어지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;비즈니스 로직을 포함하고 있는 컴포넌트를 container&lt;/b&gt; &lt;b&gt;컴포넌트,&lt;/b&gt; &lt;b&gt;데이터만 뿌려주는 형태의 컴포넌트를 Presenter&lt;/b&gt; &lt;b&gt;컴포넌트&lt;/b&gt;로 &lt;b&gt;분리&lt;/b&gt;하여 최상단 혹은 1depth에 Container를 두고 비즈니스 로직을 관리하는 &lt;b&gt;Container-Presenter 아키텍처&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;857&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btOOGo/btr9n6jDVld/rFao2vmQW58PflKH0Jm9m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btOOGo/btr9n6jDVld/rFao2vmQW58PflKH0Jm9m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btOOGo/btr9n6jDVld/rFao2vmQW58PflKH0Jm9m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtOOGo%2Fbtr9n6jDVld%2FrFao2vmQW58PflKH0Jm9m1%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;857&quot; height=&quot;396&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들게 되었을때 컴포넌트 구조가 복잡해짐에 따라 하위 컴포넌트에 특정 값을 전달하기 위해서 중간레벨에 있는 컴포넌트들이 전부 그 props를 가지고 있어야 하는 &lt;b&gt;Props Drilling&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;718&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFzCsk/btr9mSGaXZW/WEYd4R7d3WchRQXyhv31H1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFzCsk/btr9mSGaXZW/WEYd4R7d3WchRQXyhv31H1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFzCsk/btr9mSGaXZW/WEYd4R7d3WchRQXyhv31H1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFzCsk%2Fbtr9mSGaXZW%2FWEYd4R7d3WchRQXyhv31H1%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;860&quot; height=&quot;406&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;FLUX 패턴과 Redux&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FLUX 패턴은 MVC 패턴에서 벗어나 &lt;b&gt;단방향 아키텍처&lt;/b&gt;(uni-directional architecture)를 만들자는 아이디어에서 시작합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;691&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHSKCL/btr9n7phJCz/JfgadZPuyDsnwDhG2yWNEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHSKCL/btr9n7phJCz/JfgadZPuyDsnwDhG2yWNEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHSKCL/btr9n7phJCz/JfgadZPuyDsnwDhG2yWNEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHSKCL%2Fbtr9n7phJCz%2FJfgadZPuyDsnwDhG2yWNEK%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;691&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;691&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC 패턴에서 애플리케이션의 규모가 커져서 위와 같은 구조를 가지게 되었을때 View가 다양한 상호작용을 위해 여러 개의 Model을 동시에 업데이트 하고 Model 역시 여러개의 View에 데이터를 전달하는 상황이 발생합니다. 한 Model이 업데이트 되면 그에 따라 View가 업데이트되고, 업데이트된 View가 또 다른 Model을 업데이트 하는 식의 복잡한 데이터 흐름을 가지게 됩니다. 이렇게 많은 의존성을 가지게 되면 Model의 개수가 많아질수록 각 Model에서 발생한 이벤트가 애플리케이션 전체로 퍼져나갈때 이를 예측하기 힘들어 집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 페이스북에서 이를 해결하기 위해 &lt;b&gt;Flux 패턴&lt;/b&gt;을 제안합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flux는 사용자 입력을 기반으로 &lt;b&gt;Action&lt;/b&gt;을 만들고 Action을 &lt;b&gt;Dispatcher&lt;/b&gt;에 전달하여 &lt;b&gt;Store(Model)&lt;/b&gt;의 데이터를 변경한 뒤 &lt;b&gt;View&lt;/b&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;1280&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XNQQy/btsaKxHgmXQ/7R8L2eBIDbsXG3xjndvbyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XNQQy/btsaKxHgmXQ/7R8L2eBIDbsXG3xjndvbyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XNQQy/btsaKxHgmXQ/7R8L2eBIDbsXG3xjndvbyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXNQQy%2FbtsaKxHgmXQ%2F7R8L2eBIDbsXG3xjndvbyk%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;281&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Redux&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 Flux 패턴을 이용한 구현체로 &lt;b&gt;Redux&lt;/b&gt;가 탄생합니다. 기존의 Props Drilling Problem의 문제점을 해결하고 Store, Dispatch, Reducer에 대한 개념을 정확하게 다시 정리해주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flux 패턴은 View를 각각의 MVC 컴포넌트 관점으로 보는 것이 아니라 하나의 큰 View로 이해하고 View에서는 Dispatch를 통해서 Action을 전달하면 Action은 Reducer를 통해서 Data 가 Store에 보관되고 Store에 들어있는 데이터는 다시 View로 연결되는 방식을 지향합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;기존의 컴포넌트 단위의 MVC 개념에서 완전히 비즈니스로직과 view를 분리하면서 이 분리된 개념을 &lt;b&gt;상태관리(State Management)&lt;/b&gt;라고 부르게 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flux 패턴의 한계로는 &lt;b&gt;높은 학습곡선&lt;/b&gt;과 &lt;b&gt;장황한 문법&lt;/b&gt;이 지적되었습니다. 간단한 구조에서는 Props Drilling 문제가 치명적이지 않았고 상태 관리를 위해 Action, Dispatch, Reducer를 만들고 관리하는데 부수적인 코드가 많아지면서 관리가 어려워진다는 문제가 있었습니다.&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;Observer-Observable 패턴&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BGLSW/btr93nEnvsC/7MVSQw7q0zy5SyfxUj0dR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BGLSW/btr93nEnvsC/7MVSQw7q0zy5SyfxUj0dR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BGLSW/btr93nEnvsC/7MVSQw7q0zy5SyfxUj0dR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBGLSW%2Fbtr93nEnvsC%2F7MVSQw7q0zy5SyfxUj0dR1%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;1128&quot; height=&quot;792&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;792&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Props Drilling Problem 문제를 Flux와는 다른 시각으로 해결해보고자 하는 &lt;b&gt;Observer-Observable 패턴&lt;/b&gt;이 등장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flux에서 Dispatch와 Action을 배제하고 값이 바뀌면 바뀐 값을 모두에게 전달한다는 개념입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초창기 Mobx 가 이러한 방식을 기반으로 작성되었다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Angular에서는 &lt;a href=&quot;https://rxjs.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;RxJS&lt;/a&gt;를 받아들이고 Flux 패턴까지 결합하여 Observable과 Flux가 혼합된 상태관리를 만들기도 하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MVI 패턴&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹프론트에서느 &lt;a href=&quot;https://cycle.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Cycle.js&lt;/a&gt; 라이브러리에서 먼저 소개된 개념입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgB8yy/btr9QVbYPqW/7SmPm8xGsoBmmUuF9EWtok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgB8yy/btr9QVbYPqW/7SmPm8xGsoBmmUuF9EWtok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgB8yy/btr9QVbYPqW/7SmPm8xGsoBmmUuF9EWtok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgB8yy%2Fbtr9QVbYPqW%2F7SmPm8xGsoBmmUuF9EWtok%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;960&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Flux 패턴에서 Dispatch와 Action과 Update의 인터페이스를 전부 Observable를 이용한 Stream의 하나의 방식으로 만들어 비동기 문제를 해결하고 장황한 문법을 하나의 인터페이스로 만든점이 인상적이라고 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Model: 모델은 상태를 나타냅니다. MVI의 모델은 아키텍처의 다른 레이어와의 단방향 데이터 흐름을 보장하기 위해 변경이 불가능해야 합니다.&lt;br /&gt;View: View를 나타내며 하나 이상의 Activity나 Fragment로 구현됩니다.&lt;br /&gt;Intent: 사용자 또는 앱내 발생하는 Action을 나타냅니다. 모든 Action에 대해 View는 intent를 수신합니다.Presenter는 Intent를 관찰하고 Model은 새로운 상태로 변환합니다.&lt;/blockquote&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;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Context 와 Hook&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vWO1z/btr94yFTacK/R0fL8D5GVk2LGZYxRa12qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vWO1z/btr94yFTacK/R0fL8D5GVk2LGZYxRa12qK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vWO1z/btr94yFTacK/R0fL8D5GVk2LGZYxRa12qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvWO1z%2Fbtr94yFTacK%2FR0fL8D5GVk2LGZYxRa12qK%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;786&quot; height=&quot;340&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Props Drilling Problem 이 문제라면 새로운 개념을 만들기보다 Props만 Drilling되지 않는 방법을 생각해보자 하는 시각입니다. 컴포넌트 트리에서 Context라는 거대한 공통 조상을 만들고 그 Context로 부터 데이터를 제공받는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념적으로는 별도의 Store를 두는 Flux와 비슷한 개념이라 복잡한 문법을 사용하는 Redux를 React가 제공하는 Context 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;Svelte에서도 React와 동일한 Context라는 개념을 제공하고 별도의 Props를 선언하지 않고 모든 Props를 자식으로 전달해주는 기능들도 제공하고 있다고 합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Atomic 패턴&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View와 Store의 분리는 그대로 가져가되 Action, Dispatch, Store 와 같은 복잡한 구조를 해결해보기 위한 시각으로 만들어진 패턴입니다. (Recoil, Svelte Store, Vue Composition, Jotai)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 문법으로 컴포넌트 외부에서 공통의 데이터를 set, get 을 할 수 있게 하면서 동시에 동기화를 할 수 있는 방향성입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 더불어 computed, derived, select와 같은 반응형 기능을 제공하여 관련된 데이터의 동시 업데이트를 제공하고 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;View와 Model은 분리한다.&lt;br /&gt;중간의 과정은 자율에 맡기고 간단하게 Model에 접근하는 법만 제공하자.&lt;br /&gt;동기화, 동시성 처리가 중요&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MVC의 확대 (React-Query)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발은 서버데이터를 CRUD 하고 시각으로 그리는 것에 중점되어 있는데 Flux 나 Atomic은 너무 복잡한 방법이라는 시각에서 React-Qurey에서는 고전적인 ajax 데이터를 Model로 간주 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKeW7u/btsatUKmnmQ/AbIxkwhYflpunn8qorAVGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKeW7u/btsatUKmnmQ/AbIxkwhYflpunn8qorAVGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKeW7u/btsatUKmnmQ/AbIxkwhYflpunn8qorAVGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKeW7u%2FbtsatUKmnmQ%2FAbIxkwhYflpunn8qorAVGk%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;364&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;React Query&lt;br /&gt;- 서버와의 fetch 영역을 Model로 간주&lt;br /&gt;- View는 React&lt;br /&gt;- Controller는 query와 mutation이라는 2가지의 인터페이스를 통해서 서버의 데이터의 상태를 관리하고 캐싱, 동기화, refetch 등을 관리해주는 역할&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지 아키텍처들과 발전에 대해 살펴보았습니다. 여러가지 아케텍처들의 큰 공통점은 View와 Business Logic 분리인것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 좋은 아키텍처는 무엇이고 좋은 아키텍처에 프로젝트를 맞추는 것이 아키텍처를 사용하는 방법이라는 생각을 가지고 있었는데.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지 아키텍처들을 보고 발전하는 방향을 보았을때 특정 아키텍처를 정하고 거기에 프로젝트를 맞추는 것이 아니라 프로젝트 상황에 따라 아키텍처는 지속적으로 발전해야 좋은 아키텍처 설계가 아닐까 하는 생각을 가져봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@teo/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-MV-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80%EC%9A%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프론트엔드에서 MV* 아키텍쳐란 무엇인가요?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dkrnfls.tistory.com/370&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; Javascript 프론트엔드 MV* 아키텍처&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tuhbm.github.io/2019/04/24/architecture/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;아키텍처란 무엇인가?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.kimcoder.io/blog/clean-frontend-architecture&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;클린한&amp;nbsp;프론트엔드&amp;nbsp;아키텍처를&amp;nbsp;향한&amp;nbsp;첫&amp;nbsp;걸음&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/@shinbaek89/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-business-logic%EC%9D%98-%EB%B6%84%EB%A6%AC-adc10ae881ab&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프론트엔드 아키텍처: Business Logic의 분리&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@andy0011/Flux-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Flux 패턴이란?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jaehochoe.medium.com/%EB%B2%88%EC%97%AD-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%EB%A5%BC-%EC%9C%84%ED%95%9C-mvi-model-view-intent-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-165bda9dfbe7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;안드로이드를 위한 MVI 아키텍쳐 튜토리얼: 시작하기&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/35</guid>
      <comments>https://mishka.tistory.com/35#entry35comment</comments>
      <pubDate>Fri, 12 May 2023 17:33:56 +0900</pubDate>
    </item>
    <item>
      <title>React - ref, forwardRef 사용해 값 전달하기</title>
      <link>https://mishka.tistory.com/34</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ref&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 에서 &lt;b&gt;ref&lt;/b&gt; prop은 HTML Element에 직접 접근하기 위해 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 아래 &lt;b&gt;&amp;lt;Form&amp;gt;&lt;/b&gt; 컴포넌트에서는 &lt;b&gt;useRef&lt;/b&gt; 훅으로 생성한 &lt;b&gt;inputRef&lt;/b&gt; 를 &lt;b&gt;&amp;lt;input&amp;gt;&lt;/b&gt; 엘리먼트의 &lt;b&gt;ref&lt;/b&gt; prop으로 넘기고 있습니다. 이렇게 해주면 &lt;b&gt;inputRef&lt;/b&gt; 객체에 접근해서 &lt;b&gt;current&lt;/b&gt;속성에 &lt;b&gt;&amp;lt;input&amp;gt;&lt;/b&gt; 엘리먼트에 레퍼런스가 할당되고 이를 통해 &lt;b&gt;handleFocus&lt;/b&gt; 이벤트 핸들러에서 &lt;b&gt;&amp;lt;input&amp;gt;&lt;/b&gt; 엘리먼트의 &lt;b&gt;focus&lt;/b&gt; 함수를 호출할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675690407975&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Form = () =&amp;gt; {
  const inputRef = useRef(null)

  const handleFocus = () =&amp;gt; {
    inputRef.current.focus()
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;input type=&quot;text&quot; ref={inputRef} /&amp;gt;
      &amp;lt;button onClick={handleFocus}&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;&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;그렇다면 부모컴포넌트에서 자식컴포넌트 내부에 있는 input에 접근 하려면 어떻게 해야 할까요??&lt;/p&gt;
&lt;pre id=&quot;code_1675691597604&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Parent = () =&amp;gt; {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Child/&amp;gt;
    &amp;lt;/&amp;gt;
  )
}

const Child = () =&amp;gt; {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;input/&amp;gt;
    &amp;lt;/&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 부모/자식 구조에서 Parent에서 어떤 이벤트가 발생했을 때 Child의 input에 focus를 줘야한다면 어떻게 해야 할까요?&lt;/p&gt;
&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;Parent&amp;gt;&lt;/b&gt;는 &lt;b&gt;useRef&lt;/b&gt; 훅 함수로 생성한 &lt;b&gt;inputRef&lt;/b&gt; 객체를 자식인 &lt;b&gt;&amp;lt;Child&amp;gt;&lt;/b&gt; 컴포넌트에 &lt;b&gt;ref&lt;/b&gt; prop으로 넘깁니다. 그러면 자식인 &lt;b&gt;Child&lt;/b&gt; 컴포넌트는 이 &lt;b&gt;ref&lt;/b&gt; prop으로 넘어온 &lt;b&gt;inputRef&lt;/b&gt; 객체를 다시 내부에 있는 &lt;b&gt;&amp;lt;input&amp;gt;&lt;/b&gt; 엘리먼트의 &lt;b&gt;ref&lt;/b&gt; prop으로 넘겨줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675692321629&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Child = ({ ref }) =&amp;gt; {
  return &amp;lt;input type=&quot;text&quot; ref={ref} /&amp;gt;
}

const Parent = () =&amp;gt; {
  const inputRef = useRef(null)

  const handleFocus = () =&amp;gt; {
    inputRef.current.focus()
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;Child ref={inputRef} /&amp;gt;
      &amp;lt;button onClick={handleFocus}&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;pre id=&quot;code_1675696565469&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Warning: Child: `ref` is not a prop. Trying to access it will result in `undefined` being returned. 
If you need to access the same value within the child component, you should pass it as a different prop.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지를 해석해보면 &lt;b&gt;ref&lt;/b&gt;는 prop이 아니라서 &lt;b&gt;undefined&lt;/b&gt;가 반환 될것이고, 그래서 다른 prop을 사용 해야 한다고 알려주고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 가이드도 같이 보여주고 있는데요&lt;/p&gt;
&lt;pre id=&quot;code_1675696836108&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Child = ({ inheritRef }) =&amp;gt; {
  return &amp;lt;input type=&quot;text&quot; ref={inheritRef} /&amp;gt;
}

const Parent = () =&amp;gt; {
  const inputRef = useRef(null)

  const handleFocus = () =&amp;gt; {
    inputRef.current.focus()
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;Child inheritRef={inputRef} /&amp;gt;
      &amp;lt;button onClick={handleFocus}&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;가이드와 같이 ref 대신 inheritRef와 같이 다른 이름의 prop를 사용하도록 컴포넌트를 수정하여 이 문제를 해결 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, React에서 공식적으로 ref 라는 props를 지원하는데 굳이 다른 이름으로 전달 할 수 밖에 없는지 의문을 가질수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;fowardRef 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서는 위와 같은 상황을 해결하기 위해 forwardRef를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서에서는 forwarRef를 아래와 같이 설명하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675698442385&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;forwardRef lets your component expose a DOM node to parent component with a ref.
//(의역) forwardRef 는 자식 컴포넌트의 DOM node와 ref를 부모 컴포넌트에 노출 할 수 있도록 해줍니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용방법은 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675698511357&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Parent = () =&amp;gt; {
  const inputRef = useRef&amp;lt;HTMLInputElement&amp;gt;(null)
  
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Child ref={inputRef}/&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}


const Child = React.forwardRef&amp;lt;HTMLInputElement&amp;gt;((_, inputRef) =&amp;gt; {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input ref={inputRef}/&amp;gt;
    &amp;lt;/div&amp;gt;
  )
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React의 정적 메서드인 forwardRef의 인자로 기존 컴포넌트를 넣어주기만 하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Child 컴포넌트에서의 매개변수 선언부를 보면 const Child = (_, inpurRef) =&amp;gt; {} 와 같은 형태로 두번째 매개변수를 명시했는데, 해당 매개변수가 바로 부모 컴포넌트에서 ref props로 넘겨준 ref 변수가 됩니다. 전달받은 ref 변수를 특정 element와 연결 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 컴포넌트에서 prop를 받기 위한 타입정의를 했다면 아래와 같이 사용 하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675698776192&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;React.forwardRef&amp;lt;HTMLInputElement, Props&amp;gt;((props, ref) =&amp;gt; {
// ...
})&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;b&gt;마무리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 컴포넌트에서 자식 컴포넌트 내부에 DOM을 직접 조작 하는 것은 일반적인 컴포넌트 관계에서는 지양하는 패턴이라고 합니다. 여러 컴포넌트로 나누어서 제작하는 것은 세부 기능을 숨기는 추상화를 하기 위한 목적이 있기 때문에 다른 컴포넌트에서 자식 컴포넌트의 특성을 너무 쉽게 변경 할수 있다면 추상화가 잘 안되어 있다고 볼수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, input이나 button 처럼 외부에서 직접 DOM을 컨트롤 해야 하는 상황에서는 forwardRef를 사용해서 관리하면 됩니다.&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;Reference&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@dev_2dong/React%EC%9D%98-forwardRef%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4-%ED%95%98%EC%9C%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-element-%EC%B0%B8%EC%A1%B0%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@dev_2dong/React%EC%9D%98-forwardRef%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4-%ED%95%98%EC%9C%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-element-%EC%B0%B8%EC%A1%B0%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/react-forward-ref/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.daleseo.com/react-forward-ref/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/react-refs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.daleseo.com/react-refs/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://beta.reactjs.org/reference/react/forwardRef&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://beta.reactjs.org/reference/react/forwardRef&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>forwardRef</category>
      <category>ref</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/34</guid>
      <comments>https://mishka.tistory.com/34#entry34comment</comments>
      <pubDate>Tue, 7 Feb 2023 00:58:48 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript - 유틸리티 타입(Utility types) 사용</title>
      <link>https://mishka.tistory.com/33</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에서는 Type Transformation을 유연하게 도와 주는 여러 &lt;b&gt;유틸리티 타입&lt;/b&gt;을 제공하고 있습니다. (&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/utility-types.html#excludetype-excludedunion&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript Utility Types&lt;/a&gt;) 그 중에 실무에서 유용한 몇가지 타입을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에서는 조건부 형식으로 타입을 정의 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675086455439&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;T extends U ? X : Y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 형태를 취하는데 조건식 결과에 따라 X가 될 수도 Y 가 될 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트는 용도에 맞게 조건부 타입을 활용한 새로운 타입들을 미리 정의해두고 이것을 &lt;b&gt;&quot;Predefined conditional types&quot;&lt;/b&gt; 라고 하며 그 중 하나가 &lt;b&gt;Exculude&lt;/b&gt;&amp;nbsp; 입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Exclude&amp;lt;T, U&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Exclude&lt;/b&gt; 타입은 2개의 제네릭 타입을 받을 수 있으며, 첫번재 제네릭 타입 T 중 두번째 제네릭 타입 U와 겹치는 타입을 제외한 타입을 반환합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675086319353&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Exclude&amp;lt;T, U&amp;gt; = T extends U ? never : T;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 T에 오는 타입들 중 U에 오는 것들은 제외하겠다는 의미가 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675086884932&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type OnlyNumber = Exclude&amp;lt;string|number, string&amp;gt;;
// OnlyNumber는 number 타입이 됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 말해 두번째 제네릭 타입 U에 대해 첫번째 제네릭 타입 T가 할당 가능한 타입인지 판단하고, 할당 가능한 타입을 제외한 나머지 타입들을 이용하여 타입을 정의 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Exculude는 보통 어떤 유니온 타입에서 특정한 타입들을 제외하려고 할 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 사용도 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675087410910&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Player {
  name: string;
  rank: string;
  contry: string;
  playTime: number;
}

type noNamePlayer = Exclude&amp;lt;keyof Player, &quot;name&quot;&amp;gt;;
// type noNamePlayer = &quot;rank&quot; | &quot;contry&quot; | &quot;playTime&quot; 와 같다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keyof Player 를 통해서 &lt;span style=&quot;background-color: #dddddd; color: #e83e8c;&quot;&gt;&lt;/span&gt;Player 객체의 key 값만을 모으고 exclude를 사용하여 제외할 값을(name) 지정 할 수 있습니다.&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;Pick &amp;lt;T, K&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;T 타입으로 부터 K 프로퍼티만 추출 합니다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;type Pick&amp;lt;T, K extends keyof T&amp;gt; = {
    [P in K]: T[P];
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Pick&lt;/b&gt;은 어떤 정의된 객체 형태의 타입에서 특정한 프로퍼티들만 골라서 새로운 타입으로 만들어 줍니다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick&amp;lt;Todo, &quot;title&quot; | &quot;completed&quot;&amp;gt;;
// TodoPreview 타입은 Todo 타입의 프로퍼티들 중에서 title, completed만 골라낸 타입이 됩니다.&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;b&gt;Omit &amp;lt;T,&amp;nbsp; K&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Omit&lt;/b&gt; 타입은 두개의 제네릭 타입을 받으며 T에서 모든 프로퍼티를 선택한 다음 K를 제거한 타입을 구성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675088012664&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Omit&amp;lt;T, K extends keyof any&amp;gt; = Pick&amp;lt;T, Exclude&amp;lt;keyof T, K&amp;gt;&amp;gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Omit&lt;/b&gt;은 어떤 정의된 객체 형태의 타입에서 특정한 프로퍼티들을 제외시켜 줍니다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;interface Match {
  accountNo: number;
  matchRank: number;
  name: string;
  age: number;
}

type PersonInfo = Omit&amp;lt;Match, &quot;accountNo&quot; | &quot;matchRank&quot;&amp;gt;;
// PersonInfo 타입은 Match 타입의 프로퍼티들중 accountNo,matchRank를 제외한 나머지로 이루어지게 됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Partial&amp;lt;T&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Partial&lt;/b&gt;은 제네릭 타입 T에 대해서 모든 프로퍼티들을 &lt;b&gt;Optional&lt;/b&gt;하게 변경합니다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;type Partial&amp;lt;T&amp;gt; = {    
  [P in keyof T]?: T[P];
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭 타입의 프로퍼티들에 대해 기존의 타입은 유지하되, 각각의 프로퍼티들을 optional 타입으로 변경해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675089073558&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Product {
    id: number;
    seller: number;
    category: number;
    name: string;
    price: string;
}

type SearchProduct = Partial&amp;lt;Product&amp;gt;;

/*
type SearchProduct {
    id?: number;
    seller?: number;
    category?: number;
    name?: string;
    price?: string;
}
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필수 타입과 optional 타입을 구분해서 사용해야 하는 경우 아래와 같이 쓸 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;type UserInformation = RequiredUserInformation &amp;amp; Partial&amp;lt;OptionalUserInformation&amp;gt;;

interface RequiredUserInformation {
  id: string;
  uid: string;
  name: string;
}

interface OptionalUserInformation {
  age: number;
  profile: string;
  phone: string;
}&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Exculde&lt;/b&gt;: 어떤 타입(보통 유니온)에서 &lt;b&gt;특정한 타입들을 제외&lt;/b&gt;시켜 정의하고 싶을 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pick&lt;/b&gt;: 정의된 객체 유형의 타입에서 &lt;b&gt;특정 프로퍼티를 선택&lt;/b&gt;한 새로운 타입을 정의 하고 싶을 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Omit&lt;/b&gt;: 정의된 객체 유형의 타입에서 &lt;b&gt;특정 프로퍼티를 제외&lt;/b&gt;한 타입을 정의하고 싶을 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Partial&lt;/b&gt;: 모든 프로퍼티를 &lt;b&gt;Optional 하게 변경&lt;/b&gt;하고 싶을&amp;nbsp;때&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://chanhuiseok.github.io/posts/ts-3/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://chanhuiseok.github.io/posts/ts-3/&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jaehoney.tistory.com/105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jaehoney.tistory.com/105&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@ggong/Typescript%EC%9D%98-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85-1-Record-Extract-Pick&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@ggong/Typescript%EC%9D%98-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85-1-Record-Extract-Pick&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@ggong/Typescript%EC%9D%98-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85-2-Partial-Required-ReadOnly-Omit-NonNullable-ReturnType&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@ggong/Typescript%EC%9D%98-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85-2-Partial-Required-ReadOnly-Omit-NonNullable-ReturnType&lt;/a&gt;&lt;/p&gt;</description>
      <category>Study</category>
      <category>exclude</category>
      <category>omit</category>
      <category>Partial</category>
      <category>pick</category>
      <category>typescript</category>
      <category>Utility type</category>
      <category>유틸리티타입</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/33</guid>
      <comments>https://mishka.tistory.com/33#entry33comment</comments>
      <pubDate>Mon, 30 Jan 2023 23:41:37 +0900</pubDate>
    </item>
    <item>
      <title>Next.js 기초부터 알아보기</title>
      <link>https://mishka.tistory.com/32</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Next.js 는 &lt;b&gt;React 라이브러리의 프레임워크&lt;/b&gt; 입니다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/h2&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;pre-reloading을 통해 미리 데이터가 렌더링된 페이지를 가져올수 있게 해주므로 사용자에게 더 좋은 경험을 주면서, 검색 엔진에 잘 노출 될 수 있도록 해주는 SEO의 장점을 가질수 있습니다. pre-reloading은 SSR 뿐만 아니라 SSG(Static Site Generate - 정적 사이트 생성)도 가능하게 해줍니다. 또한 SSR 과 CSR도 혼합하여 사용 할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;SEO 문제 - 클라이언트 사이드의 경우 자바스크립트가 로드 되지 않은 경우 아무런 정보가 보이지 않습니다. 구글의 검색엔진의 경우 자바스트립트가 로드되지 않은 페이지를 검색엔진으로 스캔함으로 결론적으로 검색에 아무 페이지도 걸리지 않는 문제가 발생합니다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Hot reloading &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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Automatic Routing&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 기반 라우팅 시스템이 제공 됩니다. pages 폴더에 있는 파일은 해당 이름으로 라우팅 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 /user/[id] 같은 dynamic route도 지원합니다.&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;Client Side Navigation&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 &amp;lt;Link /&amp;gt; 컴포넌트를 통해서 페이지간의 빠르고 매끄러운 이동이 가능합니다. HTML의 a 태그와 달리 페이지를 리로딩 하지 않고 페이지간 이동이 가능합니다. 이럴수 있는 것은 link 컴포넌트가 뷰포트에 보였을 때 관련 페이지를 백그라운드에서 미리 가져다 놓기 때문입니다. 이를 통해 사용자가 링크를 클릭했을 때 빠르게 해당 페이지로 이동할 수 있게 해줍니다.&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;Code Splitting&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dynamic import를 이용하면 손쉽게 Code Splitting이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 스플리팅은 원하는 페이지에서 원하는 자바스크립트와 라이브러리를 렌더링 하는 것입니다. 모든 번들이 하나로 묶이지 않고, 각각 나뉘어 보다 효율적으로 자바스크립트 로딩 시간을 개선할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1673280354855&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// index.js
import React from 'react'
import dynamic from 'next/dynamic'

// index 라우트에서 사용할 루트 컴포넌트(HomePage)를 dynamic import로 불러온다.
const Home = dynamic(() =&amp;gt; import('components/pages/HomePage')) 

class index extends React.Component {
  render() {
    return &amp;lt;Home&amp;gt;&amp;lt;/Home&amp;gt;
  }
}

export default index&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;구성&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;_app.tsx&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1673281025724&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { AppProps } from 'next/app'

import Layout from '@/components/Layout'
import base from '@/styles/base'

export default function App({ Component, pageProps }: AppProps) {
  return (
    &amp;lt;Layout&amp;gt;
      &amp;lt;Component {...pageProps} /&amp;gt;
    &amp;lt;/Layout&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초로 실행되는 것이 _app.tsx 입니다. _app.tsx 에서 렌더링 하는 값은 모든 페이지에 영향을 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_app.tsx는 클라이언트에서 띄우기 바라는 전체 컴포넌트는 공통 레이아웃임으로 최초 실행되며 내부에 컴포넌트들을 실행합니다.(위와 같이 Layout 컴포넌트를 별도로 만들어서 사용 하는 방법도 있습니다.) 내부에 컴포넌트가 있다면 전부 실행하고 HTML의 body로 구성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Componet, pageProps를 받습니다. 여기서 props로 받은 Component는 요청한 페이지이고, GET&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt; /&amp;nbsp;&lt;/span&gt; 요청을 보냈다면 Component에는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp;/page/index.js&amp;nbsp;&lt;/span&gt; 파일이 props로 내려오게 됩니다. pageProps는 페이지 getInitialProps를 통해 내려받은 props들을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 _document.tsx 가 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_app.tsx에서 cosole.log 실행시 client, server 둘다 콘솔이 찍힙니다.&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;_document.tsx&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;meta태그를 정의하거나, 전체 페이지에 관련한 컴포넌트입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1673281811386&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// pages/_document.tsx
import Document, { Html, Head, Main, NextScript } from &quot;next/document&quot;;

export default class MyDocument extends Document {
  render() {
    return (
      &amp;lt;Html&amp;gt;
        &amp;lt;Head&amp;gt;
          &amp;lt;meta property=&quot;custom&quot; content=&quot;123123&quot; /&amp;gt;
        &amp;lt;/Head&amp;gt;
        &amp;lt;body&amp;gt;
          &amp;lt;Main /&amp;gt;
        &amp;lt;/body&amp;gt;
        &amp;lt;NextScript /&amp;gt;
      &amp;lt;/Html&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_document를 작성할 때는 Document 클래스를 상속받는 클래스 컴포넌트로 작성해야만 하며, 렌더 함수는 꼭 &amp;lt;Html&amp;gt;, &amp;lt;Head&amp;gt;, &amp;lt;Main&amp;gt;, &amp;lt;NextScript&amp;gt; 요소를 리턴해줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_document에서 사용하는 &amp;lt;Head&amp;gt; 태그는 next/head가 아닌 next/document 모듈에서 불러와햐 하고, _document의 &amp;lt;Head&amp;gt;태그에는 모든 문서에서 공통적으로 적용될 내용이 들어가야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_document 는 언제나 서버에서 실행되므로 브라우저 api 또는 이벤트 핸들러가 포함된 코드는 실행되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 &amp;lt;Main /&amp;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;b&gt;Server Side Lifecycle 훑어보기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Next.js 서버가 &lt;b&gt;GET&lt;/b&gt; 요청을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;b&gt;GET&lt;/b&gt; 요청에 맞는 &lt;b&gt;pages/Componet&lt;/b&gt; 를 찾는다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;b&gt;_app.tsx&lt;/b&gt; 의 &lt;b&gt;getInitialProps&lt;/b&gt; 가 있다면 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. route에 맞는 페이지의 &lt;b&gt;Componet&lt;/b&gt;의 &lt;b&gt;getInitialProps&lt;/b&gt; 가 있다면 실행하고&amp;nbsp;&lt;b&gt;pageProps&lt;/b&gt; 들을 받아온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. &lt;b&gt;_document.tsx&lt;/b&gt; 의 &lt;b&gt;getInitialProps&lt;/b&gt; 가 있다면 실행하고 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;pageProps&lt;/b&gt; 들을 받아온다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 모든 props들을 구성하고&lt;b&gt; _app.tsx&lt;/b&gt; &amp;gt;&amp;gt; &lt;b&gt;page Component&lt;/b&gt; 순으로 렌더링한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 모든 Content를 구성하고 &lt;b&gt;_document.tsx&lt;/b&gt; 를 실행하여 HTML 형태로 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getinitialProps 가 궁금하다면 아래글을 참고해보세요.&lt;/p&gt;
&lt;figure id=&quot;og_1673282821989&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;Next js 구동방식 과 getInitialProps&quot; data-og-description=&quot;Next js가 React Project의 SSR을 가능하게 한다. 라고는 하는데, 어떤 방식으로 SSR을 가능하게 할까, SSR과 CSR의 구분은 어떻게 되어 있을까.이 궁금증을 해결하기 위해, 먼저 알아야 할 것은 Next js의 구&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@cyranocoding/Next-js-%EA%B5%AC%EB%8F%99%EB%B0%A9%EC%8B%9D-%EA%B3%BC-getInitialProps&quot; data-og-url=&quot;https://velog.io/@cyranocoding/Next-js-구동방식-과-getInitialProps&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ZO0cP/hyRdPma3jv/wzvPEYH4qEYew1dUe5LGm1/img.png?width=389&amp;amp;height=129&amp;amp;face=0_0_389_129,https://scrap.kakaocdn.net/dn/biK4AI/hyReM9efNd/ufYdF8dARcQkRxrZVwFBIk/img.png?width=389&amp;amp;height=129&amp;amp;face=0_0_389_129&quot;&gt;&lt;a href=&quot;https://velog.io/@cyranocoding/Next-js-%EA%B5%AC%EB%8F%99%EB%B0%A9%EC%8B%9D-%EA%B3%BC-getInitialProps&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@cyranocoding/Next-js-%EA%B5%AC%EB%8F%99%EB%B0%A9%EC%8B%9D-%EA%B3%BC-getInitialProps&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ZO0cP/hyRdPma3jv/wzvPEYH4qEYew1dUe5LGm1/img.png?width=389&amp;amp;height=129&amp;amp;face=0_0_389_129,https://scrap.kakaocdn.net/dn/biK4AI/hyReM9efNd/ufYdF8dARcQkRxrZVwFBIk/img.png?width=389&amp;amp;height=129&amp;amp;face=0_0_389_129');&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;Next js 구동방식 과 getInitialProps&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Next js가 React Project의 SSR을 가능하게 한다. 라고는 하는데, 어떤 방식으로 SSR을 가능하게 할까, SSR과 CSR의 구분은 어떻게 되어 있을까.이 궁금증을 해결하기 위해, 먼저 알아야 할 것은 Next js의 구&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kyounghwan01.github.io/blog/React/next/basic/#next-js%E1%84%80%E1%85%A1-%E1%84%8C%E1%85%A6%E1%84%80%E1%85%A9%E1%86%BC%E1%84%92%E1%85%A1%E1%84%82%E1%85%B3%E1%86%AB-%E1%84%8C%E1%85%AE%E1%84%8B%E1%85%AD-%E1%84%80%E1%85%B5%E1%84%82%E1%85%B3%E1%86%BC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;next.js 기본 개념 알아보기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@syoung125/Next.js-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-1-Next.js-%EB%9E%80-Next.js%EB%A5%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C-Next.js%EC%9D%98-%EC%9E%A5%EC%A0%90%EC%9D%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[next.js] 기본개념: Next.js 란? Next.js를 왜 사용할까? Next.js의 장점은?&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.rhostem.com/posts/2020-05-10-nextjs-and-dynamic-import&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;dynamic import로 줄여본 next.js 앱의 최초 로딩시간&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study</category>
      <category>next.js</category>
      <category>Server Side Lifecycle</category>
      <category>SSR</category>
      <category>_app.tsx</category>
      <category>장점</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/32</guid>
      <comments>https://mishka.tistory.com/32#entry32comment</comments>
      <pubDate>Tue, 10 Jan 2023 01:49:20 +0900</pubDate>
    </item>
    <item>
      <title>스토리북으로 개발하기</title>
      <link>https://mishka.tistory.com/31</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;스토리북?&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;스토리북은 독자적인 UI 컴포넌트 개발을 위한 업계표준 컴포넌트 탐색기 입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 개발자들을 위한 문서화(Documentations)에 사용할 수도 있고, 외부 공개용 디자인 시스템(Design System)을 개발하기 위한 기본 플랫폼으로도 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;장점&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;/li&gt;
&lt;li&gt;재사용을 위한 컴포넌트 들을 Story 에서 조합해 테스트 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;컴포넌트들을 문서화 할 수도 있고 디자인 시스템에 적용해 피그마의 컴포넌트들과 동기화할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;초기세팅&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1671455477117&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Storybook 설치
npx storybook init

# Storybook 실행
yarn storybook&lt;/code&gt;&lt;/pre&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;크게 .storybook 폴더와 stories 폴더가 생성되는데, .storybook 폴더 내부의 파일들은 전역적인 설정 관련&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stories 폴더 내부에는 예시가 되는 컴포넌트와 스토리들이 위치합니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;.storybook&lt;span&gt;&amp;nbsp;폴더&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;main.js&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stories를 위한 config 설정들이 위치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;stories: stories 파일이 어디에 있는지 경로설정&lt;/li&gt;
&lt;li&gt;addon: 확장 프로그램 설정&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;preview.js&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;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;합니다. 특정한 props를 넘겼을 때, 그 자체가 하나의 스토리가 되고 그렇게 다양한 스토리들을 정의하면 스토리북에서 스토리를 직관적으로 확인 할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;기본 형태&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1671458630771&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default {
  title: 스토리북에 올릴 component폴더 계층 구조,
  component: 스토리를 만들 컴포넌트 이름
}

export const 스토리이름 = () =&amp;gt; 해당스토리에서 테스트할 인자가 담긴 컴포넌트&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;적용예시&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1671498047312&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Button.tsx
import React from 'react';
import styled from '@emotion/styled';
interface ButtonProps {
    label: string;
    size?: 'small' | 'large';
    onClick?: () =&amp;gt; void;
}

export default function Button({label, size, ...props}:ButtonProps) {
    return (
        &amp;lt;Styled.Button size={size}&amp;gt;
            &amp;lt;div className=&quot;button&quot; role=&quot;button&quot; {...props}&amp;gt;
                {label}
            &amp;lt;/div&amp;gt;
        &amp;lt;/Styled.Button&amp;gt;
    )
}

const Styled = {
    Button:styled.div&amp;lt;{size: 'small'|'large'}&amp;gt;`
        display: flex; 
        justify-content: center;
        align-items: center;
        width: ${({ size }) =&amp;gt; (size === 'small' ? '48px' : '76px')};
        border-bottom: 1px solid #FFFFFF;
        .button{
            padding-top: 11px;
            padding-bottom: 11px;
            cursor: pointer;
            color: #FFFFFF;
            text-align: center;
            font-family: JejuMyeongjo;
            font-size: 14px;
        }
    `,
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1671498127276&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Button.stories.tsx
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import Button  from './Button';

// 어떤 컴포넌트의 story인지, 어떤 설정으로 렌더링할지 정의 
export default {
  title: 'stories/Button',
  component: Button,
} as ComponentMeta&amp;lt;typeof Button&amp;gt;;

// 기본 포맷을 정해두고 bind로 제어 
const Template: ComponentStory&amp;lt;typeof Button&amp;gt; = (args) =&amp;gt; &amp;lt;Button {...args} /&amp;gt;;

// 각각이 새로운 스토리들
// export const Small = () =&amp;gt; &amp;lt;Button size=&quot;small&quot; label=&quot;button&quot; /&amp;gt;; 얘와 같음
export const Small = Template.bind({});
Small.args = {
  size: 'small',
  label: 'Button',
};

export const Large = Template.bind({});
Large.args = {
  size: 'large',
  label: 'Button',
};&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@devstone/%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B6%81-%EC%A0%9C%EB%8C%80%EB%A1%9C-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@devstone/%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B6%81-%EC%A0%9C%EB%8C%80%EB%A1%9C-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://storybook.js.org/tutorials/design-systems-for-developers/react/ko/build/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://storybook.js.org/tutorials/design-systems-for-developers/react/ko/build/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/storybook/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.daleseo.com/storybook/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Study</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/31</guid>
      <comments>https://mishka.tistory.com/31#entry31comment</comments>
      <pubDate>Tue, 20 Dec 2022 10:03:45 +0900</pubDate>
    </item>
    <item>
      <title>Jest로 테스트코드 작성하기</title>
      <link>https://mishka.tistory.com/30</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;테스트의 종류&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;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;단위 테스트는 응용 프로그램에서 테스트 가능한 가장 작은 소프트웨어를 실행하여 예상대로 동작하는지 확인 하는 테스트&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;통합테스트&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;통합테스트는 단위 테스트보다 더 큰 동작을 달성하기 위해 여러 모듈들을 모아 이들이 의도대로 협력하는지 확인하는 테스트&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통합테스트는 단위테스트와 달리 개발자가 변경할 수 없는 부분(ex. 외부 라이브러리)까지 묶어서 검증할 때 사용합니다. 이는 DB에 접근하거나 전체 코드와 다양한 환경이 제대로 동작하는지 확인하는데 필요한 모든 작업을 수행 할 수 있습니다. 그러나 통합테스트가 응용 프로그램이 완전하게 동작하는지를 무조건 증명하지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통합테스트의 장점은 단위테스트에서 발견하기 어려운 버그를 찾을 수 있다는 점입니다. 한편 통합테스트의 단점은 단위 테스트보다 더 많은 코드를 테스트하기 때문에 신뢰성이 떨어질 수 있다는 점. 또한 어디에서 에러가 발생했는지 확인하기 쉽지 않아 유지보수가 힘들다는 점이 있습니다.&lt;/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;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;인수테스트는 사용자 스토리(시나리오)에 맞춰 수행하는 테스트&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비즈니스쪽에 초점을 둔 테스트입니다. 프로젝트에 참여하는 사람들(ex. 기획자, 클라이언트 대표, 개발자 등)이 토의해서 시나리오를 만들고, 개발자는 이에 의거하여 코드를 작성합니다. 개발자가 직접 시나리오를 제작할 수도 있지만 다른 의사소통집단으로부터 시나리오를 받아(인수) 개발한다는 의미를 가지고 있습니다.&lt;/p&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를 통해 드러나는데 그렇기에 인수테스트는 주로 이 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;결국, 인수테스트는 소프트웨어 인수를 목적으로 하는 테스트입니다. 소프트웨어를 인수하기 전에 명세한 요구 사항(인수조건)대로 잘 작동하는지 검증을 합니다. 소프트웨어를 인수할 때, 소프트웨어 내부 구조나 구현 방법을 고려하기보다는 실제 사용자 관점에서 테스트하는 경우가 많다. 따라서. 인수테스트는 소프트웨어 내부 코드에 관심을 가지지 않는 블랙박스 테스트 입니다. 실제 사용자 관점에서 테스트 할 때 주로 E2E 형식을 이용해서 확인을 합니다.&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;b&gt;테스트코드란?&lt;/b&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;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;1.코드가 정상적으로 동작하는지 확인할 수 있다&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;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. 코드의 결함을 사전에 발견할 수 있다.&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;3. 코드에 대한 의존성을 분리할 수 있다.&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;4. Refactoring을 부담없이 할 수 있다.&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;5. 문서로 작용 할 수 있다.&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Jest?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jest는 페이스북에서 만들어진 ALL-IN-ONE 테스팅 라이브러리 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jest 이전에 자바스크립트 코드를 테스트 하려면 여러가지 테스팅 라이브러리가 필요했었습니다. 예를들어 Mocha나 Jasmin을 Test runner로 사용하고 Chai 나 Expect와 같은 Test Mathcher를 사용하였고, Sinon과 Testdouble 같은 Test Mock 라이브러리도 필요하였습니다. 이러한 라이브러리들은 유사하지만 살짝씩 다른부분들이 있어서 개발자들에게 혼란을 야기하였습니다. 하지만 Jest는 Jest하나의 설치로 이러한 역할들을 모두 할수 있는 &lt;span&gt;ALL-in-one 테스팅&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;라이브러리 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Jest 라이브러리 설치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm 또는 yarn을 사용하여 Jest를 설치 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670849484188&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// npm
npm i -D jest

// yarn
yarn add --dev jest&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;script 추가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 내용을 package.json 에 추가하세요&lt;/p&gt;
&lt;pre id=&quot;code_1670850565364&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// package.json
&quot;scripts&quot;: {
  &quot;test&quot;: &quot;jest&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 내용을 script에 추가하면 npm jest 혹은 yarn jest 를 터미널에 입력 함으로 jest 커멘드를 실행할 수 있습니다.&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;b&gt;테스트 코드 작성&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1670850749838&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test(&quot;테스트 설명&quot;, () =&amp;gt; {
  expect(&quot;검증 대상&quot;).toXxx(&quot;기대 결과&quot;)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 형식으로 테스트 코드를 작성할 수 있습니다. 간단한 테스트 코드를 작성해 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670850801170&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test(&quot;1 is 1&quot;, () =&amp;gt; {
  expect(1).toBe(1)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 작성하고 터미널에 npm jest 혹은 yarn jest 라고 입력하면 초록색 글씨로 테스트가 통과 했다는 메시지를 볼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670850912723&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; jest

 PASS  ./test.js
  ✓ 1 is 1 (1 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.158 s, estimated 1 s&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;toBe 부분은 Test Matcher 라고 부르는데 여러가지 방법들이 있습니다. toBe 함수의 경우에는 숫자나 문자와 같은 객체가 아닌 기본형 값을 비교 할때 사용하는 Matcher입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 jest를 실행하면 프로젝트 내에 모든 테스트 파일을 찾아서 테스트를 실행해줍니다. test.js로 끝나거나 __test__ 디텍터리 안에 있는 파일들은 모두 테스트 파일로 인식합니다. 만약 특정 테스트 파일만 실행하고 싶을 경우에는 npm test &amp;lt;파일명 이나 경로&amp;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;b&gt;Reference&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://tecoble.techcourse.co.kr/post/2021-05-25-unit-test-vs-integration-test-vs-acceptance-test/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tecoble.techcourse.co.kr/post/2021-05-25-unit-test-vs-integration-test-vs-acceptance-test/&lt;/a&gt;&lt;a href=&quot;https://jestjs.io/docs/getting-started&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jestjs.io/docs/getting-started&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/jest-basic/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.daleseo.com/jest-basic/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JEST-%F0%9F%93%9A-jest-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://inpa.tistory.com/entry/JEST-%F0%9F%93%9A-jest-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC&lt;/a&gt;&lt;/p&gt;</description>
      <category>Study</category>
      <category>Jest</category>
      <category>단위테스트</category>
      <category>인수테스트</category>
      <category>코드테스트</category>
      <category>테스트</category>
      <category>테스트종류</category>
      <category>통합테스트</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/30</guid>
      <comments>https://mishka.tistory.com/30#entry30comment</comments>
      <pubDate>Mon, 12 Dec 2022 22:40:54 +0900</pubDate>
    </item>
    <item>
      <title>Emotion의 배경지식 / 사용법 (CSS in JS)</title>
      <link>https://mishka.tistory.com/29</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Emotion?&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Emotion은 &lt;b&gt;JavaScript&lt;/b&gt;로 &lt;b&gt;css 스타일&lt;/b&gt;을 작성하도록 설계된 라이브러리입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본격적으로 Emotion에 대해 알아보기 이전에 간단히&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;b&gt;다양한 웹 스타일링 기술&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CSS&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;CSS(Cascading Style Sheets)는 HTML 이나 XML로 작성된 문서의 표시 방법을 기술하기 위한 스타일 시트 언어입니다.&lt;br /&gt;CSS는 요소가 화면, 종이, 음성이나 다른 매체 상에 어떻게 렌더링 되어야 하는지 지정합니다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;CSS의 문제점 &lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;- &lt;b&gt;Global namespace&lt;/b&gt;: 모든 스타일이 global에 선언되어 중복되지 않는 class 이름을 적용해야 한다.&lt;br /&gt;- &lt;b&gt;Dependencies&lt;/b&gt;: CSS간의 의존 관계를 관리 하기 힘들다&lt;br /&gt;- &lt;b&gt;Dead Code Elimination&lt;/b&gt;: 기능 추가, 변경, 삭제 과정에서 불필요한 CSS를 제거하기 어렵다.&lt;br /&gt;- &lt;b&gt;Minification&lt;/b&gt;: 클래스 이름의 최소화가 어렵다.&lt;br /&gt;- &lt;b&gt;Sharing Constants&lt;/b&gt;: JS 코드와 상태값을 공유 할수 없다.&lt;br /&gt;- &lt;b&gt;Non-deterministic Resolution&lt;/b&gt;: CSS 로드 순서에 따라 스타일 우선 순위가 달라진다.&lt;br /&gt;- &lt;b&gt;Isolation&lt;/b&gt;: CSS와 JS가 분리된 탓에 상속에 따른 격리가 어렵다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;feat. Christopher Chedeau(일명 Vjeux)- 페이스북 Front-end engineer&lt;/span&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;b&gt;* CSS-in-CSS&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CSS 모듈(Module)&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;CSS를 사용 할 때 클래스 이름을 고유한 값으로 자동으로 만들어서 컴포넌트 스타일 클래스 이름이 중첨되는 현상을 방지해 주는 기술&lt;br /&gt;- CSS 모듈을 이용하면 클래스명이 충돌하는 단점을 극복할 수 있다.&lt;br /&gt;- CSS 모듈은 컴포넌트 단위로 스타일을 적용할 때 유용하다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CSS 전처리기(Preprocessor)&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;자신만의 특별한 Syntax를 가지고 CSS를 생성하도록 하는 프로그램. CSS의 문제점을 프로그래밍방식(변수, 함수, 상속 등)을 사용 보완하였습니다. CSS 전처리기에는 다양한 모듈이 존재하는데 Sass, Less, Stylus 가 대표적입니다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;* CSS-in-JS&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS-in-JS는 단어 그대로 자바스크립트 코드에서 CSS를 작성하는 방식을 말합니다. 2014년 페이스북 개발자인 Christopher Chedeau aka Vjeux가 처음 소개하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Styled Components, Emotion가 대표적입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Styled-components&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;기존 돔을 만드는 방식인 CSS, SCSS 파일을 밖에 두고, 태그나 Id, class 이름으로 가져와 쓰지 않고, 동일한 컴포넌트에서 컴포넌드 이름을 쓰듯 스타일을 지정하는 것을 styled-components 라고 부릅니다.&lt;/blockquote&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;Emotion 사용법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;emotion.js는 주로 Framwork Agnostic 과 React 두가지 방식으로 사용합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;설치&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1669957429114&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Framework Agnostic
$ npm install @emotion/css
or
$ yarn add @emotion/css

# React
$ npm install @emotion/react
or
$ yarn add @emotion/react&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;사용 (&lt;a href=&quot;https://emotion.sh/docs/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서 예문&lt;/a&gt;)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ccs() 함수는 CSS 스타일 선언 내용을 인자로 받는데 문자형과 객체형으로 넘길수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669958472692&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 문자형 스타일
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const color = 'white'

render(
  &amp;lt;div
    css={css`
      padding: 32px;
      background-color: hotpink;
      font-size: 24px;
      border-radius: 4px;
      &amp;amp;:hover {
        color: ${color};
      }
    `}
  &amp;gt;
    Hover to change color.
  &amp;lt;/div&amp;gt;
)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1669960319758&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 객체형 스타일
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const color = 'white'

render(
  &amp;lt;div
    css={css({
      padding: '32px',
      backgroundColor: 'hotpink',
      fontSize: '24px',
      borderRadius: '4px',
      cursor: 'pointer',
      '&amp;amp;:hover': {
        color: `${color}`,
      },
    })}
  &amp;gt;
    Hover to change color.
  &amp;lt;/div&amp;gt;
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;JSX pragma&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;emotion의 css prop을 제대로 사용하기 위해서는 파일 상단에 pragma 를 선언해 주어야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1669962072495&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pragma란 컴파일러에게 하는 전처리 명령이라고 생각하면 되는데 babel 트랜스파일러에게 JSX코드를 변환할때 React의 jsx() 함수 대신 Emotion의 jsx() 함수를 사용하라고 알려주는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React v16 이하 의 오래된 버전을 사용하고 있는 프로젝트에서는 약간 다른 형태로 사용하고 jsx()함수도 불러와야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1669961994763&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// React v16 이하
/** @jsx jsx */
import { css, jsx } from &quot;@emotion/react&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;pragma 지우기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 사용할 때 마다 pragma를 사용하는건 무척 번거로운 일입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 버전에 따라 @emotion/babel-prest-css-prop 나 @babel/preset-react 의 importSource옵션 변경을 통하여 pragma 없이 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 같은 경우에는 Next.js 세팅을 하고 있는데 Next.js의 경우는 tsconfig.json 에 아래 옵션만 추가해주시면 간단히 해결됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1669968439135&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// tsconfig.json
&quot;jsxImportSource&quot;: &quot;@emotion/react&quot;,&lt;/code&gt;&lt;/pre&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;&amp;lt;button&amp;gt; 요소의 CSS prop을 통해서 다양한 CSS 속성 정의를 객체로 넘길 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670251601628&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// EmotionBtn.js

function EmotionBtn({ children }) {
  return (
    &amp;lt;button
      css={{
        border: '1px solid gray',
        borderRadius: '6px',
        color: 'black',
        fontSize: '14px',
        padding: '10px 16px',
        cursor: 'pointer',
      }}
    &amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  )
}

export default EmotionBtn&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만든 컴포넌트를 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670251949203&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import EmotionBtn from 'EmotionBtn'

function App() {
  return &amp;lt;EmotionBtn&amp;gt;Button&amp;lt;/EmotionBtn&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 버튼을 브라우저에서 확인해보면 &amp;lt;button&amp;gt; 요소에 Emotion이 자동으로 생성해준 클래스 이름이 붙어 있는 것을 확인하실 수 있습니다. (클래스 이름은 랜덤으로 생성됩니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1670252120870&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button class=&quot;css-1ayjb79-Btn&quot;&amp;gt;Button&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타일도 확인해 보면 선언한내용과 같은것을 확인 할 수 있습니다. 선언한 것과 다른것이 있다면 Emotion에서 자동으로 브라우저별 필요한 vendor prefixing을 해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670252405910&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.css-1ayjb79-Btn {
  border: 1px solid gray;
  border-radius: 6px;
  color: black;
  font-size: 14px
  padding: 10px 16px;
  cursor: pointer;
  -webkit-appearance: none;
  -moz-appearance: none;
  -ms-appearance: none;
  appearance: none;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Prop을 이용한 스타일링&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prop에 따라 스타일의 변화를 주는 스타일링을 할 수 있습니다. &amp;lt;VariableBtn&amp;gt; 컴포넌트에 variant 라는 prop을 추가하고 변화를 주어 스타일링을 해보도록 하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670270574760&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// VariableBtn.js

const colors = {
  default: 'black',
  danger: 'red',
  outline: 'blue',
}

function VariableBtn({ children, variant }) {
  return (
    &amp;lt;button
      css={{
        border: '1px solid gray',
        borderRadius: '6px',
        color: colors[variant],
        fontSize: '14px',
        padding: '10px 16px',
        cursor: 'pointer',
        appearance: 'none',
        userSelect: 'none',
      }}
    &amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  )
}

export default VariableBtn&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 때에는 아래와 같이 porp에 값을 변경시켜 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670270765551&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import VariableBtn from 'VariableBtn'

function App() {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;VariableBtn variant=&quot;default&quot;&amp;gt;default&amp;lt;/VariableBtn&amp;gt;
      &amp;lt;VariableBtn variant=&quot;danger&quot;&amp;gt;danger&amp;lt;/VariableBtn&amp;gt;
      &amp;lt;VariableBtn variant=&quot;outline&quot;&amp;gt;outline&amp;lt;/VariableBtn&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prop을 이용해서 스타일링의 변화를 줄때 하나의 속성만이 아니라 여러개를 이용 할 수도 있습니다. 이런 경우에는 CSS속성을 객체로 만들어줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670271568635&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MultiVariableBtn.js

const colors = {
  default: 'black',
  danger: 'red',
  outline: 'blue',
}

const sizeStyle = {
  sm: {
    fontSize: '12px',
    padding: '3px 12px',
  },
  md: {
    fontSize: '14px',
    padding: '5px 16px',
  },
  lg: {
    fontSize: '16px',
    padding: '9px 20px',
  },
}

function MultiVariableBtn({ children, size = 'md', variant = 'default' }) {
  return (
    &amp;lt;button
      css={{
        border: '1px solid gray',
        borderRadius: '6px',
        color: colors[variant],
        ...sizeStyle[size],
        cursor: 'pointer',
        appearance: 'none',
        userSelect: 'none',
      }}
    &amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  )
}

export default MultiVariableBtn&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 때에는 간단히 props를 전달해주면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670271676704&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import MultiVariableBtn from 'MultiVariableBtn'

function App() {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;MultiVariableBtn size=&quot;sm&quot; variant=&quot;defalt&quot;&amp;gt;
        Sm Size
      &amp;lt;/MultiVariableBtn&amp;gt;
      &amp;lt;MultiVariableBtn size=&quot;md&quot; variant=&quot;danger&quot;&amp;gt;
        Md Size
      &amp;lt;/MultiVariableBtn&amp;gt;
      &amp;lt;MultiVariableBtn size=&quot;lg&quot; variant=&quot;outline&quot;&amp;gt;
        Lg Size
      &amp;lt;/MultiVariableBtn&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Global 스타일링&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역에 스타일링이 필요한 경우에도 간편하게 스타일링을 추가 할수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1670272896623&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Global.js

import { Global } from '@emotion/react'

const GlobalStyle = () =&amp;gt; (
  &amp;lt;Global
    styles={{
      '*': {
        color: 'blue',
      },
      a: {
        color: 'red',
      },
    }}
  /&amp;gt;
)

export default GlobalStyle&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1670272932527&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// index.js

import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom/client&quot;;
import App from &quot;./App&quot;;
import GlobalStyle from &quot;./global&quot;;

const root = ReactDOM.createRoot(
  document.getElementById(&quot;root&quot;) as HTMLElement
);
root.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;GlobalStyle /&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;
);&lt;/code&gt;&lt;/pre&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;b&gt;Reference&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://emotion.sh/docs/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://emotion.sh/docs/introduction&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.daleseo.com/emotion/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.daleseo.com/emotion/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gong-check.github.io/dev-blog/FE/%EC%98%A8%EC%8A%A4%ED%83%80/emotion%20%EC%A0%81%EC%9A%A9%EA%B8%B0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gong-check.github.io/dev-blog/FE/%EC%98%A8%EC%8A%A4%ED%83%80/emotion%20%EC%A0%81%EC%9A%A9%EA%B8%B0/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tech.osci.kr/2022/06/14/%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90-%EC%8A%A4%ED%83%80%EC%9D%BC-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0-with-emotion/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tech.osci.kr/2022/06/14/%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%97%90-%EC%8A%A4%ED%83%80%EC%9D%BC-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0-with-emotion/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@nightowl094/Next.js-Emotion-css-prop-%EC%84%A4%EC%A0%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@nightowl094/Next.js-Emotion-css-prop-%EC%84%A4%EC%A0%95&lt;/a&gt;&lt;/p&gt;</description>
      <category>Study</category>
      <category>css</category>
      <category>CSS in CSS</category>
      <category>css in js</category>
      <category>emotion</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/29</guid>
      <comments>https://mishka.tistory.com/29#entry29comment</comments>
      <pubDate>Tue, 6 Dec 2022 05:48:55 +0900</pubDate>
    </item>
    <item>
      <title>React Query</title>
      <link>https://mishka.tistory.com/28</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;React Query&lt;/b&gt;는 React 어플리케이션에서 서버의 상태를 불러오고, 캐싱하며, 지속적으로 동기화 하고 업데이트 하는 작업을 도와 주는 라이브러리 입니다.&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;b&gt;장점&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;* 캐싱&lt;br /&gt;* Boilerplate 코드의 감소&lt;br /&gt;* get 한 데이터에 update 가 발생하면 자동으로 get을 다시 수행한다.&lt;br /&gt;* 동일 데이터 여러번 요청시 한번만 요청한다 (옵션에 따라 중복호출 허용 시간 조절 가능)&lt;br /&gt;* 데이터가 오래되었다고 판단하면 다시 get (invalidateQueries)&lt;br /&gt;* API 요청 수행을 위한 규격화된 방식 제공&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;사용&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1669552709277&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx create-react-app my-app
cd my-app
yarn add react-query
yarn &amp;amp;&amp;amp; yarn start&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;세팅&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1669552768299&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/index.js
import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom&quot;;
import App from &quot;./App&quot;;
import { QueryClient, QueryClientProvider } from &quot;react-query&quot;;
import { ReactQueryDevtools } from &quot;react-query/devtools&quot;;

const queryClient = new QueryClient();

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;QueryClientProvider client={queryClient}&amp;gt;
      {/* devtools */}
      &amp;lt;ReactQueryDevtools initialIsOpen={true} /&amp;gt;
      &amp;lt;App /&amp;gt;
    &amp;lt;/QueryClientProvider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById(&quot;root&quot;)
);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;useQuery&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터를 get 하기 위한 api / post,update에는 useMutation을 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 첫번째 파라미터로 unique Key가 들어가고, 두번째 파라미터로 비동기 함수(api 호출함수)가 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- unique Key는 다른 컴포넌트에서도 해당 키를 사용하면 호출이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; unique Key는 string 과 배열을 받는데 배열로 넘기면 0번 값은 string 값으로 다른 컴포넌트에서 부를 값이 들어가고 두번째 값을 넣으면 query 함수 내부에 파라미터로 해당 값이 전달됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- return 값은 api의 성공,실패 여부. 즉 api return값을 포함한 객체입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- useQuery는 비동기로 작동합니다. 여러개의 비동기 query가 있다면 useQueries를 권장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- enabled를 사용하면 useQuery를 동기적으로 사용가능&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;예제&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1669560374644&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Todos = () =&amp;gt; {
  const { isLoading, isError, data, error } = useQuery(&quot;todos&quot;, fetchTodoList, {
    refetchOnWindowFocus: false, // react-query는 사용자가 사용하는 윈도우가 다른 곳을 갔다가 다시 화면으로 돌아오면 이 함수를 재실행합니다. 그 재실행 여부 옵션 입니다.
    retry: 0, // 실패시 재호출 몇번 할지
    onSuccess: data =&amp;gt; {
      // 성공시 호출
      console.log(data);
    },
    onError: e =&amp;gt; {
      // 실패시 호출 (401, 404 같은 error가 아니라 정말 api 호출이 실패한 경우만 호출됩니다.)
      // 강제로 에러 발생시키려면 api단에서 throw Error 날립니다. (참조: https://react-query.tanstack.com/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default)
      console.log(e.message);
    }
  });

  if (isLoading) {
    return &amp;lt;span&amp;gt;Loading...&amp;lt;/span&amp;gt;;
  }

  if (isError) {
    return &amp;lt;span&amp;gt;Error: {error.message}&amp;lt;/span&amp;gt;;
  }

  return (
    &amp;lt;ul&amp;gt;
      {data.map(todo =&amp;gt; (
        &amp;lt;li key={todo.id}&amp;gt;{todo.title}&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;* status 로 isLoding, isSucess를 한번에 처리 할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1669560419482&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Todos() {
  const { status, data, error } = useQuery(&quot;todos&quot;, fetchTodoList);

  if (status === &quot;loading&quot;) {
    return &amp;lt;span&amp;gt;Loading...&amp;lt;/span&amp;gt;;
  }

  if (status === &quot;error&quot;) {
    return &amp;lt;span&amp;gt;Error: {error.message}&amp;lt;/span&amp;gt;;
  }

  return (
    &amp;lt;ul&amp;gt;
      {data.map(todo =&amp;gt; (
        &amp;lt;li key={todo.id}&amp;gt;{todo.title}&amp;lt;/li&amp;gt;
      ))}
    &amp;lt;/ul&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;useQuery의 동기적 실행&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enabled 옵션을 사용하면 useQuery를 동기적으로 사용 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useQuery의 3번째 인자로는 옵션값이 들어가는데 enabled값을 true일때 useQuery를 실행합니다. 이를 이용하면 동기적으로 함수를 실행 할 수 있습니다.&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;b&gt;useQueries&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useQuery를 비동기로 여러개 실행할 경우 어려움을 겪을수 있습니다. 이를 방지하기 위해 promise.all처럼 useQuery를 하나로 묶을수 있는 기능이 useQueries 입니다. promise.all처럼 하나의 배열에 각 쿼리에 대한 상태 값이 객체로 들어옵니다.&lt;/p&gt;
&lt;pre id=&quot;code_1669560755158&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 아래 예시는 롤 룬과, 스펠을 받아오는 예시입니다.
const result = useQueries([
  {
    queryKey: [&quot;getRune&quot;, riot.version],
    queryFn: () =&amp;gt; api.getRunInfo(riot.version)
  },
  {
    queryKey: [&quot;getSpell&quot;, riot.version],
    queryFn: () =&amp;gt; api.getSpellInfo(riot.version)
  }
]);

useEffect(() =&amp;gt; {
  console.log(result); // [{rune 정보, data: [], isSucces: true ...}, {spell 정보, data: [], isSucces: true ...}]
  const loadingFinishAll = result.some(result =&amp;gt; result.isLoading);
  console.log(loadingFinishAll); // loadingFinishAll이 false이면 최종 완료
}, [result]);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;useMutation&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 바꿀때 사용하는 api입니다. return 값은 useQuery와 동일하다.&lt;/p&gt;
&lt;pre id=&quot;code_1669560911366&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState, useContext, useEffect } from &quot;react&quot;;
import loginApi from &quot;api&quot;;
import { useMutation } from &quot;react-query&quot;;

const Index = () =&amp;gt; {
  const [id, setId] = useState(&quot;&quot;);
  const [password, setPassword] = useState(&quot;&quot;);

  const loginMutation = useMutation(loginApi, {
    onMutate: variable =&amp;gt; {
      console.log(&quot;onMutate&quot;, variable);
      // variable : {loginId: 'xxx', password; 'xxx'}
    },
    onError: (error, variable, context) =&amp;gt; {
      // error
    },
    onSuccess: (data, variables, context) =&amp;gt; {
      console.log(&quot;success&quot;, data, variables, context);
    },
    onSettled: () =&amp;gt; {
      console.log(&quot;end&quot;);
    }
  });

  const handleSubmit = () =&amp;gt; {
    loginMutation.mutate({ loginId: id, password });
  };

  return (
    &amp;lt;div&amp;gt;
      {loginMutation.isSuccess ? &quot;success&quot; : &quot;pending&quot;}
      {loginMutation.isError ? &quot;error&quot; : &quot;pending&quot;}
      &amp;lt;input type=&quot;text&quot; value={id} onChange={e =&amp;gt; setId(e.target.value)} /&amp;gt;
      &amp;lt;input
        type=&quot;password&quot;
        value={password}
        onChange={e =&amp;gt; setPassword(e.target.value)}
      /&amp;gt;
      &amp;lt;button onClick={handleSubmit}&amp;gt;로그인&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Index;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;update후에 get 실행&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mutation 함수가 성공 할때, unique Key 로 맵핑된 get 함수로 invalidateQueries 에 넣어주면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1669561016433&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const mutation = useMutation(postTodo, {
  onSuccess: () =&amp;gt; {
    // postTodo가 성공하면 todos로 맵핑된 useQuery api 함수를 실행합니다.
    queryClient.invalidateQueries(&quot;todos&quot;);
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mutation에서 return된 값을 이용해서 get 함수의 파라미터를 변경해야 될 경우에는 setQueryData를 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1669561080134&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const queryClient = useQueryClient();

const mutation = useMutation(editTodo, {
  onSuccess: data =&amp;gt; {
    // data가 fetchTodoById로 들어간다
    queryClient.setQueryData([&quot;todo&quot;, { id: 5 }], data);
  }
});

const { status, data, error } = useQuery([&quot;todo&quot;, { id: 5 }], fetchTodoById);

mutation.mutate({
  id: 5,
  name: &quot;nkh&quot;
});&lt;/code&gt;&lt;/pre&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;b&gt;react Suspense 와 Error boundary 같이 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Suspense 사용하여 loading을 Error boundary를 사용하여 에러 핸들링을 더욱 직관적으로 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;suspense를 사용하기 위해 QueryClient에 옵션을 하나 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1669561493724&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/index.js
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 0,
      suspense: true
    }
  }
});

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;QueryClientProvider client={queryClient}&amp;gt;
      &amp;lt;App /&amp;gt;
    &amp;lt;/QueryClientProvider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById(&quot;root&quot;)
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수에 suspense를 사용하는 예시&lt;/p&gt;
&lt;pre id=&quot;code_1669561526585&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const { data } = useQurey(&quot;test&quot;, testApi, { suspense: true });

// 사용
return (
  // isLoading이 true이면 Suspense의 fallback 내부 컴포넌트가 보여집니다.
  // isError가 true이면 ErrorBoundary의 fallback 내부 컴포넌트가 보여집니다.
  &amp;lt;Suspense fallback={&amp;lt;div&amp;gt;loading&amp;lt;/div&amp;gt;}&amp;gt;
    &amp;lt;ErrorBoundary fallback={&amp;lt;div&amp;gt;에러 발생&amp;lt;/div&amp;gt;}&amp;gt;
      &amp;lt;div&amp;gt;{data}&amp;lt;/div&amp;gt;
    &amp;lt;/ErrorBoundary&amp;gt;
  &amp;lt;/Supense&amp;gt;
);&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;&lt;b&gt;Reference&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://kyounghwan01.github.io/blog/React/react-query/basic/#react-suspense%E1%84%8B%E1%85%AA-react-query-%E1%84%89%E1%85%A1%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kyounghwan01.github.io/blog/React/react-query/basic/#react-suspense%E1%84%8B%E1%85%AA-react-query-%E1%84%89%E1%85%A1%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/react-query-1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tech.kakaopay.com/post/react-query-1/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Study</category>
      <category>React</category>
      <category>react-query</category>
      <category>Study</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/28</guid>
      <comments>https://mishka.tistory.com/28#entry28comment</comments>
      <pubDate>Mon, 28 Nov 2022 00:12:48 +0900</pubDate>
    </item>
    <item>
      <title>React 상태관리 라이브러리 1탄 (Redux)</title>
      <link>https://mishka.tistory.com/27</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;React 에는 많은 상태관리 라이브러리들이 있습니다. 그중에 가장 많이 사용하는 Redux를 먼저 정리해 보았습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Redux의 역사&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;MVC 패턴&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux의 역사는 MVC 패턴에서 시작합니다. MVC 패턴에서 Cotroller는 Model에 정의된 데이터를 조회하거나 업데이트하는 역할을 하고, 변경된 Model의 데이터를 View에 반영해 줍니다. 또한 사용자는 View를 통해 데이터를 입력하고 Model에 반영되며, View 와 Model은 데이터를 양방향으로 주고받는 형태입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MoRpQ/btrRC0aFXJG/wsIKXnbrKZCSRTYeXRk7qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MoRpQ/btrRC0aFXJG/wsIKXnbrKZCSRTYeXRk7qk/img.png&quot; data-alt=&quot;출처: Flux 공식문서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MoRpQ/btrRC0aFXJG/wsIKXnbrKZCSRTYeXRk7qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMoRpQ%2FbtrRC0aFXJG%2FwsIKXnbrKZCSRTYeXRk7qk%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;1300&quot; height=&quot;286&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: Flux 공식문서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 규모가 커질수록 수많은 View와 Model들이 생겨났기 때문에 데이터가 어디로 흐르는지 파악하기 어렵다는 문제점이 있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qXQSN/btrRBIH12xT/Am5naMgoVDlJCLLZtFExQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qXQSN/btrRBIH12xT/Am5naMgoVDlJCLLZtFExQ1/img.png&quot; data-alt=&quot;출처: Fulx 공식문서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qXQSN/btrRBIH12xT/Am5naMgoVDlJCLLZtFExQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqXQSN%2FbtrRBIH12xT%2FAm5naMgoVDlJCLLZtFExQ1%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;1024&quot; height=&quot;553&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: Fulx 공식문서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 기능을 추가할때 마다 크고 작은 문제가 생겼고, 예상 할 수 없는 결과(Side Effect)가 일어났습니다.&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;Flux의 시작&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제들을 해결하기 위해 페이스북 개발팀에서 새로운 아키텍처를 적용하기로 하여 나온것이 Flux 패턴입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RfjEp/btrRFeMWtfJ/uu8AF9O0UWsFUaCYXSZUXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RfjEp/btrRFeMWtfJ/uu8AF9O0UWsFUaCYXSZUXK/img.png&quot; data-alt=&quot;출처: Flux 공식문서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RfjEp/btrRFeMWtfJ/uu8AF9O0UWsFUaCYXSZUXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRfjEp%2FbtrRFeMWtfJ%2Fuu8AF9O0UWsFUaCYXSZUXK%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;1300&quot; height=&quot;393&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: Flux 공식문서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flux 패턴은 어떤 Action이 발생하면 dispatcher에 의해 store에 변경된 사항이 저장되고 그 저장된 데이터들에 의해 view가 변경되는 단방향 패턴입니다. 이러한 패턴의 가장 큰 장점은 양방향으로 흐르던 MVC 패턴과 반대로 단방향으로 흐르기 때문에 흐름을 파악하기 쉽고 예측 가능하다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 Flux 패턴을 적용한 구현체들이 많이 있는데 그 중 하나가 바로 &lt;b&gt;Redux&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;&lt;b&gt;Redux의 핵심 키워드&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Store&lt;/b&gt;&lt;br /&gt;Store는 애플리케이션의 상태 트리를 가지고 있는 객체.&lt;br /&gt;Redux 앱에는 단 하나의 저장소만 있어야 합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;State&lt;br /&gt;&lt;/b&gt;State는 넓은 의미의 단어이지만, Redux API 에서는 보통 저장소에 의해 관리되고 getState()에 의해 반환되는 하나의 상태 값을 지칭. state는 Redux 애플리케이션의 전체 상태를 나타내며, 보통 깊게 중첩되어 있는 객체입니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Action&lt;/b&gt;&lt;br /&gt;Action은 상태를 변화시키려는 의도를 표현하는 평범한 객체.&lt;br /&gt;action으 store에 데이터를 넣는 유일한 방법입니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Action Creator&lt;/b&gt;&lt;br /&gt;Action Creator는 단지 Action을 만드는 함수 입니다.&lt;br /&gt;action creator를 호출하면 Action을 만들어낼 뿐 dispatch하지는 않습니다.&lt;br /&gt;action creator를 호출해 그 결과를 Store인스턴스로 바로 dispatch하는 함수를 바인드된 액션 생성자 라고 부르기도 합니다.&amp;nbsp;&lt;br /&gt;action creator가 현재 상태를 읽어야 하거나 API 호출을 실행하거나, 라우트 전환같은 부수효과를 일으켜야 한다면, action대신 asyncAction을 반환해야 합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Reducer&lt;/b&gt;&lt;br /&gt;Reducer는 누적값과 값을 받아서 새로운 누적값을 반환하는 함수.&lt;br /&gt;이들은 값들의 컬렉션을 받아서 하나의 값을 줄이는데 사용됩니다. reducer는 Redux만의 개념은 아닙니다.&lt;br /&gt;Redux에서 누적값은 상태 객체이고, 누적될 값은 액션입니다. &lt;br /&gt;리듀서는 주어진 이전 상태와 액션에서 새로운 상태를 계산합니다. &lt;br /&gt;=&amp;gt; 현재(이전)의 state와 action을 인자로 받아 store에 접근해 action에 맞춰 state를 변경 합니다.&lt;br /&gt;이들은 반드시 같은 입력이 있으면 같은 출력을 반환하는 &lt;b&gt;순수 함수&lt;/b&gt; 여야만 합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Dispatch&lt;/b&gt;&lt;br /&gt;Dispatch는 store의 내장 함수 중 하나로, action이나 async action을 받는 함수.&lt;br /&gt;기본 dispatch 함수는 반드시 동기적으로 store에 reducer에 action을 보내야 합니다. 그러면 reducer는 Store가 반환한 이전 상태와 함께 새 상태를 계산합니다. reducer가 사용하기 위해서 action은 평범한 객체여야 합니다.&lt;br /&gt;middleware는 기본 dispatch 함수를 감쌉니다. middleware를 통해 dispatch 함수는 action뿐만 아니라 async action을 처리 할 수 있습니다. middleware는 action이나 async action을 다음 middleware에 넘기기 전에 변환하거나, 지연시키거나, 무시하거나, 해석할 수 있습니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Async Action&lt;br /&gt;&lt;/b&gt;Async Action은 dispatch로 보내지는 값이지만. 아직 reducer에게 받아들려질 준비가 되어 있지는 않았습니다. async action은 기본 dispatch함수로 전달되기 전에 middleware를 통해 action등으로 바뀌어야 합니다. async action은 여러분이 사용하는 middleware에 따라 서로 다른 타입이 될 수 있습니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Middleware&lt;/b&gt;&lt;br /&gt;Middleware는 dispatch함수를 결합해서 새 dispatch함수를 반환하는 고차함수.&lt;br /&gt;middleware는 함수 결합을 통해 서로 결합할 수 있습니다. 이는 action을 로깅하거나, 라우팅과 같은 부수효과를 일으키거나, 비동기 API 호툴을 일련의 동기 액션으로 바꾸는데 유용합니다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Subscribe&lt;br /&gt;&lt;/b&gt;Subscribe는 store의 내장 함수 중 하나로, 특정 함수를 전달해주면 action이 dispatch 되었을 때마다 전달된 함수가 호출 됩니다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Redux의 세가지 원칙&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Single source of truth&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;애플리케이션의 모든 상태는 하나의 저장소 안에 하나의 개체 트리 구조로 저장됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1c1e21;&quot;&gt;이를 통해 범용적인 애플리케이션(universal application, 하나의 코드 베이스로 다양한 환경에서 실행 가능한 코드)을 만들기 쉽게 만들 수 있습니다. 서버로부터 가져온 상태는 시리얼라이즈되거나(serialized) 수화되어(hydrated) 전달되며 클라이언트에서 추가적인 코딩 없이도 사용할 수 있습니다. 또한 하나의 상태 트리만을 가지고 있기 때문에 디버깅에도 용이할 것입니다. 빠른 개발 사이클을 위해 개발중인 애플리케이션의 상태를 저장해놓을 수도 있습니다. 하나의 상태 트리만을 가지고 있기 때문에 이전에는 굉장히 구현하기 어려웠던 기능인 실행취소/다시실행(undo/redo)을 손쉽게 구현할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. State is read-only&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상태를 변화시키는 유일한 방법은 무슨 일이 벌어지는 지를 묘사하는 액션 객체를 전달하는 방법 뿐이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1c1e21;&quot;&gt;이를 통해서 뷰나 네트워크 콜백에서 결코 상태를 직접 바꾸지 못 한다는 것을 보장할 수 있습니다. 모든 상태 변화는 중앙에서 관리되며 모든 액션은 엄격한 순서에 의해 하나하나 실행되기 때문에, 신경써서 관리해야할 미묘한 경쟁 상태는 없습니다. 액션은 그저 평범한 객체입니다. 따라서 기록을 남길 수 있고, 시리얼라이즈할 수 있으며, 저장할 수 있고, 이후에 테스트나 디버깅을 위해서 재현하는 것도 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. Changes are made with pure functions&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;액션에 의해 상태 트리가 어떻게 변화하는 지를 지정하기 위해 프로그래머는 순수&lt;span&gt;&amp;nbsp;&lt;/span&gt;리듀서를 작성해야합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1c1e21;&quot;&gt;리듀서는 그저 이전 상태와 액션을 받아 다음 상태를 반환하는 순수 함수입니다. 이전 상태를 변경하는 대신 새로운 상태 객체를 생성해서 반환해야한다는 사실을 기억해야 합니다. 처음에는 하나의 리듀서만으로 충분하지만, 애플리케이션이 성장해나가면 상태 트리의 특정한 부분들을 조작하는 더 작은 리듀서들로 나누는 것도 가능합니다. 리듀서는 그저 함수이기 때문에 호출되는 순서를 정하거나 추가적인 데이터를 넘길 수도 있습니다. 심지어 페이지네이션과 같이 일반적인 재사용 가능한 리듀서를 작성하는 것도 가능합니다.&lt;/span&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;b&gt;&lt;span style=&quot;color: #1c1e21;&quot;&gt;Redux flow&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1c1e21;&quot;&gt;1. UI에서 컴포넌트 내에 존재하는 이벤트가 호출됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1c1e21;&quot;&gt;2. 이벤트와 연결된 &lt;b&gt;Action Creator&lt;/b&gt;가 호출됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1c1e21;&quot;&gt;3. Action Creator에서 생성된 &lt;b&gt;Action&lt;/b&gt;이 호출됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1c1e21;&quot;&gt;4. Action이 &lt;b&gt;Reducer&lt;/b&gt;로 전달 됩니다. 이 과정을 &lt;b&gt;Dispatch&lt;/b&gt;에서 담당합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1c1e21;&quot;&gt;5. Reducer에서 Dispatch된 Action에 따라 상태 값을 변경합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1c1e21;&quot;&gt;6. 변경사항이 렌더링되어 UI에 나타납니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study</category>
      <category>flux</category>
      <category>MVC</category>
      <category>Redux</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/27</guid>
      <comments>https://mishka.tistory.com/27#entry27comment</comments>
      <pubDate>Mon, 21 Nov 2022 22:38:14 +0900</pubDate>
    </item>
    <item>
      <title>ESLint,  Prettier 무엇이 다르고 어떻게 Setting 해야 할까?</title>
      <link>https://mishka.tistory.com/26</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;'ESLint'&lt;/b&gt;는 자바스크립트 코드에서 발견된 문제를 식별하기 위한 &lt;b&gt;정적 코드 분석 도구&lt;/b&gt;입니다. 이 도구는 2013년에 니콜라스 C. 자카스에 의해 개발되었습니다. ESLint의 규칙들은 구성이 가능하며, 사용자는 지정한 규칙을 정의하고 로드할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 중 특정 기능을 구현할 때, 그 기능을 구현하기 위한 여러가지 방법이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들자면 함수를 정의 할 때, function 키워드를 사용하여 할수도 있고 arrow function을 쓸 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 배열의 반복문을 사용할 때, for문을 사용할 수도 있지만, forEach, map 등 Array 내장 함수를 사용 할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 여러가지 방식들을 &lt;b&gt;일관성있는 방식으로 구현할 수 있도록 도와주고 고쳐주는 것&lt;/b&gt;이 ESLint 의 역할입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;'Prettier'&lt;/b&gt;는 코드를 분석하여 깔끔하고 일관된 코드스타일을 유지시켜주게 도와주는 &lt;b&gt;코드 포멧터 &lt;/b&gt;입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prettier 는 ESLint 처럼 '코드 구현 방식'이 아닌 줄 바꿈, 공백, 들여 쓰기 등의 에디터에서 '텍스트'가 일관되게 작성되도록 도와 주는 역할을 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1665552455696&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const foo = () =&amp;gt; {
  const a = [1, 2, 3] // 스콮프 내부 작성시 두 공배 들여쓰기
}
  // &amp;lt;= 빈 줄이 한 줄 이상 안됨.
foo()&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;b&gt;ESLint Setting&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;ESLint 설치&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1665552694401&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install -D eslint
// or
$ yarn add -D eslint&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESlint 는 설치만 한다고 바로 프로젝트에서 사용할 수 없고, &lt;b&gt;ESLint extension&lt;/b&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;스크린샷 2022-10-12 오후 2.33.02.png&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;1102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chtChk/btrOnLni7Nc/fT4RRZrefRxuFtFHCHryz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chtChk/btrOnLni7Nc/fT4RRZrefRxuFtFHCHryz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chtChk/btrOnLni7Nc/fT4RRZrefRxuFtFHCHryz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchtChk%2FbtrOnLni7Nc%2FfT4RRZrefRxuFtFHCHryz1%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;1636&quot; height=&quot;1102&quot; data-filename=&quot;스크린샷 2022-10-12 오후 2.33.02.png&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;1102&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESLint Extionsin의 설명도 같이 살펴보면 해당 워크스페이스에서 ESLint 가 설치되어 있는지 확인하고, 없으면 글로벌 ESLint 를 참조한다. 그리고 필요에 따라 .eslintrc 파일이 필요할 수도 있다고 설명하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESLint 를 사용하려면 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;ESLint Library&lt;/span&gt;만 설치하거나 ESLint extension만 설치 하는 것이 아니라 둘다 설치 및 세팅이 되어 있어야 한다.&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;.eslintrc 구성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://eslint.org/docs/latest/user-guide/configuring/configuration-files&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;eslint Doc&lt;/a&gt; 에 나와 있는 example 을 살펴보자&lt;/p&gt;
&lt;pre id=&quot;code_1665554199116&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// .eslintrc
{
    &quot;root&quot;: true,
    &quot;extends&quot;: [
        &quot;eslint:recommended&quot;,
        &quot;plugin:@typescript-eslint/recommended&quot;
    ],
    &quot;parser&quot;: &quot;@typescript-eslint/parser&quot;,
    &quot;parserOptions&quot;: { &quot;project&quot;: [&quot;./tsconfig.json&quot;] },
    &quot;plugins&quot;: [
        &quot;@typescript-eslint&quot;
    ],
    &quot;rules&quot;: {
        &quot;@typescript-eslint/strict-boolean-expressions&quot;: [
            2,
            {
                &quot;allowString&quot; : false,
                &quot;allowNumber&quot; : false
            }
        ]
    },
    &quot;ignorePatterns&quot;: [&quot;src/**/*.test.ts&quot;, &quot;src/frontend/generated/*&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;b&gt;root&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;default 값은 true, 값이 true가 아니면, eslintrc 파일을 찾을 때, 해당 프로젝트 디텍토리 뿐 아니라, 내 PC의 root 파일 시스템 root 디텍토리까지 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;extends&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eslint rule 설정이 저장되어 잇는 외부 file을 extends 하는 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;extends에 eslint:recommended, plugin:@typescript-eslint/recommended를 추가하면, 사용하려는 해당 플러그인에서 기본적으로 제공하는 rule set이 적용됩니다.&lt;/p&gt;
&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;parser&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 코드 파일을 검사할 파서를 설정합니다. 기본 설정은 Espree이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Babel 과 함께 사용되는 파서로는 babel-eslint 가 있고 Typescript 구문 분석을 위해 사용되는 @typescript-eslint/parser 가 있습니다.&lt;/p&gt;
&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;parserOptions&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESLint 사용을 위해 지원하려는 javascript 언어 옵션을 지정할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ecmaVersion: 사용할 ECMAScript 버전을 설정&lt;/li&gt;
&lt;li&gt;sourceType: parser의 export 형태를 설정&lt;/li&gt;
&lt;li&gt;ecmaFeatres: ECMAScript의 언어 확장 기능을 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;globalReturn: 전역 스코프의 사용 여부 (node, commonjs 환경에서 최상위 스코프는 module)&lt;/li&gt;
&lt;li&gt;impliedStric: strict mode 사용 여부&lt;/li&gt;
&lt;li&gt;jsx: ECMAScript 규격의 JSX 사용 여부&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;plugins&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESLint는 서드파티 플러그인을 지원합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 필요한 각 플러그인들은 npm 이나 yarn을 통해서 설치 하고, 해당 플러그인을 plugins 에 추가하여 사용할 수 있습니다.&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;plugin 종류 몇가지를 살펴보겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-style=&quot;style2&quot;&gt;eslint-config-airbnb-base: 에어비엔비 린트 플러그인&lt;/li&gt;
&lt;li data-ke-style=&quot;style2&quot;&gt;eslint-config-next: Next.js 전용 린트 플러그인&lt;/li&gt;
&lt;li data-ke-style=&quot;style2&quot;&gt;eslint-plugin-react: 리액트 전용 플러그인&lt;/li&gt;
&lt;li data-ke-style=&quot;style2&quot;&gt;eslint-plugin-prettier: 린트 위에 사용할 프리티어 플러그인&lt;/li&gt;
&lt;li data-ke-style=&quot;style2&quot;&gt;eslint-config-prettier: 린트 설정과 중복되는 부분이 있으면 프리티어 룰에서 제외하는 플러그인&lt;/li&gt;
&lt;li data-ke-style=&quot;style2&quot;&gt;@typescript-eslint/eslint-plugin: : 타입스크립트 전용 린트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 @typescript-eslint/eslint-plugin을 사용 할 려면, eslintrc 파일의 plugins 배열에 해당 모듈에서 제공하는 @typescript-eslint를 추가하고, 다른 모듈도 같이 쓸거면 배열에 같이 추가하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인을 추가할 때,&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp; eslint-plugin-&amp;nbsp;&lt;/span&gt; 접두사를 생략 할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;rules&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 lint rule을 적용합니다.&amp;nbsp;extends로 자동설정된 rules 중에 특정 rule을 끄거나 변경하여 설정을 바꿀 수 있습니다.&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;&quot;off&quot; 또는 0: 규칙을 사용하지 않음&lt;/li&gt;
&lt;li&gt;&quot;warn&quot; 또는 1: 규칙을 경고로 사용&lt;/li&gt;
&lt;li&gt;&quot;error&quot; 또는 2: 규칙을 오류로 사용&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;span style=&quot;background-color: #dddddd;&quot;&gt; eslint-plugin- &lt;/span&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;b&gt;ignorePatterns&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ignorePatterns 필드 또는 eslintignore 파일을 작성하여 파일 및 디텍토리를 제외하도록 지정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그외 옵션들&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;processor&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 형식의 파일로 부터 Javascript 코드를 추출해내고, 추출한 코드를 대상으로 Lint를 수행하는 전처리기와 후처리기를 작성하는 용도로 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1665566983529&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;plugins&quot;: [a-plugin],
&quot;processor&quot;: &quot;a-plugin/a-processor&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많이 사용되는 processor로 eslint-plugin-markdown은 eslint 의 preprocessor 와 postprocessor 를 구현하여 markdown 문서와 문서의 Javascript 코드블록에 대해서 정적분석을 수행하고 린팅을 적용합니다.&lt;/p&gt;
&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;env&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전 정의된 전역 변수 사용을 정의 합니다. 자주 사용되는 설정으로는 browser, node 가 있습니다. (사전 정의된 전역변수는 공식 문서에서 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1665567271149&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;env&quot;: {
  &quot;browser&quot;: true,
  &quot;node&quot;: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;browser, node 설정을 하지 않을 경우 console, require 같은 사전 정의된 전역변수 환경에 있는 static 메서드를 인식할 수 없어서 에러가 발생합니다. 이외에도 선언되지 않은 전역변수를 사용하는 경우 ESLint 경고가 발생하지 않도록, globals 를 이용하여 사용자 전역 변수를 추가할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1665567466056&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;globals&quot;: {
  &quot;$&quot;: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# &lt;a href=&quot;https://eslint.org/play/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ESLint 데모&lt;/a&gt;를 사용하면 UI를 이용해서 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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Prettier Setting&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prettier를 세팅하는 방법은 2가지가 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. 별도의 Prittier 관련 플러그인을 npm, yarn으로 설치하지 않고 VSCode의 extiension을 설치&lt;br /&gt;2. Prettier 플러그인을 직접 설치 후 eslintrc에 세팅&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 방법은 VSCode 에디터 자체에 prettier rule 을 세팅하는 것입니다. 따라서 내 환경의 VSCode에서만 해당 Prettier 방식이 적용 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 방법은 프로젝트 자체에 prettier rule 을 세팅하는 것으로, 해당 프로젝트를 다른 환경에서 돌려도 동일하게 prettier rule을 적용해서 사용할 수 있는 방식입니다.&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;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;VSCode의 Prettier extiension 설치 (Prettier - Code formatter)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Preitt&lt;span&gt;ier - Code fomatter 는 설치하는 순간 바로 적용됩니다. 상세한 설정 방식은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/getstarted/settings&quot;&gt;docs&lt;/a&gt;를 참고 하시면 자세히 안내되어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-10-13 오후 5.53.02.png&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;1028&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7Re4k/btrOxnMuh22/iKnKFjmTc9nJeRIhBQhLOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7Re4k/btrOxnMuh22/iKnKFjmTc9nJeRIhBQhLOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7Re4k/btrOxnMuh22/iKnKFjmTc9nJeRIhBQhLOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7Re4k%2FbtrOxnMuh22%2FiKnKFjmTc9nJeRIhBQhLOK%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;1636&quot; height=&quot;1028&quot; data-filename=&quot;스크린샷 2022-10-13 오후 5.53.02.png&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;1028&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cmd + shift + p 를 눌러서 검색 창에 Open User Setting으로 들어가서 prettier를 검색하면, 설정할수 있는 리스트가 나타납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCose Settings 는 현재 PC의 VSCode 환경 세팅이기 때문에 VSCode Extension으로 설치한 Prettier 플러그인에만 적용이 가능하고, npm 이나 yarn 으로 직접 설치한 prettier 플러그인에는 적용되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접설치 했다면 &lt;b&gt;'반드시 .prettierrc'&lt;/b&gt; 파일을 이용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;적용 우선 순위&lt;/b&gt;&lt;br /&gt;VSCode Extension 으로 설치 한 경우에는 VSCode Settings 와 .prettierrc 파일 둘다에서 설정이 가능하지만, .prettierrc 파일이 있으면 VSCode Settings의 설정은 무시되고 .prettierrc&amp;nbsp;파일의 룰이 우선 적용됩니다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;직접 설치 (npm 또는 Yarn)&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;Prettier 설치&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1665639031560&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install -D prettier
// or
$ yarn add -D prettier&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 설정 방식은 &lt;a href=&quot;https://prettier.io/docs/en/install.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Prettier Docs&lt;/a&gt; 를 참고 하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;자동 저장 Setting&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장 시 자동으로 린트를 잡아주는 기능은 VSCode 가 동작하는 기능입니다.&amp;nbsp; 따라서 VSCode Settings 에서 설정을 해주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cmd + shift + p를 누르고 Open Settins.json 을 선택하면 Settings 설정이 JSON 형식으로 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 자동 저장 기능을 설정하는 옵션이 두가지가 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;editor.codeActionsOnSave&lt;/h4&gt;
&lt;pre id=&quot;code_1665639739020&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;editor.codeActionsOnSave&quot;: {
  &quot;source.fixAll.eslint&quot;: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이밍에서 알 수 있듯이 Save 할 때 코드를 동작하고, 코드 단에서 eslint rule 에 의해 error 로 판단되는 부분들을 lint rule 에 맞게 알아서 수정을 해주는 설정입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;editor.formatOnSave&lt;/h4&gt;
&lt;pre id=&quot;code_1665639895441&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;editor.formatOnSave&quot;: true,
&quot;editor.defaultFormatter&quot;: &quot;esbenp.prettier-vscode&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설정은 Save 할때 VSCode extension으로 설정된 Prettier 환경에 맞게 코드를 수정해주는 옵션입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'editor.codeActionsOnSave'와 다르게 ESLint 에러를 보고 수정하는 것이 아니라 에디터에 있는 텍스트들을 prettier rule에 맞게 정리해줍니다.&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;Reference&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/ESLint&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.wikipedia.org/wiki/ESLint&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://helloinyong.tistory.com/325&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://helloinyong.tistory.com/325&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@kyusung/eslint-config-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@kyusung/eslint-config-2&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Setting</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/26</guid>
      <comments>https://mishka.tistory.com/26#entry26comment</comments>
      <pubDate>Thu, 13 Oct 2022 15:30:33 +0900</pubDate>
    </item>
    <item>
      <title>이것만 알고가자 피그마 (feat. 개발자)</title>
      <link>https://mishka.tistory.com/25</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;피그마(Figma)는?&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;클라우드 기반으로 웹 브라우저로 동작하는&lt;b&gt; UI 디자인 툴&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피그마는 웹 브라우저 기반이기때문에 작업 환경에 대한 걱정 없이 사용할수 있습니다. 다시 말하자면 설치할 필요가 없고 운영체제와 상관 없이 사용할 수 있다는 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 피그마는 디자인이 클라우드에 있고 고유한 URL 이 존재하기 때문에 개발자가 항상 최신 버전의 디자인을 빠르게 확인 할 수 있고(사실 개발자 뿐만 아니라 사용하는 사용자 모두) 내장된 여러가지 기능을 통해 디자이너와 개발자 사이에 커뮤니케이션을 원할하게 해줍니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;The design lives in the cloud and has a unique URL, so it serves as the source of truth for the entire team. -&amp;nbsp;&lt;br /&gt;&lt;a href=&quot;https://www.figma.com/blog/under-the-hood-of-figmas-infrastructure/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Under the hood of Figma&amp;rsquo;s infrastructure&lt;/a&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;개발자 핸드오프 (Handoff)&lt;/b&gt;&lt;/h3&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;디자인팀은 개발팀에게 대량의 디자인 이미지와 추출한 애셋파일 그리고 여러 페이지의 워드 문서를 첨부해서 공유 했습니다. 거기에 디자인 파일에 접근하기 위해 비싼 디자인 소프트웨어의 라이센스가 필요했습니다. 그리고 디자인이 새롭게 업데이트 되면 관련된 모든 사람에게 변경 내용을 공유해야 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 피그마 이전에 제플린, 인비전 같은 툴의 발전으로 이전보다 과정이 간소화 되고, 개발자들이 디자인파일에 대해 더 좋은 접근 권한을 가지게 되었습니다. 사용자들이 최신 버전의 디자인을 쉽게 찾을수 있게 되었지만 디자이너들은 아직도 별도의 툴을 써야 했고 최신 버전을 맞추기 위해 추가적인 작업이 필요했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 피그마는 피그마 하나로 전체 디자인, 이미지 파일, 폰트, 간격, 사용자 인터렉션 등 많은 것들을 전달 할 수가 있습니다. 또한 코멘트를 주고 받을수도 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;처음 피그마를 접하게 되면 브라우저로 열지, 데스크탑 용 앱을 다운로드 할지 선택 할 수 있습니다. (윈도우용, 맥OS용 모두 지원하고 있습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데스크탑용 앱은 네이티브로 개발된 것은 아니고 크로스 플랫폼을 지원하는 일렉트론 앱 입니다. 브라우저와 데스크탑 버전의 기능은 데스크탑 앱은 로컬 폰트 지원이 내장되어 있는 반면 브라우전 버전은 로컬 폰트를 사용하기 전에 피그마 폰트 도우미(&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;Figma Font Helper&lt;/span&gt;)를 설치 해야 한다는 것을 제외하면 대부분 동일합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피그마의 인터페이스는 크게 세 부분으로 나뉘어 있습니다.&lt;/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;오른쪽에는 파일요소에 대한 정보를 볼수 있는 &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;&amp;nbsp;&lt;/p&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화면 확대/축소는&lt;span style=&quot;background-color: #dddddd;&quot;&gt; &lt;span style=&quot;color: #222222;&quot;&gt;Cmd ⌘&lt;/span&gt;(ctrl) + 마우스 휠&lt;/span&gt;을 하거나 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp;+&amp;nbsp;&lt;/span&gt; 나&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt; -&amp;nbsp;&lt;/span&gt; 키를 눌러서 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;화면 이동은 스페이스바 + 마우스 드래그로 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;빠른 확대는 단일 프레임이나 요소를 선택한 상태에서&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt; Shift + 2 &lt;/span&gt;를 누르면 됩니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;캔버스에 모든 요소가 보이도록 하는 빠른 전체화면은&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt; Shift + 1 &lt;/span&gt;을 누르면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그외 단축키 들은&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt; Ctrl + Shift + ?&amp;nbsp;&lt;/span&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;피그마에서는 다양한 파일 형식으로 이미지를 추출 할 수 있다. 원하는 요소를 선택하면 오른쪽 툴바 영역에서 요소에 대한 정보들이 뜹니다. 그 중 아래에 있는 내보내기(export) 버튼을 통해서 저장 할수 있습니다. 또한 이미지를 배수로 저장할 수도 있고, 파일 유형을 선택 할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;원하는 요소를 오른쪽 클릭해서 빠르게 추출 할 수 도 있고, 오른쪽 클릭 후 표시되는 Copy/Paste 메뉴에서 선택해서 이미지나 SVG 코드로도 복사할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추출 가능한 모든 이미지를 한 번에 내보낼 수도 있습니다. 왼쪽 상단의 피그마 메인메뉴 &amp;gt; File &amp;gt; Export 를 클릭하거나&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Shift + &lt;span style=&quot;color: #222222;&quot;&gt;Cmd ⌘ + e&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #222222;&quot;&gt; 누르면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;그럼 파일에서 추출할 애셋으로 표시한 모든 애셋 항목이 목록으로 표시됩니다. 표시된 목록의 이미지 파일 유형과 수치를 확인 하고 필요없는 파일은 제외 할수 있으며, 썸네일 이미지에 마우스를 올려놓으면 저장 할 때의 파일명과 유형을 확인 할 수 있습니다.&lt;/span&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;span style=&quot;color: #222222;&quot;&gt;디자인 요소 정보 확인&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;디자인 요소를 클릭하면 최상위 단계의 요소만 선택됩니다. 특정 레이어를 선택하려면&amp;nbsp; &lt;span style=&quot;background-color: #dddddd; color: #222222;&quot;&gt;&amp;nbsp;Cmd ⌘ &lt;/span&gt;&lt;span style=&quot;color: #222222;&quot;&gt;를 누른 상태에서 클릭하거나 요소를 오른쪽 클릭해서 모든 중첩된 레이어를 볼 수 있는 메뉴를 열어 원하는 레이어를 선택 할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;만일 요소를 더블 클릭하면 더블 클릭할 때마다 한 단계 아래의 요소가 선택됩니다. 원하는 항목을 선택할 때까지 계속 들어갈 수 있습니다. 요소를 선택하면 오른쪽 툴바영역에서 &lt;b&gt;Inspect&lt;/b&gt;에 &lt;b&gt;Code&lt;/b&gt;에서 선택한 요소에 대한 CSS 정보를 볼 수 있습니다. CSS 정보는 자동으로 생성되는 것으로 표시되는 정보가 정확하지 않을 수 있으니 가이드 정도로 이용하는 것이 좋습니다.&lt;/span&gt;&lt;/span&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;span style=&quot;color: #222222;&quot;&gt;요소들의 간격 확인&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;디자인 요소와 요소 사이의 거리를 측정하고 싶을때는 시작점의 요소를 선택한 후&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt; Option(Alt)&amp;nbsp;&lt;/span&gt; 키를 누른 상태로 마우스를 다은 요소에 올리면 치수를 측정할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/432462346&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/KOC9m/hyP0zw11oG/U1SD0EBXyqKQbD1pJuKAY1/img.jpg?width=1304&amp;amp;height=720&amp;amp;face=0_0_1304_720,https://scrap.kakaocdn.net/dn/cktkjd/hyP0ykA1jv/FHMp2ihjkWZW1lQ7rY9k9K/img.jpg?width=1304&amp;amp;height=720&amp;amp;face=0_0_1304_720&quot; data-video-width=&quot;500&quot; data-video-height=&quot;276&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;475&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/432462346?service=daum_tistory&quot; width=&quot;500&quot; height=&quot;276&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;Measuring distance between elements&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피그마는 요소 사이의 거리를 빨간 선으로 표시하고 거리를 픽셀 단위로 보여줍니다. 다른 그룹 또는 프레임의 특정 하위 요소 까지의 거리를 측정하려면 해당 내부 요소를 선택하는 것과 마찬가지로 &lt;span style=&quot;background-color: #dddddd; color: #222222;&quot;&gt;Cmd ⌘ &lt;/span&gt;&lt;span style=&quot;color: #222222;&quot;&gt;키를 누르면 됩니다.&lt;/span&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;span style=&quot;color: #222222;&quot;&gt;코멘트 기능&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;코멘트 기능을 활용하여 디자이너와 소통 할수 있습니다. 상단 툴바에 있는 채팅버플 모양의 아이콘을 클릭하거나&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt; C&amp;nbsp;&lt;/span&gt; 를 눌러서 코멘트를 달수 있습니다. 클릭한 상태에서 드래그 하면 영역을 지정해서 코멘트를 달수도 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;코멘트를 모두 작성하고 나면&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt; V &lt;/span&gt;&amp;nbsp;또는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp;Esc&amp;nbsp;&lt;/span&gt; 를 눌러서 다시 일반 커서 모드로 돌아오면 됩니다. 댓글을 달때 알림을 원한다면 &lt;b&gt;@&lt;/b&gt; 를 활용해서 멘션을 달아주면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;코멘트를 읽고 더 이상 필요없거나 구현이 완료되면 &lt;b&gt;완료(resolve) 표시&lt;/b&gt;를 눌러서 코멘트가 쌓이는 것을 방지 할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #222222;&quot;&gt;Referense&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;a href=&quot;https://help.figma.com/hc/en-us&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://help.figma.com/hc/en-us&lt;/a&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://webactually.com/2021/01/18/%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%ED%94%BC%EA%B7%B8%EB%A7%88%EC%9D%98-%EB%AA%A8%EB%93%A0-%EA%B2%83/&quot;&gt;개발자가 알아야 할 피그마의 모든 것 - Webactually&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://onlydev.tistory.com/142&quot;&gt;피그마 사용법과 협업하기(개발자 시점)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>figma</category>
      <category>개발자</category>
      <category>피그마</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/25</guid>
      <comments>https://mishka.tistory.com/25#entry25comment</comments>
      <pubDate>Mon, 3 Oct 2022 22:28:57 +0900</pubDate>
    </item>
    <item>
      <title>이름 정의 규칙 (Naming Convention)</title>
      <link>https://mishka.tistory.com/24</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. CamelCase&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1-1. UpperCamelCase&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;규칙자체 이름에서 보여지듯 식별자의 첫단어는 대문자(U)로 지정되어 있고, 그 뒤에 Camel, Case 같이 서로 상이한 단어들을 구분짓기 위해 각 단어의 시작을 대문자로 표기한다. 나머지 문자는 모두 소문자로 표기한다.&lt;/p&gt;
&lt;pre id=&quot;code_1653975927605&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//예시
UserName
BackgroundColor&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;1-2. lowerCamelCase&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 식별자의 첫번째 문자는 소문자로 표기해야 하며, 나머지 규칙은 UpperCamelCase 와 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규칙자체 이름에서 보여지듯 식별자의 첫 단어를 소문자(l)f로 지정하였고, 나머지 규칙은 UpperCamelCase 와 동일하다.&lt;/p&gt;
&lt;pre id=&quot;code_1653976089859&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//예시
userName
backgroundColor&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;&lt;b&gt;kebab-case&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단어와 단어 사이에 하이픈(-)을 사용하여 구분한다. 케밥이 꼬챙이에 꽃힌 모습과 비슷하다하여 케밥케이스라 명명되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1653976534007&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 예시
user-name
background-color&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;&lt;b&gt;Snake_Case&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단어와 단어 사이에 언더바(_)를 사용하여 구분한다. 언더바가 들어있는 표현 방식이 뱀처럼 생겼다고 하여 스네이크 케이스라 명명되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1653981953611&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 예시
user_name
background_color&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Programming</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/24</guid>
      <comments>https://mishka.tistory.com/24#entry24comment</comments>
      <pubDate>Tue, 31 May 2022 14:48:47 +0900</pubDate>
    </item>
    <item>
      <title>Pinia - Vuex 를 대체 할 수 있을까??</title>
      <link>https://mishka.tistory.com/23</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&amp;nbsp;Pinia&amp;nbsp;&lt;/b&gt;&lt;/span&gt; 는 Vue의 새로운 상태 관리 라이브러리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pinia 는 2019년 11월경 Composition API 를 사용하여 Store for Vue가 어떻게 생겼는지 재설계하기 위한 실험으로 시작되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후 초기 원칙은 동일하지만&amp;nbsp; Pinia 는 Vue2와 Vue3 모두에서 작동하며 Composition API 를 사용할 필요는 없습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VueConf Toronton 2021 에서 Vue의 창시자 Evan You가 상태 관리 플러그인으로 vuex가 아닌 Pinia를 추천&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Pinia([piːnjʌ] 영어로 &quot;peenya&quot; 로 발음됨)&amp;nbsp;는 유효한 패키지 이름인 pi&amp;ntilde;a(스페인어로 파인애플) 에 가장 가까운 단어입니다.&amp;nbsp;&lt;br /&gt;파인애플은 실제로 여러개의 과일을 만들기 위해 결합된 개별 꽃의 그룹입니다. Store 와 마찬가지로 하나하나가 개별적으로 태어나지만 격국에는 모두 연결되어 있음을 의미합니다.&lt;/blockquote&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;Pinia를 사용해야 하는 이유?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pinia 는 Vue의 구성 요소/페이지 간에 상태를 공유할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Composition API 에 익숙하다면 아래와 같은 형태의 간단한 내보내기로 전역 상태를 공유 할수 있다고 생각 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1652330683092&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const state = reative({})&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;그러나 작은 단일 페이지 어플리케이션에서도 Pinia 를 사용하면 많은 것을 얻을 수 있습니다.&lt;/p&gt;
&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;Mutations 이 없습니다.&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;상태변경을 위해 mutations 를 정의하고 commit 하는 과정이 필요없습니다.&lt;/li&gt;
&lt;li&gt;vue 인스턴스의 state 값을 변경 할 때 처름 read/write 하면됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Typescipt 를 지원&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;Typescipt 를 통한 유형 추론을 최대한 활용할 수 있게 만들어져 있어서 복잡한 래핑을 하지 않아도 된다.&lt;/li&gt;
&lt;li&gt;타입을 별도로 지정해주지 않아도 타입 추론이 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Namespace modules 없음&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Devtools 지원&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;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Pinia 와 Vuex의 다른점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp;&lt;b&gt;Pinia&lt;/b&gt;&amp;nbsp;&lt;/span&gt; 의 Store 선언 구문&lt;/p&gt;
&lt;pre id=&quot;code_1652344693229&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () =&amp;gt; ({ count: 0 }),
  actions: {
    increment() {
      this.count++
    }
  },
  getters: {
    doubleCount(state) {
      return state.count * 2
    }
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vuex 와의 가장 큰 차이점이라고 할 수 있는 것은&amp;nbsp; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp;&lt;b&gt;mutations&lt;/b&gt;&amp;nbsp;&lt;/span&gt; 의 유무이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vuex 에 존재하던 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&amp;nbsp;mutations&lt;/b&gt;&amp;nbsp;&lt;/span&gt; 선언 필요 없이 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp;actions&amp;nbsp;&lt;/span&gt;&lt;/b&gt; 에서 값을 변화 시킬 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 때는 아래와 같은 형태로 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1652345282647&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useCounterStore } from '@/stores/counter'

export default {
  setup() {
    const counter = useCounterStore()

    const onClickAdd = () =&amp;gt; {
      // actions 를 직접선언
      counter.increment()
      // 이런식으로 Composition API 사용하는식으로 사용도 가능
      counter.count++
      // 내부 API 를 사용 가능하고 (with autocompletion ✨)
      counter.$patch({ count: counter.count + 1 })
      
    }
    
    return {
      onClickAdd,
      doubleValue: computed(() =&amp;gt; counter.doubleCount),
    }
  }
}&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;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt; &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Composition API&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에서 사용형태와 비슷하며 쉽고 간편하게 Store 에 접근이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&amp;nbsp;Vuex&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에서 Store 정의하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp;mutation&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&amp;nbsp;actions&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt; getters&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&amp;nbsp; 순서대로 작성하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&amp;nbsp;dispatch&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;만약 computed 선언을 하지 않고 구조분해할당을 하고 싶다면&lt;/p&gt;
&lt;pre id=&quot;code_1652680278980&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// App.vue
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'

export default {
  setup() {
    const counter = useCounterStore()
    const { doubleCount } = storeToRefs(counter)

    const onClickAdd = () =&amp;gt; {
      counter.count++
    }
    
    return {
      onClickAdd,
      doubleCount
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 하면 쉽고 간편하게 사용할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Vue3의 반응형 시스템 내부에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment&quot;&gt;구조분해할당&lt;/a&gt;을 사용하는 경우 반응형이 작동하지 않기 때문에 Vue3 에서 구조분해할당을 사용하시 위해 반응형 객체를 toRefs 로 묶어서 반응형을 유지할 수 있도록 지원한다.&lt;br /&gt;&lt;a href=&quot;https://v3.ko.vuejs.org/guide/reactivity-fundamentals.html#%E1%84%87%E1%85%A1%E1%86%AB%E1%84%8B%E1%85%B3%E1%86%BC%E1%84%92%E1%85%A7%E1%86%BC-%E1%84%89%E1%85%A1%E1%86%BC%E1%84%90%E1%85%A2-%E1%84%80%E1%85%AE%E1%84%8C%E1%85%A9-%E1%84%87%E1%85%AE%E1%86%AB%E1%84%92%E1%85%A2%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5-destructuring&quot;&gt;참조&lt;/a&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;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp;Composition API &lt;/span&gt;&lt;/b&gt;&amp;nbsp;에 익숙 하다면 아래와 같은 형태로 사용할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1652680278981&quot; class=&quot;typescript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// stores/counter.js
export const useCounterStore = defineStore('counter', () =&amp;gt; {
  const count = ref(0)
  function increment() {
    count.value++
  }
  const doubleCount = computed(() =&amp;gt; count.value * 2)

  return { count, increment, doubleCount }
})&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;b&gt;마치며&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 상태관리 라이브러리인 vuex 와 비교를 해보았을때 Pinia 가 매력적인 몇가지 사항들이 있는데, mutations 가 없어진 것과 Typescript 지원 그리고 devtools 의 공식 지원이다. 이번 새로운 프로젝트에 Pinia 도입 해서 사용해 보았는데 Composition API, Typescript 과의 궁합도 잘맞았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vuex 와 동시 사용도 가능 하다고 하니 다들 한번 사용해 보시면 좋을것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;참조&lt;br /&gt;&lt;/b&gt;&lt;a href=&quot;https://pinia.vuejs.org/introduction.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;피니아&lt;/a&gt; &lt;a href=&quot;https://pinia.vuejs.org/introduction.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://velog.io/@eggplantiny/Pinia-Vuex-%EB%A5%BC-%EB%8C%80%EC%B2%B4%ED%95%A0-%EC%83%88%EB%A1%9C%EC%9A%B4-Store&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Pinia - Vuex 를 대체할 새로운 Store!&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://intrepidgeeks.com/tutorial/pinia&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Pinia&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://blog.logrocket.com/pinia-vs-vuex/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Pinia vs. Vuex: Which state management library is best for Vue?&lt;/a&gt;&lt;/p&gt;</description>
      <category>Vue</category>
      <category>composition api</category>
      <category>devtools</category>
      <category>mutations</category>
      <category>pinia</category>
      <category>typescript</category>
      <category>Vuex</category>
      <category>피니아</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/23</guid>
      <comments>https://mishka.tistory.com/23#entry23comment</comments>
      <pubDate>Mon, 16 May 2022 14:51:34 +0900</pubDate>
    </item>
    <item>
      <title>Vue에서 Moment.js 사용하기(vue-moment)</title>
      <link>https://mishka.tistory.com/21</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 날짜 또는 시간을 다루기 위해서는 기본적으로 Date 객체를 사용합니다.&lt;br /&gt;이를보다 편리하고 간단하게 활용할수 있는 라이브러리에 대해 정리해 보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Moment.js는 Date 형식의 데이터 파싱,검증,조작 등을 간편하게 할수 있게 해주는 유용한 라이브러리이다.&lt;br /&gt;이를 Vue에서 사용하기 쉽도록 수정 배포된 버전이 vue-moment 이다.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install vue-moment&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;Vue.use(require('vue-moment'))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;require를 사용하지 않는다면&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;import VueMoment from 'vue-moment'
Vue.use(VueMoment)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위과 같이 설치하고 나면 moment를 호출하여 사용할 수 있다.&lt;br /&gt;&lt;code&gt;Vue.$moment&lt;/code&gt; 의 형태로 사용한다. 스크립트에서는 &lt;code&gt;this.$moment&lt;/code&gt; 탬플릿에서는 &lt;code&gt;$moment&lt;/code&gt; 의 형태로 사용한다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;// 현재시간
{{$moment().format('YYYY-MM-DD')}} 

// 데이터 입력 시간
{{$moment(time).format('YYYY-MM-DD')}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;필터링(filtering) 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;format(포맷팅)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;format 을 사용해서 원하는 형태의 시간정보를 생성할수 있다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;moment().format();                                // &quot;2014-09-08T08:02:17-05:00&quot; (ISO 8601, no fractional seconds)
moment().format('YYYY-MM-DD');                    // &quot;2014-09-08&quot;
moment().format('dddd, MMMM Do YYYY, h:mm:ss a'); // &quot;Sunday, February 14th 2010, 3:25:50 pm&quot;
moment().format('ddd, hA');                       // &quot;Sun, 3PM&quot;
moment().format('[Today is] dddd');               // &quot;Today is Sunday&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://momentjs.com/docs/#/displaying/format/&quot;&gt;더 다양한 방법 보기 - format&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;add(더하기)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 시간에 원하는 시간을 추가하여 기존 시간을 변경할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;this.$moment(someDate).add(7, 'days')  // 7일이 추가된 시간&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://momentjs.com/docs/#/manipulating/add/&quot;&gt;더 다양한 방법 보기 - add&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;subtract(빼기)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 시간에 원하는 시간을 빼서 기존 시간을 변경할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;this.$moment(someDate).subtract(7, 'days')  // 7일 뺀 시간&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://momentjs.com/docs/#/manipulating/subtract/&quot;&gt;더 다양한 방법 보기 - subtract&lt;/a&gt;&lt;/p&gt;</description>
      <category>Vue</category>
      <category>Moment</category>
      <category>Vue</category>
      <category>Vue.js</category>
      <category>시간</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/21</guid>
      <comments>https://mishka.tistory.com/21#entry21comment</comments>
      <pubDate>Fri, 27 Aug 2021 08:45:50 +0900</pubDate>
    </item>
    <item>
      <title>늦어버린 2020년 돌아보기</title>
      <link>https://mishka.tistory.com/22</link>
      <description>&lt;p&gt;이런 저런 이유(다 핑계일뿐이다 ㅠ)로 회고를 미루고 미뤘더니 지난 회고를 쓰고 1년 6개월 이라는 시간이 지나 버렸다.&lt;br&gt;더 늦기전에 되돌아보고자 한다.&lt;/p&gt;
&lt;!-- more --&gt;

&lt;h2&gt;프론트엔드 개발자로의 시작&lt;/h2&gt;
&lt;p&gt;2019년 LSP를 퇴사하고 결론적으론 퍼블리셔에서 프론트엔드 개발자로의 전직을 성공했다. 퍼블리셔도 물론 재미있는 직종이었고 업무의 방향이&lt;br&gt;확실하긴 했지만 그동안 느낌 점은 퍼블리셔로는 한계가 느껴졌다. 그래서 프로트엔드 개발자로의 전향을 꿈꾸도 이것 저것 공부를 위해 노력을 했다.&lt;br&gt;배운것들을 정리하고 공유하기 위해 시작한 기술블로그도  그 노력의 일환이었다. 노력이 통하였는지 다행히 전 직장 퇴사가 결정되고 3곳의 회사에서&lt;br&gt;러브콜이 와서 행복한 고민에 빠졌었다. 그중에 선택한곳이 현재 재직중인 &amp;#39;(주)테이블링&amp;#39; 이다. 입사할때는 (주)밀랑 이었는데 중간에 사명이 변경되었다.&lt;br&gt;테이블링은 현재 외식사업의 전반적인 서비스를 관리 하는 곳이다. 매장의 예약/대기 관리 시스템과 유저들이 외식을 간편하게 이용할 수 있도록 불편함을&lt;br&gt;줄여주는 서비스를 운영하고 있다.&lt;/p&gt;
&lt;p&gt;면접에서 대표님의 비전에 대한 열망이 정말 좋았고 회사를 운영하는 방식 같은 것들이 함께 꿈을 꾸기에 충분한 느낌을 받았다.&lt;br&gt;또한 부족한 개발 실력을 같이 할 수 있는 사수도 있어서 큰 고민없이 회사를 선택할 수 있었다. &lt;/p&gt;
&lt;p&gt;처음 겪어보는 프론트엔드 개발 환경은 많이 낯설었다. 스터디를 통해서 큰 흐름은 배웠지만 스터디와 실전의 차이는 명확했기에&lt;br&gt;약간의 두려움도 생겼다. 옆에서 사수 친절히 알려주셔서 다행히 어려움을 극복할 수 있었다.&lt;/p&gt;
&lt;h2&gt;공부는 죽을때 까지...&lt;/h2&gt;
&lt;p&gt;프론트엔드 개발자로 취업을 성공했지만 부족한 점을 많이 느끼고 있었기에 공부를 멈출수는 없었다.&lt;br&gt;아직 배워야 할것들이 많았고 처리해야하는 일은 나를 기다려주지 않았다. 업무 후에 여가시간을 모두 공부하는 시간에 투자 하였다.&lt;br&gt;기본적인 JavaScript 뿐만아니라. TypeScript, React 등등 여러가지 스터디들을 순차적으로 또 산발적으로 중간중간 필요한 것들을&lt;br&gt;찾아보면서 부족한 부분들을 보충하는 시간들을 가졌다. 개발자를 하면서 &amp;#39;공부는 늙어 죽을 때까지 해도 다 못한다&amp;#39;는 속담이 더 크게 와닿는것 같다.&lt;br&gt;배울수록 새롭게 알아가는 것도 많고 이전에 알고 있던 것들도 새롭게 다가오는 부분들도 많아서 좋은 시간들이었다. &lt;/p&gt;
&lt;h2&gt;사이드 프로젝트팀 &amp;#39;Awesome&amp;#39;&lt;/h2&gt;
&lt;p&gt;여러가지 스터디를 하던 도중에 이론적인 부분뿐만 아니라 배운 것들을 실무적으로 적용시켜보자는 마음에 사이드 프로젝트에 관심을 가지고 알아보게 되었다.&lt;br&gt;그러던 중 이전에 같이 스터디 멤버의 추천을 받아서 현재 Awesome 팀에 합류하게 되었다. 제가 참여할때는 Awesome의 2번째 프로젝트의 시작이었는데&lt;br&gt;Awesome 1번째 프로젝트는 개발자분들이 없어서 기획과 디자인 단계에서 마무리한 프로젝트였고 다음에는 실제로 구현해보고자하는 니즈가 생겨서&lt;br&gt;개발자와 함께 프로젝트를 해보자하고 구하던 찰나에 시기가 맞물려 합류하게 되었다.&lt;br&gt;팀에 합류 했을때는 이미 코로나로 모임이 힘든 시점이라 오프라인 모임이 아닌 구글밋을 통한 온라인 모임위주로 프로젝트를 진행하였다.&lt;br&gt;현재 프로젝트는 기획 마무리 단계를 거쳐 디자인작업을 하고 있다.&lt;br&gt;이 프로젝트에 관한 회고는 따로 진행하는게 좋을것 같아 자세한 사항은 완성된 후에 정리해 보겠다. &lt;/p&gt;
&lt;p&gt;약 1년 6개월의 시간을 돌이켜 보면 많은것들을 한것 같기는 한데 또 돌이켜보면 부족한 부분들도 많이 보이는것 같아서 아쉬운 부분이 생긴다.&lt;br&gt;가장 큰 사건은 프로트엔드개발자로서 전직을 무사히 성공했고 성장해 나가고 있다는 데에서 스스로에서 박수를 보내고 싶다.&lt;br&gt;아직은 부족한 점이 많은 개발자 이지만 계속 성장해 나감으로써 스스로에게 부끄럽지 않은 모습이 되었으면 좋겠다.&lt;/p&gt;</description>
      <category>회고</category>
      <category>Awesome</category>
      <category>회고</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/22</guid>
      <comments>https://mishka.tistory.com/22#entry22comment</comments>
      <pubDate>Thu, 26 Aug 2021 16:44:37 +0900</pubDate>
    </item>
    <item>
      <title>Webstorm 과 jira 연동해서 사용하기</title>
      <link>https://mishka.tistory.com/20</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;webstorm을 사용하고 업무를 진행할 때 &lt;code&gt;jira&lt;/code&gt;를 사용하다보니 jira를 별도의 창으로 띄워놓고 이슈를 생성하고, webstorm에서 작업하다가 커밋하면서&lt;br /&gt;jira 티켓번호 생성하고 푸시한 다음에 티켓을 이동시켜주고 하는 반복작업이 많아졌습니다. 어떻게 보면 별거아닌 작업일 수 있지만 까먹게 되는 일이&lt;br /&gt;많고 바쁠때는 모아서 하기도 하다보니 이슈 트레킹 하는데 문제가 있어보여 찾아보다가 연동하는 방법을 찾아 정리해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단축키는 &lt;code&gt;Mac OS&lt;/code&gt;를 기준으로 정리하였습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;webstorm에서 &lt;code&gt;preferences &amp;gt; Tasks &amp;gt; Servers&lt;/code&gt; 로 이동합니다. 화면에서 &lt;b&gt;+&lt;/b&gt; 버튼을 클릭해서 &lt;b&gt;JIRA&lt;/b&gt;를 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;723&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPsaUE/btrdkaPNu98/n8zLPtsuFdwbhMiVOJkMR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPsaUE/btrdkaPNu98/n8zLPtsuFdwbhMiVOJkMR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPsaUE/btrdkaPNu98/n8zLPtsuFdwbhMiVOJkMR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPsaUE%2FbtrdkaPNu98%2Fn8zLPtsuFdwbhMiVOJkMR1%2Fimg.png&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;723&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JIRA 외에 다른 이슈 트레커들도 연동해서 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JIRA 버튼을 누르면 설정창이 뜨는데 설정창에서 Server URL 에는 JIRA 주소를 입력합니다. 그리고 계정/비밀번호를 입력해 줍니다.&lt;br /&gt;저는 회사계정으로 입력했더니 비밀번호 입력하는 칸이 &lt;code&gt;API Token&lt;/code&gt; 을 입력하는 칸으로 변경되더라구요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;703&quot; data-origin-height=&quot;206&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YkqEo/btrdffq2Mxy/zXGQq58QDSTVBckIrT0Dvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YkqEo/btrdffq2Mxy/zXGQq58QDSTVBckIrT0Dvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YkqEo/btrdffq2Mxy/zXGQq58QDSTVBckIrT0Dvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYkqEo%2Fbtrdffq2Mxy%2FzXGQq58QDSTVBckIrT0Dvk%2Fimg.png&quot; data-origin-width=&quot;703&quot; data-origin-height=&quot;206&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 토큰 생성 방법은 atlassian 사이트를 참고 하셔서 발급 받으시면됩니다. -&lt;a href=&quot;https://confluence.atlassian.com/cloud/api-tokens-938839638.html&quot;&gt;사이트 링크&lt;/a&gt;&lt;br /&gt;API token은 한번 발급 받으면 다시 볼수 없으니 계속해서 사용하고 싶으시면 개인저장소에 저장해놓거나 잃어버리면 새로 발급 받아야합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Search 입력란은 Task 목록을 방식을 설정하는 방식을 이야기하는데 기본값으로 최신정렬순으로 되어있으니 필요하신분만 수정하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;720&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmMf3z/btrdcOHKzg1/sTvoKZVBh9gSk5TZZcSqDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmMf3z/btrdcOHKzg1/sTvoKZVBh9gSk5TZZcSqDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmMf3z/btrdcOHKzg1/sTvoKZVBh9gSk5TZZcSqDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmMf3z%2FbtrdcOHKzg1%2FsTvoKZVBh9gSk5TZZcSqDk%2Fimg.png&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;720&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정이 끝나면 우측 하단에 있는 &lt;code&gt;Test&lt;/code&gt; 버튼을 클릭하여 테스트를 해볼수 있습니다. &lt;b&gt;Connection is successful&lt;/b&gt; 이라는 메세지가 나오면 됩니다.&lt;br /&gt;그리고 나서 Apply &amp;gt; OK 눌러주시면 설정은 끝났습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용해본 결과 현재(2020.03.30일 기준 2019.3 버전) webstorm에서는 JIRA 티켓 생성은 지원하고 있지 않았습니다.&lt;br /&gt;기존에 되어있는 티켓을 Task 가져오는게 아니라면 티켓 생성은 JIRA 사이트에서 해주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Tools &amp;gt; Tasks &amp;amp; contexts &amp;gt; Open Task&lt;/code&gt; 로 &lt;b&gt;본인에게 할당된 티켓 목록&lt;/b&gt;을 볼 수 있습니다. 단축키는 &lt;code&gt;option+shift+n&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;369&quot; data-origin-height=&quot;217&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lWt22/btrdevVd7rR/wda0PoxkjOLm52tlIAuzj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lWt22/btrdevVd7rR/wda0PoxkjOLm52tlIAuzj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lWt22/btrdevVd7rR/wda0PoxkjOLm52tlIAuzj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlWt22%2FbtrdevVd7rR%2Fwda0PoxkjOLm52tlIAuzj1%2Fimg.png&quot; data-origin-width=&quot;369&quot; data-origin-height=&quot;217&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;img src=&quot;/img/webstorm/jira-ticket.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 위해 티켓을 별도로 하나 생성했습니다. FE-66 티켓을 선택해 주면 아래와 같은 창이 열립니다.&lt;br /&gt;update issue state는 티켓의 state를 원하는 상태로 변경할 수 있습니다. clear current context는 현재 편집기에 열려있는 모든 탭을 닫습니다.&lt;br /&gt;create branch 를 하면 원하는 브런치를 생성 할수도 있고, gitflow 메뉴를 이용하면 gitflow 정책대로 브런치를 생성 할 수도 있습니다.gitflow 정책에 대해서는 지난 블로그 내용을 참고 바랍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;669&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwWi2I/btrdcPfBLO6/obSysiKIYrU27VbvTfLqUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwWi2I/btrdcPfBLO6/obSysiKIYrU27VbvTfLqUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwWi2I/btrdcPfBLO6/obSysiKIYrU27VbvTfLqUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwWi2I%2FbtrdcPfBLO6%2FobSysiKIYrU27VbvTfLqUK%2Fimg.png&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;669&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발이 완료되면 커밋을 해줍니다. 단축키는 &lt;code&gt;command+k&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;791&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRZXlG/btrdkPdzYHO/wsmUTC0pKw07tURPHUuowK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRZXlG/btrdkPdzYHO/wsmUTC0pKw07tURPHUuowK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRZXlG/btrdkPdzYHO/wsmUTC0pKw07tURPHUuowK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRZXlG%2FbtrdkPdzYHO%2FwsmUTC0pKw07tURPHUuowK%2Fimg.png&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;791&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커밋 메세지는 자동생성되는데 기본 옵션으로 &lt;code&gt;티켓ID + 제목&lt;/code&gt; 으로 되어있고 설정을 변경할 수 있습니다.&lt;br /&gt;&lt;code&gt;Preferences &amp;gt; Tasks &amp;gt; Servers&lt;/code&gt; &lt;b&gt;Commit Message 탭&lt;/b&gt;을 클릭해서 원하는 규칙으로 수정 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;344&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJrbcn/btrdewGFcgT/DpXvGce9YCkzqopFrwI6i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJrbcn/btrdewGFcgT/DpXvGce9YCkzqopFrwI6i0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJrbcn/btrdewGFcgT/DpXvGce9YCkzqopFrwI6i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJrbcn%2FbtrdewGFcgT%2FDpXvGce9YCkzqopFrwI6i0%2Fimg.png&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;344&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;티켓 상태도 상황에 맞게 변경 할 수 있습니다. 단축키는 &lt;code&gt;option+shift+w&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;465&quot; data-origin-height=&quot;215&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bROK8j/btrdeXqGrud/sTKR5EFXigTu1oUvKNj24K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bROK8j/btrdeXqGrud/sTKR5EFXigTu1oUvKNj24K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bROK8j/btrdeXqGrud/sTKR5EFXigTu1oUvKNj24K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbROK8j%2FbtrdeXqGrud%2FsTKR5EFXigTu1oUvKNj24K%2Fimg.png&quot; data-origin-width=&quot;465&quot; data-origin-height=&quot;215&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;추가 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 Task에서 생성해주는 브랜치 명은 티켓ID 로 자동할당됩니다. 생성 템플릿을 변경하려면&lt;br /&gt;&lt;code&gt;Preferences &amp;gt; Tasks&lt;/code&gt;에서 &lt;b&gt;Feature branch name format&lt;/b&gt;을 원하는 규칙으로 수정하면됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;718&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBwL6H/btrdd8lChpI/F5AuxSyiUfBHpkUBUrgQjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBwL6H/btrdd8lChpI/F5AuxSyiUfBHpkUBUrgQjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBwL6H/btrdd8lChpI/F5AuxSyiUfBHpkUBUrgQjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBwL6H%2Fbtrdd8lChpI%2FF5AuxSyiUfBHpkUBUrgQjk%2Fimg.png&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;718&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/260&quot;&gt;IntelliJ를 JIRA와 연동해서 사용하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://www.jetbrains.com/help/idea/managing-tasks-and-context.html#&quot;&gt;IntelliJ IDEA&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://confluence.atlassian.com/cloud/api-tokens-938839638.html&quot;&gt;API tokens&lt;/a&gt;&lt;/p&gt;</description>
      <category>Setting</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/20</guid>
      <comments>https://mishka.tistory.com/20#entry20comment</comments>
      <pubDate>Thu, 26 Aug 2021 16:33:28 +0900</pubDate>
    </item>
    <item>
      <title>Webstorm에서 gitflow 사용하기</title>
      <link>https://mishka.tistory.com/19</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;vscode 에서 webstorm으로 갈아타면서 플러그인으로 사용하던 몇가지 기능들을 webstorm 에서도 사용해 보고 싶었습니다.&lt;br /&gt;그 중에 git 을 사용하다 보면 대부분의 경우 &lt;b&gt;&lt;a href=&quot;/2020/03/30/gitflow&quot;&gt;git-flow&lt;/a&gt;&lt;/b&gt; 를 따라서 작업을 진행하게 됩니다. 물론 cli 를 이용해서&lt;br /&gt;정책만을 따라 가며 작업할 수 있지만 번거로운 작업들도 있고 급할때는 까먹기도 하기 때문에 편하게 사용하는 방법을 찾아보았습니다.&lt;br /&gt;&lt;b&gt;&lt;a href=&quot;https://plugins.jetbrains.com/plugin/7315-git-flow-integration&quot;&gt;IntelliJ Git Flow Integration&lt;/a&gt;&lt;/b&gt;를 이용한 방법입니다.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;gitflow 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 전에 gitflow 를 사용할때는 별도로 gitflow 를 설치하지 않았었는데 webstorm에서 gitflow 를 사용하려면 &lt;code&gt;avh&lt;/code&gt; 버전으로 설치가 필요하다.&lt;br /&gt;mac OS의 경우 &lt;b&gt;&lt;a href=&quot;/2019/09/27/zsh-setting&quot;&gt;homebrew&lt;/a&gt;&lt;/b&gt; 를 통해 gitflow 를 설치해줍니다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;brew install git-flow-avh&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 OS 환경에서의 설치 방법은 &lt;a href=&quot;https://github.com/petervanderdoes/gitflow-avh/wiki/Installation&quot;&gt;링크&lt;/a&gt;를 참고하세요&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gitflow 를 설치하고 나면 preferences &amp;gt; plugins &amp;gt; marketplace 에서 &lt;code&gt;Git Flow Integration&lt;/code&gt; 를 검색하여 설치해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;748&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbDJuP/btrdevnlxqf/TQrQummdW5aZxwk7BJSGtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbDJuP/btrdevnlxqf/TQrQummdW5aZxwk7BJSGtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbDJuP/btrdevnlxqf/TQrQummdW5aZxwk7BJSGtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbDJuP%2Fbtrdevnlxqf%2FTQrQummdW5aZxwk7BJSGtk%2Fimg.png&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;748&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Webstorm을 재시작하면 설치가 완료됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인 설치 후 Webstorm 우측 하단을 보시면 아래그림과 같이 No Gitflow 를 보이시면 정상적으로 설치가 완료된것입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/webstorm/gitflow-nogitflow.png&quot; alt=&quot;&quot; /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;147&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFFgeH/btrdewzOpmO/Z9QaYfmV9apaPJuWEwTLyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFFgeH/btrdewzOpmO/Z9QaYfmV9apaPJuWEwTLyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFFgeH/btrdewzOpmO/Z9QaYfmV9apaPJuWEwTLyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFFgeH%2FbtrdewzOpmO%2FZ9QaYfmV9apaPJuWEwTLyK%2Fimg.png&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;147&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;No Gitflow 를 클릭하셔셔 init Repo 를 선택하면 아래와 같은 설정화면이 나옵니다.&lt;/p&gt;
&lt;center&gt;&lt;/center&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;413&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RWbi6/btrdetXBKzt/KJ9BbAHUFml4YVKL0uQZRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RWbi6/btrdetXBKzt/KJ9BbAHUFml4YVKL0uQZRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RWbi6/btrdetXBKzt/KJ9BbAHUFml4YVKL0uQZRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRWbi6%2FbtrdetXBKzt%2FKJ9BbAHUFml4YVKL0uQZRk%2Fimg.png&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;413&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 옵션이 git-flow 규칙을 따라 설정되어있어서 똑같은 규칙으로 사용하시다면 별도의 수정없이 OK 눌러 주시면됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 옵션을 설정하고 나면 No gitflow 에서 gitflow 로 변경되고 gitflow 를 클릭하면 원하시는 동작을 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;112&quot; data-origin-height=&quot;199&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eAgNLx/btrdiwlAqrs/lwNTRgXpjRHV27KulVSeGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eAgNLx/btrdiwlAqrs/lwNTRgXpjRHV27KulVSeGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eAgNLx/btrdiwlAqrs/lwNTRgXpjRHV27KulVSeGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeAgNLx%2FbtrdiwlAqrs%2FlwNTRgXpjRHV27KulVSeGk%2Fimg.png&quot; data-origin-width=&quot;112&quot; data-origin-height=&quot;199&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;img src=&quot;/img/webstorm/gitflow-start.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Start Feature를 눌러 feature 브런치를 생성하고 finish feature를 하면 자동으로 develop 브런치에 merge 가 이루어집니다.&lt;br /&gt;Git flow 전략에 따라 버튼만 선택해주면 모든 이벤트가 자동으로 진행됩니다. 별도로 브랜치를 체크아웃하면서 merge, remove, tag 등을 할 필요가 없어서 반복작업의 시간을 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;merge의 기본옵션은 fast-forward 입니다. 이를 변경하여 merge도 하나의 커밋으로 취급하고 싶으신 분들은 prefereces &amp;gt; Other Settings &amp;gt;&lt;br /&gt;gitflow 에서 Do not fast-forward when merging, always create commit(--no-ff) 옵션에 체크 하시면됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;718&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUDZBr/btrdgSCmlYF/3jh3nfUuuczfnc11zsnRHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUDZBr/btrdgSCmlYF/3jh3nfUuuczfnc11zsnRHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUDZBr/btrdgSCmlYF/3jh3nfUuuczfnc11zsnRHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUDZBr%2FbtrdgSCmlYF%2F3jh3nfUuuczfnc11zsnRHK%2Fimg.png&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;718&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;img src=&quot;/img/webstorm/gitflow-fast-forward.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reference&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/268&quot;&gt;Git Flow Integration으로 Git Flow 심플하게 운영하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://plugins.jetbrains.com/plugin/7315-git-flow-integration&quot;&gt;Git Flow Integration&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://k39335.tistory.com/82&quot;&gt;Gitflow로 branch를 관리하자!!&lt;/a&gt;&lt;/p&gt;</description>
      <category>Setting</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/19</guid>
      <comments>https://mishka.tistory.com/19#entry19comment</comments>
      <pubDate>Thu, 26 Aug 2021 16:31:03 +0900</pubDate>
    </item>
    <item>
      <title>gitflow 란? git-flow 를 사용한 브랜치 전략</title>
      <link>https://mishka.tistory.com/18</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;GitFlow?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃플로우(git-flow) 전략은 소프트웨어의 소스코드를 관리하고 출시하기 위한 &lt;b&gt;'브랜치 관리 전략(branch management strategy)'&lt;/b&gt;중 하나이다.&lt;br /&gt;git-flow 전략외에도 &lt;a href=&quot;https://guides.github.com/introduction/flow/&quot;&gt;github flow&lt;/a&gt; 와 &lt;a href=&quot;https://about.gitlab.com/blog/2014/09/29/gitlab-flow/&quot;&gt;gitlab flow&lt;/a&gt; 전략등도 있다. 각자에게 맞는 전략을 선택해서 사용하는게 가장 중요하다.&lt;br /&gt;git-flow 는 Vicent Driessen 이 제안한 git 의 workflow 디자인에 기반한 브랜칭 모델이다. git-flow 에서 사용하는 브랜치의 종류는 5가지이며,&lt;br /&gt;크게 항상 유지되는 메인브렌치(master, develop)와 일정 기간 유지되는 보조 브랜치(feature, realease, hotfix)로 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Master - 제품으로 출시 되는 브랜치&lt;/li&gt;
&lt;li&gt;Develop - 다음 출시 버전을 개발하는 브랜치&lt;/li&gt;
&lt;li&gt;Feature - 기능을 개발하는 브랜치&lt;/li&gt;
&lt;li&gt;Realease - 이번 출시 버전을 준비하는 브랜치&lt;/li&gt;
&lt;li&gt;Hotfix - 출시 버전에서 발생한 버그를 수정하는 브랜치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 그림들 중에 역시 아래의 그림이 git-flow를 한눈에 이해하기에는 가장 좋은 것 같아서 가져왔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;1524&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BhhFg/btrdjed3S0c/kJWx80BpJJpE07dLfdjmV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BhhFg/btrdjed3S0c/kJWx80BpJJpE07dLfdjmV1/img.png&quot; data-alt=&quot;git-flow-model&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BhhFg/btrdjed3S0c/kJWx80BpJJpE07dLfdjmV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBhhFg%2Fbtrdjed3S0c%2FkJWx80BpJJpE07dLfdjmV1%2Fimg.png&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;1524&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;git-flow-model&lt;/figcaption&gt;
&lt;/figure&gt;
&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;Master&lt;/code&gt; 와 &lt;code&gt;Develop&lt;/code&gt; 브랜치를 만드는데,&lt;br /&gt;Develop는 Master에서 부터 시작되는 브랜치입니다. 위 두 브랜치는 항상 존재하는 브랜치 입니다.&lt;br /&gt;새로운 추가 작업이 있는 경우 &lt;b&gt;Develop&lt;/b&gt; 에서 &lt;code&gt;Feature&lt;/code&gt; 브랜치를 생성한다.&lt;br /&gt;Feature는 언제나 Develop에서 시작해야 합니다. 기능추가 작업이 완료되면 Feature는 Develop로 &lt;b&gt;Merge&lt;/b&gt;한다.&lt;br /&gt;QA를 위해 &lt;b&gt;Develop&lt;/b&gt;에서 &lt;code&gt;Release&lt;/code&gt; 브랜치를 생성한다. QA를 진행 하면서 발생한 버그들은 Release에 수정된다.&lt;br /&gt;QA가 끝나면 Release 브랜치를 &lt;b&gt;Develop&lt;/b&gt; 와 &lt;b&gt;Master&lt;/b&gt; 브랜치로 각각 &lt;b&gt;Merge&lt;/b&gt; 한다.&lt;br /&gt;&lt;code&gt;Hotfix&lt;/code&gt; 브랜치는 언제나 Master에서 시작해야 합니다. 작업이 완료되면 Hotfix는 &lt;b&gt;Master&lt;/b&gt; 와 &lt;b&gt;Develop&lt;/b&gt; 브랜치로 각각 &lt;b&gt;Merge&lt;/b&gt; 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더욱 자세한 내용은 &lt;a href=&quot;https://nvie.com/posts/a-successful-git-branching-model/&quot;&gt;A successful Git branching model&lt;/a&gt;를 참고하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git 을 여러사람이 함께 사용하다보면 필연적으로 브랜치 전략을 세워서 git을 관리해야됩니다. 어떤 브랜치 전략을 사용하는지는 해당 팀의 성격에 맞게 선택하고 함께 공유하는게 중요하다고 생각합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nvie.com/posts/a-successful-git-branching-model/&quot;&gt;A successful Git branching model&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://woowabros.github.io/experience/2017/10/30/baemin-mobile-git-branch-strategy.html&quot;&gt;우린 Git-flow를 사용하고 있어요 - 우아한형제들 기술 블로그&lt;/a&gt;&lt;/p&gt;</description>
      <category>Git</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/18</guid>
      <comments>https://mishka.tistory.com/18#entry18comment</comments>
      <pubDate>Thu, 26 Aug 2021 16:28:22 +0900</pubDate>
    </item>
    <item>
      <title>Vue 에서 Axios를 사용하여 서버통신 해보기</title>
      <link>https://mishka.tistory.com/17</link>
      <description>&lt;h2&gt;Axios 란?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Axios&lt;/code&gt;는 &lt;strong&gt;HTTP 클라이언트 라이브러리&lt;/strong&gt; 중 하나이다.&lt;br&gt;비동기 방식으로 HTTP 데이터 요청을 실행하고 또한 IE8 이상을 포함한 모든 최신 브라우저를 지원한다.&lt;br&gt;&lt;code&gt;Axios&lt;/code&gt;는 &lt;strong&gt;Promise&lt;/strong&gt;를 기반의 자바스크립트 비동기 처리 방식을 사용합니다.&lt;/p&gt;
&lt;!-- more --&gt;

&lt;h2&gt;설치&lt;/h2&gt;
&lt;p&gt;보통 npm을 통해 설치를 진행합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install axios&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이밖에도 yarn,bower,CDN 을 통해 설치도 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;// yarn
yarn add axios

// bower
bower install axios

// Using unpkg CDN
&amp;lt;script src=&amp;quot;https://unpkg.com/axios/dist/axios.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;사용방법&lt;/h2&gt;
&lt;p&gt;Axios는 여러가지 &lt;strong&gt;별칭 method&lt;/strong&gt;를 제공하고 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;axios.get(url[, config])&lt;br&gt;axios.post(url[, data[, config]])&lt;br&gt;axios.patch(url[, data[, config]])&lt;br&gt;axios.delete(url[, config])&lt;br&gt;위에 자주 쓰이는 4가지 이외에도 여러가지 를 지원 하고 있다.&lt;br&gt;axios.request(config)&lt;br&gt;axios.head(url[, config])&lt;br&gt;axios.options(url[, config])&lt;br&gt;axios.put(url[, data[, config]])&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;별칭으로 사용하는 경우에는 url, method 및 data 특성을 구성에 지정할 필요가 없습니다.&lt;/p&gt;
&lt;h2&gt;GET (불러오기)&lt;/h2&gt;
&lt;p&gt;GET은 말 그대로 서버에서 데이터를 가져오는데 사용합니다. 많이 사용하는 명령어 중에 하나입니다.&lt;br&gt;서버 주소 &lt;code&gt;/api&lt;/code&gt;로 부터 값을 가져올때는 아래와 같이 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;axios.get(&amp;#39;/api&amp;#39;)
  .then(res =&amp;gt; {
    console.log(res); 
  })
  .catch(err) =&amp;gt; {
    console.log(err);
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;axios 요청할 때 메소드의 두번째 인자인 config 객체에 요청값을 같이 넘길 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;axios.get(&amp;#39;/api&amp;#39;, {
  params: { title: &amp;#39;타이틀&amp;#39; },
  headers: { &amp;#39;Content-Type&amp;#39;: &amp;#39;application/json&amp;#39; },
  timeout: 1000  
}).then(res =&amp;gt; {
  console.log(res);
  })&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;POST (입력하기)&lt;/h2&gt;
&lt;p&gt;서버에 값을 입력 할 때 사용합니다. 서버의 데이터 리스트의 마지막에 넘기는 정보를 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;axios.post(&amp;#39;/api&amp;#39;, { title: &amp;#39;타이틀&amp;#39; })
  .then(res =&amp;gt; {
  console.log(res);  
  })&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;PATCH (수정하기)&lt;/h2&gt;
&lt;p&gt;서버의 특정 데이터를 수정합니다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;axios.patch(&amp;#39;/api&amp;#39;, { title: &amp;#39;타이틀변경&amp;#39; })
  .then(res =&amp;gt; {
    console.log(res);  
  })&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;DELETE (삭제하기)&lt;/h2&gt;
&lt;p&gt;서버의 특정 값을 삭제합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;axios.delete(&amp;#39;/api/val&amp;#39;) //val = 특정 값
  .then(res =&amp;gt; {
    console.log(res);  
  })&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Vue 에서 Axios 사용하기&lt;/h2&gt;
&lt;p&gt;vue 에서 axios 를 사용하려면 Vue.prototype에 axios를 추가하면 됩니다. main.js에 아래와 같은 내용을 추가한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import Vue from &amp;#39;vue&amp;#39;
import App from &amp;#39;./App.vue&amp;#39;
import axios from &amp;#39;.axios&amp;#39; // import axios

Vue.prototype.$axios = axios; // prototype에 axios 추가

Vue.config.productionTip = false

new Vue({
    render: h =&amp;gt; h(App),
}).$mount(&amp;#39;#app&amp;#39;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같이 작성하면 Vue 인스턴스 내부에서 axios를 따로 import 하지 않아도 &lt;code&gt;this.$axios&lt;/code&gt;를 이용해서 사용 할 수 있다.&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/axios/axios&quot;&gt;https://github.com/axios/axios&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://tuhbm.github.io/2019/03/21/axios/&quot;&gt;https://tuhbm.github.io/2019/03/21/axios/&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://ux.stories.pe.kr/138&quot;&gt;https://ux.stories.pe.kr/138&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://luji.tistory.com/83&quot;&gt;https://luji.tistory.com/83&lt;/a&gt;&lt;/p&gt;</description>
      <category>Vue</category>
      <category>axios</category>
      <category>Vue.js</category>
      <category>서버통신</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/17</guid>
      <comments>https://mishka.tistory.com/17#entry17comment</comments>
      <pubDate>Thu, 26 Aug 2021 16:18:01 +0900</pubDate>
    </item>
    <item>
      <title>[VScode] VScode Extension - 확장프로그램 추천 및 설치방법</title>
      <link>https://mishka.tistory.com/16</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;VScode는 비교적 가벼운 에디터 입니다. 기본적으로는 필수적인 기능들만을 제공 하고 있습니다.&lt;br /&gt;대신 마켓플레이스(Marketplace)를 통해서 많은 확장프로그램(Extension)들을 설치하여 즉시 사용 할수 있습니다.&lt;br /&gt;확장프로그램을 통해 보다 편리하게 코드를 작성 할 수 있습니다.&lt;br /&gt;그 중에서 많이 사용하는 것들을 한번 알아보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;확장프로그램 찾아보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VScode 내에서 확장기능을 찾아서 설치할 수 있습니다. VScode를 실행하면 왼쪽에 여러가지 아이콘들이 있습니다.&lt;br /&gt;그 중에 가장 아래에 있는 네모모양의 아이콘 또는 명령어(&lt;code&gt;Ctrl+Shift+X&lt;/code&gt;)를 실행하여 확장프로그램들을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;48&quot; data-origin-height=&quot;248&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9egy0/btrdfHHOEfY/00DHp19APEKtC7ADdkfbwk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9egy0/btrdfHHOEfY/00DHp19APEKtC7ADdkfbwk/img.jpg&quot; data-alt=&quot;Extension&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9egy0/btrdfHHOEfY/00DHp19APEKtC7ADdkfbwk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9egy0%2FbtrdfHHOEfY%2F00DHp19APEKtC7ADdkfbwk%2Fimg.jpg&quot; data-origin-width=&quot;48&quot; data-origin-height=&quot;248&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Extension&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 검색창의 검색을 통해서 간단하게 설치 할수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;확장프로그램 리스트 (Extension List)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Korean Language Pack for Visual Studio Code&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTKMwx/btrdgRpTzMI/e0sDIH7BORvEJKLnTrfTlk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTKMwx/btrdgRpTzMI/e0sDIH7BORvEJKLnTrfTlk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTKMwx/btrdgRpTzMI/e0sDIH7BORvEJKLnTrfTlk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTKMwx%2FbtrdgRpTzMI%2Fe0sDIH7BORvEJKLnTrfTlk%2Fimg.jpg&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=MS-CEINTL.vscode-language-pack-ko&quot;&gt;Korean Language Pack for Visual Studio Code&lt;/a&gt;는 VScode의 언어의 한국어 팩입니다. VScode의 언어를 한국어로 변경해 줍니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Git History&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cck890/btrdjeE9kG7/3tztXUpat5aRnFdEz5MH41/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cck890/btrdjeE9kG7/3tztXUpat5aRnFdEz5MH41/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cck890/btrdjeE9kG7/3tztXUpat5aRnFdEz5MH41/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcck890%2FbtrdjeE9kG7%2F3tztXUpat5aRnFdEz5MH41%2Fimg.jpg&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=donjayamanne.githistoryg&quot;&gt;Git history&lt;/a&gt;는 Git으로 관리되는 프로젝트의 Git log. file History, branch, commit 등을 편리하게 확인 할수 있는 확장프로그램입니다.&lt;br /&gt;파일을 열고 &lt;code&gt;F1&lt;/code&gt;키를 누르고 &lt;code&gt;Git: View History&lt;/code&gt;, &lt;code&gt;Git: View File History&lt;/code&gt;, &lt;code&gt;Git: View Line History&lt;/code&gt;를 입력하여 사용합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Active File In StatusBar&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buKnhK/btrdcQsdFRy/7VO7UdqGD76KRm9kyHVxCk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buKnhK/btrdcQsdFRy/7VO7UdqGD76KRm9kyHVxCk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buKnhK/btrdcQsdFRy/7VO7UdqGD76KRm9kyHVxCk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuKnhK%2FbtrdcQsdFRy%2F7VO7UdqGD76KRm9kyHVxCk%2Fimg.jpg&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=RoscoP.ActiveFileInStatusBar&quot;&gt;Active File In StatusBar&lt;/a&gt;는 Vscode 상태 표시 줄에 현재 활성 파일의 전체 경로를 표시하는 확장 프로그램입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/RoscoP/ActiveFileInStatusBar/raw/master/media/ActiveFileInStatusBar.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;현재 활성 파일의 경로를 하단 상태 막대에 표시합니다. 이 경로를 클릭하면 클립보드에 복사하여 사용 할수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Auto Rename Tag&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nPGyd/btrdcRxRG2J/41CVSJWbrc3gbHWGD5g601/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nPGyd/btrdcRxRG2J/41CVSJWbrc3gbHWGD5g601/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nPGyd/btrdcRxRG2J/41CVSJWbrc3gbHWGD5g601/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnPGyd%2FbtrdcRxRG2J%2F41CVSJWbrc3gbHWGD5g601%2Fimg.jpg&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag&quot;&gt;Auto Rename Tag&lt;/a&gt;는 HTML/XML 태그의 이름을 자동으로 바꿉니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/formulahendry/vscode-auto-rename-tag/raw/master/images/usage.gif&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Guides&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0kndE/btrdd44NUG0/5JkI0b3AdL6McfiI1mJDuk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0kndE/btrdd44NUG0/5JkI0b3AdL6McfiI1mJDuk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0kndE/btrdd44NUG0/5JkI0b3AdL6McfiI1mJDuk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0kndE%2Fbtrdd44NUG0%2F5JkI0b3AdL6McfiI1mJDuk%2Fimg.jpg&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=spywhere.guides&quot;&gt;Guides&lt;/a&gt;은 안내선을 표시해 주는 확장프로그램입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;indent-rainbow&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbRJrM/btrdeYC4EyN/UabmBzc7NoVfEXkmCD9e51/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbRJrM/btrdeYC4EyN/UabmBzc7NoVfEXkmCD9e51/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbRJrM/btrdeYC4EyN/UabmBzc7NoVfEXkmCD9e51/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbRJrM%2FbtrdeYC4EyN%2FUabmBzc7NoVfEXkmCD9e51%2Fimg.jpg&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=oderwat.indent-rainbow&quot;&gt;indent-rainbow&lt;/a&gt;는 텍스트 앞의 들여쓰기를 각 단계에서 네가지 색상으로 번갈아 보여주어 들여쓰기를 보다 쉽게 읽을 수 있게 해주는 확장프로그램입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/oderwat/vscode-indent-rainbow/master/assets/example.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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Rainbow Brackets&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnSffC/btrdesYHAp4/1uLlCAvXgBuHMkHkqA8olK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnSffC/btrdesYHAp4/1uLlCAvXgBuHMkHkqA8olK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnSffC/btrdesYHAp4/1uLlCAvXgBuHMkHkqA8olK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnSffC%2FbtrdesYHAp4%2F1uLlCAvXgBuHMkHkqA8olK%2Fimg.jpg&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=2gua.rainbow-brackets&quot;&gt;Rainbow Brackets&lt;/a&gt;은 괄호(bracket)마다 다른 컬러를 제공합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Settings Sync&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biSbxY/btrdgRwE7bo/jkUQiTBLzi3lPfGPs9xnk1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biSbxY/btrdgRwE7bo/jkUQiTBLzi3lPfGPs9xnk1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biSbxY/btrdgRwE7bo/jkUQiTBLzi3lPfGPs9xnk1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiSbxY%2FbtrdgRwE7bo%2FjkUQiTBLzi3lPfGPs9xnk1%2Fimg.jpg&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync&quot;&gt;Settings Sync&lt;/a&gt;는 Github Gist를 사용하여 VScode의 설정(확장플러그인 포함)을 동기화 시켜주는 도구 입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Waka Time&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QrZGJ/btrdkKXCIem/phOUSiMk7K13ezfOm8965k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QrZGJ/btrdkKXCIem/phOUSiMk7K13ezfOm8965k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QrZGJ/btrdkKXCIem/phOUSiMk7K13ezfOm8965k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQrZGJ%2FbtrdkKXCIem%2FphOUSiMk7K13ezfOm8965k%2Fimg.jpg&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;165&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=WakaTime.vscode-wakatime&quot;&gt;Waka Time&lt;/a&gt;은 VScode에서 어떤 프로젝트에서 어떤 언어로 작성했는지 그에 따른 비율 시간을 측정하여 줍니다.&lt;br /&gt;별도의 &lt;a href=&quot;https://wakatime.com&quot;&gt;회원가입&lt;/a&gt;이 필요합니다. VScode 플러그인에 API kye를 입력해서 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 확장프로그램들은 제가 사용하는것 위주로 정리하였습니다. 추천 프로그램 있으면 댓글로 남겨주세요^^&lt;/p&gt;</description>
      <category>Setting</category>
      <category>extension</category>
      <category>vscode</category>
      <category>확장프로그램</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/16</guid>
      <comments>https://mishka.tistory.com/16#entry16comment</comments>
      <pubDate>Thu, 26 Aug 2021 16:09:44 +0900</pubDate>
    </item>
    <item>
      <title>[VScode] Visual Studio Code에서 터미널을 git bash 기본으로 설정하기</title>
      <link>https://mishka.tistory.com/15</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio Code(이하 VScode)에서 터미널을 같이 사용 할수 있습니다. 별도의 창으로 작업을 하면 비효율 적이기도 하고 탭을 계속해서 눌러주어야 하는 불편함을 감수해야 합니다.&lt;br /&gt;VScode의 기본 터미널 사용값은 powershell입니다. 설정변경을 통해서 gitbash를 사용 할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;git bash로 변경하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VScode를 실행해서 &lt;code&gt;ctrl&lt;/code&gt; + &lt;code&gt;,&lt;/code&gt; 를 눌러 설정에 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 검색칸에 &lt;code&gt;terminal.integrated.shell.windows&lt;/code&gt;를 입력합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;99&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AzQwu/btrdiwlxR4U/t9HZ7yiu6GVAjGu1VUoEc0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AzQwu/btrdiwlxR4U/t9HZ7yiu6GVAjGu1VUoEc0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AzQwu/btrdiwlxR4U/t9HZ7yiu6GVAjGu1VUoEc0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAzQwu%2FbtrdiwlxR4U%2Ft9HZ7yiu6GVAjGu1VUoEc0%2Fimg.jpg&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;99&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 검색결과가 뜹니다.&lt;br /&gt;&lt;code&gt;settings.json에서 편집&lt;/code&gt;을 누릅니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;settings.json 편집&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;177&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckf6Xo/btrdjPLQsik/1UjtNkOqsuKppI5KisR6Uk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckf6Xo/btrdjPLQsik/1UjtNkOqsuKppI5KisR6Uk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckf6Xo/btrdjPLQsik/1UjtNkOqsuKppI5KisR6Uk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fckf6Xo%2FbtrdjPLQsik%2F1UjtNkOqsuKppI5KisR6Uk%2Fimg.jpg&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;177&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 문구를 추가해 줍니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;terminal.integrated.shell.windows&quot;: &quot;C:\\Program Files\\Git\\bin\\bash.exe&quot;,
    &quot;workbench.startupEditor&quot;: &quot;newUntitledFile&quot;,
    &quot;editor.renderIndentGuides&quot;: false,
    &quot;editor.minimap.enabled&quot;: false,
    &quot;editor.renderWhitespace&quot;: &quot;boundary&quot;,
    &quot;window.zoomLevel&quot;: 0,
    &quot;workbench.iconTheme&quot;: &quot;vscode-icons&quot;,
    &quot;files.autoGuessEncoding&quot;: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&quot;C:\\Program Files\\Git\\bin\\bash.exe&quot;&lt;/code&gt;에는 설치경로가 다르다면 &lt;code&gt;자신의 git 설치경로&lt;/code&gt;를 입력해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장을 해주고 &lt;code&gt;ctrl&lt;/code&gt;+&lt;code&gt;shift&lt;/code&gt;+&lt;code&gt;`&lt;/code&gt; 을 눌러서 새 터미널을 열어 확인합니다.&lt;br /&gt;바로 반영이 안되면 VScode를 한번 껏다가 켜줍니다.&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;br /&gt;&lt;code&gt;ctrl + k&lt;/code&gt; , &lt;code&gt;ctrl + s&lt;/code&gt; 눌러서 바로 가기 키 설정에 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색창에 &lt;code&gt;터미널&lt;/code&gt;을 검색합니다.&lt;br /&gt;더블클릭하면 새로운 단축키를 설정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;750&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BAYN9/btrdjPdZKBB/qEi3cKUct98KmEKHlUkHT0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BAYN9/btrdjPdZKBB/qEi3cKUct98KmEKHlUkHT0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BAYN9/btrdjPdZKBB/qEi3cKUct98KmEKHlUkHT0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBAYN9%2FbtrdjPdZKBB%2FqEi3cKUct98KmEKHlUkHT0%2Fimg.jpg&quot; data-origin-width=&quot;1568&quot; data-origin-height=&quot;750&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Setting</category>
      <category>gitbash</category>
      <category>Visual Studio Code</category>
      <category>VS Code</category>
      <category>터미널</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/15</guid>
      <comments>https://mishka.tistory.com/15#entry15comment</comments>
      <pubDate>Thu, 26 Aug 2021 16:06:08 +0900</pubDate>
    </item>
    <item>
      <title>지난 1년6개월의 회고</title>
      <link>https://mishka.tistory.com/14</link>
      <description>&lt;p&gt;2019-08 작성글&lt;/p&gt;
&lt;p&gt;이번에 정들었던 &lt;strong&gt;&lt;code&gt;(주)라이프스타일프로젝트(이하 LSP)&lt;/code&gt;&lt;/strong&gt; 를 떠나면서 그동안의 회사생활 및 1년6개월 동안의 생활을 회고해보고 달라진 점들을 정리해본다. 사실 지금 글을 쓰고있는 &lt;code&gt;오늘(8월29일)&lt;/code&gt;이 마지막 출근날이다.&lt;/p&gt;
&lt;h2&gt;뜻밖의 연락&lt;/h2&gt;
&lt;p&gt;현재 다니고있고 곧 퇴사(8월31일부)하게 되는 LSP와의 인연은 &lt;code&gt;2018년 2월&lt;/code&gt;에 시작됐다.&lt;br&gt;그 당시 이직을 준비하고 있는 상태였던 필자는 취업사이트에 이력서를 올려놓고 여러군데에 면접을 보고 있는 상황이었다.&lt;br&gt;그러던중 지금의 회사 인사팀에서 먼저 필자의 이력서를 보고 연락이 왔다. 그당시 회사명은 &lt;code&gt;(주)유앤김파트너스&lt;/code&gt; 였다.&lt;/p&gt;
&lt;p&gt;그때까지만 해도 이력서를 넣지 않은 곳에서 직접적으로 연락이 온적은 없어서 일단 신기했고 또 그 당시 회사 주력브랜드였던 &lt;code&gt;미프(미남프로젝트)&lt;/code&gt;의 제품을 우연히 정글의 법칙에서 김병만이 사용하는 것을 본 후라 그런지 더욱 신기했었다.&lt;/p&gt;
&lt;p&gt;그렇게 면접제의를 받고 면접을 보게 되었고 1:3의 면접이었다. 면접관으로는 지금 같이 일하고 있는 팀장님, 인사담당자, 부사장님 이렇게 세분이 들어오셨었다. 몇가지 기술적인 질문이 있었으나 크게 어려운 질문은 없었고 상당히 즐거운 분위기에서 면접은 마무리되었다. 기억나는게 한가지 있는데 면접 마지막에 언제 연락을 줄 수 있냐는 필자의 질문에 회사측에서 필자가 첫번째 면접자라고 시간이 조금 걸릴것 같다고 했는데 그 대답을 듣고 필자가 호기롭게 &lt;code&gt;&amp;quot;첫번째가 가장좋은거 아시죠??&amp;quot;&lt;/code&gt;하며 너스레를 떨었던 것이 기억이 난다. ㅎㅎㅎ 지금 생각해도 참..... 그 덕분인지는 모르겠지만 면접 다음날 바로 연락이 왔는데 다른 사람을 추가면접보지 않고 바로 필자를 합격자로 정했다는 연락이었다. 연봉제안도 필자가 제시했던 금액보다 좋은 조건을 제시해주어서 여러가지 망설임이 있었지만 입사를 결정하게 되었다.&lt;/p&gt;
&lt;h2&gt;두근두근 첫 출근&lt;/h2&gt;
&lt;p&gt;입사는 설이 지나고였다. 명절을 보내고 새로운 마음으로 회사에 첫 출근을 하였다. 부서는 &lt;code&gt;크리에이티브팀&lt;/code&gt;이었고, 필자를 제외한 모든 부서원들은 디자이너들이었다. 필자의 업무는 기존에 계시던 분이 인수인계를 해주셨는데, 회사가 원래는 판교에서 지금의 역삼역근처로 이사를 오면서 거리도 멀어지고 건강도 안좋아지셔서 어쩔수 없이 퇴사를 하게 되었다고 하셨다.&lt;/p&gt;
&lt;p&gt;그렇게 인수인계 받은 기존의 사이트는 &lt;code&gt;카페24 쇼핑몰 솔루션&lt;/code&gt;을 통해 작업이 되어있었고 대부분이 이미지로 코딩이 되어있는 상태였다. 기존에 계시던분이 원래 디자이너 출신이어서 간단하게 코딩이 이루어져 있었다. 추후에 대대적인 리뉴얼이 이루어졌다.&lt;br&gt;그때까지만 해도 필자는 카페25 쇼핑몰 솔루션은 로그인만 해서 보았던게 전부였고 그 전에 하드코딩을 했었어서 카페25를 통한 코딩에 막연한 두려움을 약간을 가지고 있었으나 기존에 하던것에서 약간의 환경의 변화만 있었을 뿐 어려움 없이 적응 할 수 있었다.&lt;br&gt;또한 인수인계를 해주고 떠나시는 분을 제외하고는 회사에서 코딩 및 개방을 할 수 있는 사람이 한 사람도 없어서 막막하기도 하였다.&lt;/p&gt;
&lt;h2&gt;하면된다.&lt;/h2&gt;
&lt;p&gt;짧은 인수인계 기간이 끝나고 본격적으로 홀로 업무가 시작되었다.&lt;br&gt;그때 급한 업무를 처음으로 처리했던 기억이 나는데 기존에 계시던 분도 처음하는 일이었도 필자도 처음 듣는 업무여서 약간 당황했덨던 기억이난다. 업무내용은 그 때 당시 미프 모바일 사이트를 패키징하여 어플출시를 앞두고 있었는데 그 어플 출시를 위해서 애플 개발자 등록을 해야하고 결재를 해야되는데 그 단계에서 애플과 메일을 주고받고 전화를 받고 거기에서 받은 인증번호를 다시 입력하고 하는 등의 복잡한 프로세스가 있었는데 전에 계신던 분도 몇번 시도를 하다가 중간에 문제가 생겨서 해결을 못하시고 퇴사를 하시면서 필자에게 넘어왔던 업무였다. 중간에 무엇을 잘못했는지 중간 프로세스에서 막혀있는 상태였고 그렇게 상당한 시간이 흘러서 앱스토어 어플 출시 자체가 기약없이 미뤄지고 있는 상황이었다. 필자도 처음 겪는 업무여서 현재의 상황과 구글링 그리고 앱스토어의 문의를 통해 예상보다 빠르고 쉽게 앱스토어 어플 출시를 이루어낼 수 있었다. 오랜기간 멈춰져있던 업무를 필자가 잘 마무리 해서 입사한 후에 업무에 적응하는데 큰 도움이 되었던 사건이었다.&lt;/p&gt;
&lt;h2&gt;일을 하면서&lt;/h2&gt;
&lt;p&gt;1년6개월 동안 LSP를 다니면서 많은 것들을 배우고 성장하는 시간이었다. 회사의 기초가 화장품제조 및 판매이다보니 쇼핑몰 사이트의 개발과 운영에 대해 많은 것들을 배우게 되었고 쇼핑몰의 기초가 되는 카페24 쇼핑몰 솔루션에 대한 깊은 이해를 가질수 있었다. 그와 더불어 기존에 다니넌 에이전시와는 다르게 여유시간들을 갖게되어 아무래도 별도의 클라이언트가 있는것이 아니라 자사의 제품들만 다루기 때문에 일정과 같은 부분에서 회사 내부에 사정에 맞게 조율이 가능한 부분이 있었다. 많은 커뮤니티활동과 더불어 학원에도 다닐수 있는 기회가 있었고 스터디에도 참여해서 많은 것들을 배우는 시간을 가질 수 있었다.&lt;/p&gt;
&lt;p&gt;필자는 크리에이티브팀에 소속되어있었다. 암묵적으로 &lt;code&gt;디자인팀&lt;/code&gt;으로 불리운다 그 이유는 앞에서 잠깐 언급했듯이 필자를 제외한 모든 팀원이 디자이너로 구성되어있기 때문이었다. 필자도 퍼블려서로 전향하기 이전에는 무대디자인을 하였기에 디자이너들과의 소통 및 협업에는 큰 문제가 없었다. 그리고 화장품회사의 특성(?)상 남자직원들보다는 여자직원들의 비중이 더 높았었는데, 이전 무대일을 할때에도 디자인 관련 부서에서 일했기에 여자직원들이 더 많은 환경이었다보니 자연스레 섞여서 생활을 했었고 그러다보니 커뮤니케이션 같은 부분에서는 전혀 어려움이 없었으나 코딩 및 개발 업무를 알고 있는 사람은 전혀 없어서 어떤한 문제가 생겼을 때 혼자서 해쳐나가야하는 어려움은 늘 존재했었고 같이 문제에 대해 고민해 줄 사람의 필요가 느껴졌었다. 당시 회사 사정상 여러 개발자를 두는것은 무리가 있었고 그러다 보니 배움과 공유에 대한 갈증이 생겼고 자연스럽게 커뮤니티활동과 스터디로 그 갈증을 푸는 계기가 되었다.&lt;/p&gt;
&lt;h2&gt;공부, 스터디&lt;/h2&gt;
&lt;p&gt;처음부터 스터디를 시작한 것은 아니었다. 스터디에 대한 정보도 없었고 그러한 것들이 활발히 진행되고 있는 것도 모르는 상황이었다. 그래서 처음에는 익숙한 컴퓨터학원을 통해 부족하다고 생각했던 개발에 대한 부분들의 수업을 듣기 시작했다. 그렇게 몇가지 수업들을 들었으나 만족할 만한 실력향상은 나타나지 않았던것 같다. 그러던중 우연히 &lt;code&gt;&amp;#39;하코사&amp;#39;&lt;/code&gt;라는 커뮤니티를 접하게 되었고 스터디들이 활발히 진행되고 있는 것을 알게 되었다. 필자와 같은 고민을 하는 분들이 많이 있었으며 자신의 어려움을 질문하고 답을 얻어가고 함께 공부하는 커뮤니티였다. 그렇게 여러가지 정보를 얻어가던 중에 한 &lt;code&gt;살롱&lt;/code&gt;에 참여하게 되었고 그곳에서 들은 이야기들은 지금까지 내가 알고 있던 내용들과는 많이 다른 내용들이었다. &amp;#39;프론트엔드개발자가 되어야지&amp;#39;, &amp;#39;지금 하고 있는 업무에서 추가적인 것들을 배우면서 커리어를 쌓으면 할 수 있는 걸 거야&amp;#39; 와 같은 막연한 계획들이 있었는데 그 계획들을 처음부터 다시 생각하게 되는 살롱이었다. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;살롱 : 18세기 중반 프랑스에서 지성인과 예술가가 모여 토론을 펼치고 지식을 나누던 사교 모임&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;살롱에 참여하기 전까지는 프론트엔드 개발자로 전향을 하려면 지금 하던 퍼블리셔 업무에서 추가적으로 약간의 자바스크립트를 배우고 동적인 UI개발을 더 익히면 되겠지 라는 수준이었는데 참여하고 난 후에는 생각이 완전히 바뀌었다. 우선 퍼블리셔와 프론트엔드개발자는 겹치는 부분도 물론 있었지만 근본적으로 다른 직군에 속해 있는 것이라서 추구하는 방향도 다르고 그렇기에 공부해야 하는 방향 자체도 다르다는 것이었다. 대략적으로 퍼블리셔거 보여지는 뷰단에서 UI적인 개발을 주로 한다면 프로트엔드개발자는 프로든단에서 DATA를 다루어 보여지는 뷰단을 선계해 가는 느낌이다. 그렇다보니 접근하는 방식에서부터 차이가 있고 생각하는 방법도 다르게 해야 올바르게 개발자로 거듭날 수 있다는 생각이 들었다. &lt;/p&gt;
&lt;h2&gt;책 집필스터디에 참여&lt;/h2&gt;
&lt;p&gt;그렇게 살롱다녀와서 거기서 알게된 인연으로 정말 좋은 기회인 책집필을 위한 스터디에 참여하는 기회가 찾아왔다.&lt;br&gt;&lt;code&gt;Vue.js&lt;/code&gt;에 관한 책을 집할 하면서 그 내용들을 가지고 구현해 보고 스터디였는데 피자는 당시 Vue.js는 당연히 처음접하였고 프레임워크언어자체가 대한 개념 및 개발을 처음 시작하는 단계였다. 그래서인지 스터디에서 배우는 하나하나가 너무나 값진 경험이었다. 처음 배우는 내용들이다보니 질문도 많았고 이것저것 안되는 것들도 그리고 모르는 것들도 많았는데 집필하시는 분들에게는 이부분이 오히려 도움이 많이 되었다고 하셨다. 서로에게 여러가지를 주고 받을수 있는 윈윈이었으니 잘 마무리 되었다. ㅎㅎㅎ 스터디가 끝나고 간단한 메모어플과 게시판어플을 구현해볼수 있었다. 현재는 책은 무시히 출판되었고 필자는 간단하게나마 추천사로 책 한부분에 남을수 있어서 좋았다. 궁금해 하실 분들을 위해 책제목을 남겨본다. 책 제목은 &lt;code&gt;&amp;quot;커피한잔 마시며 끝내는 Vue.js&amp;quot;&lt;/code&gt; 이다.&lt;/p&gt;
&lt;p&gt;그렇게 Vue.js 스터디를 끝나마치고 더 배워야 한다는 생각에 git스터디, javajscript스터디에 참여하게 되었고 그러다보니 필자에게 맞는 새로운 스터디를 기획해서 스터디를 진행하게 되었다. 현재는 JS ES6를 중점적으로 스터디를 하고 잇으며 8월 31일에 마지막 스터디 모임을 앞두고 있는 상황이다. 이번 스터디에 함께한 스터디원분들 중 많은 분들이 다음 스터디로 함께 했으면 좋겠다는 의사를 밝혀주셔서 현재는 함께 기획하고 있는 중에 있다.&lt;/p&gt;
&lt;p&gt;이렇게 회사 생활을 하면서 커뮤니티와 많은 스터디들을 통해서 부족한 것들을 채우고 발전해나가는 시간들을 가실 수 있었던 것에 대해서는 긍적적으로 생각한다.&lt;/p&gt;
&lt;h2&gt;마치며&lt;/h2&gt;
&lt;p&gt;지난 1년 6개월 동안 LSP에 지내면서 함께한 모든 분들에게 감사의 인사를 전하고 싶다.&lt;br&gt;비록 더 많은 시간들을 함께 하지는 못하지만 어디에서든지 각자의 몫을 하며 빛나고 있기를 소망한다.&lt;/p&gt;</description>
      <category>회고</category>
      <category>라이프스타일프로젝트</category>
      <category>퍼블리셔</category>
      <category>프론트엔드</category>
      <category>회고</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/14</guid>
      <comments>https://mishka.tistory.com/14#entry14comment</comments>
      <pubDate>Thu, 26 Aug 2021 15:39:32 +0900</pubDate>
    </item>
    <item>
      <title>Github SSH key 생성 및 적용하기</title>
      <link>https://mishka.tistory.com/13</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;많은 Git 서버들은 SSH 공개키로 인증을 합니다. 또한 Github 연결시마다 계정정보를 입력해야 하는 번거로움을 제거해 준다. 이번에 입사한 회사에서도 서버에서 SSH를 사용해서 인증하는 시스템을 가지고 있어서 이 기회에 세팅을하면서 적용방법을 정리해 보았다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SSH 공개키 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 공개키를 사용하려면 공개키를 만들어야 한다. 그 전에 공개키가 있는지 확인이 필요하다. 기본적으로 사용자의 SSH키들은 사용자의 &lt;code&gt;~/.ssh&lt;/code&gt; 디텍토리에 저장한다. 디텍토리의 파일을 살펴서 공개키가 있는지 확인 할 수 있다.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;cd ~/.ssh
ls&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 &lt;code&gt;id_dsa&lt;/code&gt;나 &lt;code&gt;id_rsa&lt;/code&gt;라고 되어 있다. 그 중 &lt;code&gt;.pub&lt;/code&gt; 파일이 &lt;b&gt;공개키&lt;/b&gt;이고 다른 파일은 &lt;b&gt;개인키&lt;/b&gt; 입니다.&lt;br /&gt;이 파일이 없거나 .ssh 디텍토리가 없으면 &lt;code&gt;ssh-keygen&lt;/code&gt; 프로그램으로 키를 생성하여 준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;터미널 실행하여 다음 명령어를 실행&lt;/h3&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;ssh-keygen -t rsa -b 4096 -C &quot;your_email@example.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적은 이메일을 레이블로 사용해서 새 SSH 키를 작성한다는 메세지가 출력됩니다.(아래와 같은 메세지가 나온다면 성공입니다.)&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;&amp;gt; Generating public/private rsa key pair.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSH Key 저장위치 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;키를 저정할 파일을 입력하십시오&quot; 라는 프로프트가 표시되면 Enter를 누르십시오.(기본위치로 지정)&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;&amp;gt; Enter a file in which to save the key (/Users/you/.ssh/id_rsa): [Press enter]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSH Key 비밀번호 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀번호 없이 설정할경우 엔터 두번을 눌러주면된다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;&amp;gt; Enter passphrase (empty for no passphrase): [Type a passphrase]
&amp;gt; Enter same passphrase again: [Type passphrase again]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SSH Key 등록&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성한 SSH Key를 등록해 봅시다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;터미널을 실행하여 아래 명령어를 실행하여 백그라운드에서 ssh-agent를 시작&lt;/h3&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;eval &quot;$(ssh-agent -s)&quot;
&amp;gt; Agent pid 59566&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ssh의 config 파일에 아래 text를 입력&lt;/h3&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;vi ~/.ssh/config&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Host *&lt;br /&gt;AddKeysToAgent yes&lt;br /&gt;UseKeychain yes&lt;br /&gt;IdentityFile ~/.ssh/id_rsa&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;아래 명령어를 입력하여 SSH Key 값을 ssh-agent에 추가&lt;/h3&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;ssh-add -K ~/.ssh/id_rsa&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SSH Key를 Github 계정에 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github 로그인후 우측상단 메뉴에서 &lt;code&gt;Setting&lt;/code&gt;을 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;848&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzekTa/btrdjOF6oMq/vdpoiQ6wjgk3X0lOXK0EgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzekTa/btrdjOF6oMq/vdpoiQ6wjgk3X0lOXK0EgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzekTa/btrdjOF6oMq/vdpoiQ6wjgk3X0lOXK0EgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzekTa%2FbtrdjOF6oMq%2FvdpoiQ6wjgk3X0lOXK0EgK%2Fimg.png&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;848&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측의 &lt;b&gt;Personal settings&lt;/b&gt; 메뉴 중에 &lt;code&gt;SSH and GPG keys&lt;/code&gt; 클릭&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;848&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzekTa/btrdjOF6oMq/vdpoiQ6wjgk3X0lOXK0EgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzekTa/btrdjOF6oMq/vdpoiQ6wjgk3X0lOXK0EgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzekTa/btrdjOF6oMq/vdpoiQ6wjgk3X0lOXK0EgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzekTa%2FbtrdjOF6oMq%2FvdpoiQ6wjgk3X0lOXK0EgK%2Fimg.png&quot; data-origin-width=&quot;390&quot; data-origin-height=&quot;848&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;New SSH Key&lt;/code&gt;를 눌러서 Title과 Key를 입력후 &lt;code&gt;Add SSH Key&lt;/code&gt; 버튼을 눌러준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Key 에는 아래명령어를 사용해서 .pub 의 공개키를 입력해 준다.&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;cat ~/.ssh/id_rsa.pub&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;id_rsa.pub 예시&lt;/b&gt;&lt;br /&gt;ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU&lt;br /&gt;GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3&lt;br /&gt;Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA&lt;br /&gt;t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En&lt;br /&gt;mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx&lt;br /&gt;NrRFi9wrf+M7Q== &lt;a href=&quot;mailto:schacon@agadorlaptop.local&quot;&gt;schacon@agadorlaptop.local&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github SSH key 생성 및 적용하기를 해보았다. Github 연결시 계정정보를 입력해야 하는 번거로움에서 해방돠었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SSH 연결 테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH 를 연결하고 나서 직접 커밋하기 전에 테스트를 해보는 방법을 알아보았다. 연결테스트를 하기 전에 아래 작업들을 모두 완료해야한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SSH 공개키 생성&lt;/li&gt;
&lt;li&gt;SSH Key 등록&lt;/li&gt;
&lt;li&gt;SSH Key를 Github 계정에 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;b&gt;터미널을 엽니다.&lt;/b&gt; 그리고 아래 문구를 입력해 줍니다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ ssh -T git@github.com&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 아래와 같은 경고가 경고문구를 보실 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vbnet&quot;&gt;&lt;code&gt;The authenticity of host 'github.com (IP ADDRESS)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;vbnet&quot;&gt;&lt;code&gt;The authenticity of host 'github.com (IP ADDRESS)' can't be established.
RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
Are you sure you want to continue connecting (yes/no)?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 같은 메시지중 하나와 일치하는지 확인 후 &lt;b&gt;yes&lt;/b&gt; 를 입력해 줍니다. 아래와 같은 멘트가 나오면 성공입니다.&lt;/p&gt;
&lt;pre class=&quot;irpf90&quot;&gt;&lt;code&gt;Hi username! You've successfully authenticated, but GitHub does not
provide shell access.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 아래 링크를 통해 확인 하실 수 있습니다.&lt;br /&gt;&lt;a href=&quot;https://help.github.com/en/github/authenticating-to-github/testing-your-ssh-connection&quot;&gt;https://help.github.com/en/github/authenticating-to-github/testing-your-ssh-connection&lt;/a&gt;&lt;/p&gt;</description>
      <category>Setting</category>
      <category>github</category>
      <category>SSH</category>
      <category>ssh key</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/13</guid>
      <comments>https://mishka.tistory.com/13#entry13comment</comments>
      <pubDate>Thu, 26 Aug 2021 15:33:47 +0900</pubDate>
    </item>
    <item>
      <title>Github 블로그에 Custom도메인 연결하기</title>
      <link>https://mishka.tistory.com/12</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;*아래글은 Github 블로그 운영당시에 작성한 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hexo를 이용해 github 블로그를 만들고 SEO를&lt;br /&gt;적용시켜보기도 하고 댓글 시스템도 연결하고 스타일도 조금씩 건들여보고 하다보니 결국에 나만의 도메인을 만들어서 연결해 보고 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 여러가지를 알아보다보니 Github 자체에서 Custom Domain을 간편하게 연결할수 있도록 해주고 있었다.&lt;br /&gt;기본적으로 Github를 통해서 정적페이지를 호스팅 하게되면 기본도메인이 주어는데 아래와 같은 형식으로 생성됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;USERNAME.github.io&lt;br /&gt;그래서 저는 &lt;a href=&quot;mishka86.github.io&quot;&gt;mishka86.github.io&lt;/a&gt; 로 설정되었습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!-- more --&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 도메인을 연결하려면 원하시는 도메인 주소를 구입하신 후에 도메인 연결을 하시면 됩니다.&lt;br /&gt;저는 &lt;code&gt;mishka.kr&lt;/code&gt; 도메인을 &lt;a href=&quot;https://www.hosting.kr/&quot;&gt;hosting.kr&lt;/a&gt;을 통해서 구입했습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DNS 설정 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 hosting.kr 설정을 위주로 설명하겠지만 다른 도메인 서비스에서도 비슷한 루트로 변경이 가능 하니 참고하시기 바랍니다.&lt;br /&gt;도메인의 네임서버를 변경해 주어야합니다. hosting.kr에서는 부가서비스로 구분이 되어있네요.&lt;br /&gt;Home &amp;gt; 도메인 &amp;gt; 부가서비스 &amp;gt; 네임서버 설정 관리 들어가서 github에서 요구 하는데로 변경을 시켜줍니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;Type&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;서브 도메인&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;IP주소 / 레코드 값&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;A Record&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;185.199.108.153&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;A Record&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;185.199.109.153&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;A Record&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;185.199.110.153&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;A Record&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;185.199.111.153&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;CName Record&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;www&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;USERNAME.github.io&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A Record는 Domain을 물리적인 IP 주소로 연결 할 수 있도록 합니다.&lt;br /&gt;CName은 물리적인 IP 주소가 아닌 다른 Domain을 연결 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A Record의 경우 위에 것에서 하나를 선택해서 입력해 주면됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;120&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IF4HZ/btrdffkcq85/0kt5YAsCdRMD1lxF1Ubqp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IF4HZ/btrdffkcq85/0kt5YAsCdRMD1lxF1Ubqp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IF4HZ/btrdffkcq85/0kt5YAsCdRMD1lxF1Ubqp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIF4HZ%2Fbtrdffkcq85%2F0kt5YAsCdRMD1lxF1Ubqp1%2Fimg.png&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;120&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 A Record와 CName을 위와 같이 세팅 해주었습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Github Pages 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github에서 블로그가 있는 Repository 로 이동합니다.&lt;br /&gt;메뉴에서 Settings &amp;gt; Options 에 &lt;b&gt;Gihub Pages&lt;/b&gt; 항목에서&lt;br /&gt;Costom domain 을 자신이 가지고 있는 도메인 주소로 변경하고 Save 버튼을 눌러줍니다.&lt;br /&gt;HTTPS주소를 사용하시려면 하단에 Enfoce HTTPS 항목을 체크해 주시면 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2018년 5월 1일 &lt;a href=&quot;https://github.blog/2018-05-01-github-pages-custom-domains-https/&quot;&gt;Github 공식 블로그&lt;/a&gt; 에서 정식 지원 소식이 올라왔습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;231&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UJ5pl/btrdj6Nh4dz/rGMrWcYGqKHISaCTXRouG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UJ5pl/btrdj6Nh4dz/rGMrWcYGqKHISaCTXRouG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UJ5pl/btrdj6Nh4dz/rGMrWcYGqKHISaCTXRouG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUJ5pl%2Fbtrdj6Nh4dz%2FrGMrWcYGqKHISaCTXRouG0%2Fimg.png&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;231&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설정을 하면 이후에 USERNAME.github.io 로 접근되는 요청이 설정하신 도메인으로 Redirect 됩니다.&lt;br /&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;이렇게 진행을 했는데 저는 문제가 한가지 발생했는데요 블로그를 배포하면 Custom domain 설정이 날아가는거였습니다.&lt;br /&gt;혹은 CNAME 파일이 정상적으로 생성되지 않은 경우도 발생 할 수도 있다고 하는데요 그럴 경우에 아래와 같은 해결법이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hexo의 CNAME 생성을 위한 패키지를 설치 합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;npm install hexo-generator-cname --save&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;b&gt;_config.yml&lt;/b&gt; 파일에서 아래와 같이 플러그인 설정을 해줍니다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;plugins: hexo-generator-cname&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;b&gt;_config.yml&lt;/b&gt; 에서 url 이름도 적용한 도메인과 같게 변경해 주세요.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;url: https://mishka.kr&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Github 블로그에 Custom Domain을 연결해 주었습니다. 여러분들도 자신만의 도메인을 연결해서 자신만의 블로그에 특징을 더해보세요.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.holaxprogramming.com/2017/05/15/github-page-and-custom-domain/&quot;&gt;Github Pages에 Custom Domain 적용하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://blog.chulgil.me/how-to-make-blog-using-github-3/&quot;&gt;블로그 만들기 GitHub 심화 3편 - 커스텀 도메인&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;https://blog.gaerae.com/2018/05/github-pages-custom-domains-https.html&quot;&gt;Github Pages 개인 도메인도 무료로 HTTPS 지원 시작!&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>blog</category>
      <category>github</category>
      <category>hexo</category>
      <category>도메인연결</category>
      <category>블로그</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/12</guid>
      <comments>https://mishka.tistory.com/12#entry12comment</comments>
      <pubDate>Thu, 26 Aug 2021 14:56:31 +0900</pubDate>
    </item>
    <item>
      <title>Mac os에 zsh 세팅하기</title>
      <link>https://mishka.tistory.com/11</link>
      <description>&lt;h2&gt;ZSH 란?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Z 셸(Z shell, zsh)&lt;/strong&gt;은 상호작용 로그인 셸이자 셸 스크립트를 위한 강력한 명령 줄 &lt;strong&gt;인터프리터&lt;/strong&gt;로 사용할 수 있는 유닉스 셸이다.&lt;br&gt;&lt;strong&gt;Zsh&lt;/strong&gt;는 bash, ksh, tcsh의 일부 기능을 포함하여 수많은 개선 사항이 갖추어진 &lt;strong&gt;확장형 본 셸&lt;/strong&gt;이다. &lt;a href=&quot;https://ko.wikipedia.org/wiki/Z_%EC%85%B8&quot;&gt;위키백과&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;ZSH 기능 살펴보기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;경로 자동 추론&lt;/li&gt;
&lt;li&gt;타이핑 교정&lt;/li&gt;
&lt;li&gt;명령어 추천&lt;/li&gt;
&lt;li&gt;다양한 플러그인&lt;/li&gt;
&lt;li&gt;이쁜 디자인이 핵심이다 ㅋㅋㅋ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;개발속도를 올려줄 수 있는 간편한 기능들이 많이 있다. &lt;/p&gt;
&lt;h2&gt;ZSH 설치&lt;/h2&gt;
&lt;p&gt;본격적으로 ZSH를 설치 해보자&lt;br&gt;먼저 macOS 용 패키지 관리자인 &lt;code&gt;Homebrew&lt;/code&gt;를 설치한다.&lt;br&gt;설치방법은 &lt;a href=&quot;https://brew.sh/index_ko&quot;&gt;Homebrew 사이트&lt;/a&gt;에 자세히 나와있으니 이번 포스팅에서는 생략한다.&lt;/p&gt;
&lt;p&gt;먼저 아래 명령으로 zsh 가 설치되어있나 확인작업을 해준다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;zsh --version
zsh 5.7.1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;설치되어 있지 않다면 아래의 명령으로 설치를 해준다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install zsh&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;설치가 끝났다면 기본 쉘을 chsh을 사용하여 변경해준다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;chsh -s `which zsh`&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Oh My Zsh&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ohmyz.sh/&quot;&gt;Oh My Zsh&lt;/a&gt;는 ZSH 구성 관리를 위한 오픈 소스 커뮤니티 중심 프레임 워크 입니다. 여기에는 수천가지의 유용한 기능, 도우미, 플러그인, 테마 및 몇가지 소리가 제공됩니다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ sh -c &amp;quot;$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sh -c &amp;quot;$(wget https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ZSH 설치가 완료되었다. 이렇게만 설치하고 사용하여도 상관없지만 이왕 설치한 김에 몇가지 편리한 기능들을 추가로 세팅해보기로 했다.&lt;/p&gt;
&lt;h2&gt;iTerm2 설치&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.iterm2.com/&quot;&gt;iTerm2&lt;/a&gt;는 터미널의 부족한 기능들을 보안해주는 &lt;code&gt;터미널 에뮬레이터&lt;/code&gt;이다. iTerm2에서 제공하는 많은 기능들 중에 유용한 기능들은 아래와 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;자동완성 기능 (Cmd + ;)&lt;/li&gt;
&lt;li&gt;터미널 분할 창 기능 (Split Panes)&lt;/li&gt;
&lt;li&gt;터미널 내에서 찾기 기능&lt;/li&gt;
&lt;li&gt;마우스 없이 복사와 붙여넣기&lt;/li&gt;
&lt;li&gt;더 많은 기능 살펴보기 : &lt;a href=&quot;https://www.iterm2.com/features.html&quot;&gt;https://www.iterm2.com/features.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;iTerm2 설치는 &lt;a href=&quot;https://www.iterm2.com/&quot;&gt;iTerm2홈페이지&lt;/a&gt;에서 다운로드 받은 후에 프로그램을 어플리케이션으로 옮겨주기만 하면된다.&lt;/p&gt;
&lt;h2&gt;iTerm2 테마설치(선택)&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mbadolato/iTerm2-Color-Schemes#installation-instructions&quot;&gt;iTerm Color Schemes&lt;/a&gt;에 접속하여 컬러 스킨을 다운로드 받는다.&lt;br&gt;다운을 받으면 iTerm2를 실행하여 &lt;code&gt;cmd + ,&lt;/code&gt;를 눌러서 환경설정 창을 띄워준다. Profile &amp;gt; colors 메뉴에 들어가서 &lt;code&gt;Color Presets&lt;/code&gt;를 눌러서 하단의 imports를 누른다. 다운로드 받은 폴더 schemes의 테마 중 원하는 테마를 선택한다.(취향존중)&lt;/p&gt;
&lt;h2&gt;ZSH 테마 설치&lt;/h2&gt;
&lt;p&gt;많은 테마들 중에 필자는 &lt;a href=&quot;https://github.com/denysdovhan/spaceship-prompt&quot;&gt;&lt;strong&gt;Spaceship ZSH&lt;/strong&gt;&lt;/a&gt; 테마를 설치해 보았다.&lt;/p&gt;
&lt;p&gt;아래 명령어를 사용해서 설치를 진행한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 복사
git clone https://github.com/denysdovhan/spaceship-prompt.git &amp;quot;$ZSH_CUSTOM/themes/spaceship-prompt&amp;quot;
# 심볼릭링크(심볼릭 링크는 원본파일을 가리키도록 링크만 시켜둔 것 - 윈도우의 바로가기)
ln -s &amp;quot;$ZSH_CUSTOM/themes/spaceship-prompt/spaceship.zsh-theme&amp;quot; &amp;quot;$ZSH_CUSTOM/themes/spaceship.zsh-theme&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;.zshrc&lt;/strong&gt; 파일에서 &lt;strong&gt;ZSH_THEME&lt;/strong&gt; 내용을 변경해준다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vi ~/.zshrc
또는 open ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;해당 내용을 실행&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;source ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 테마 적용시 폰트가 깨져서 원하던 모습을 볼수 없을 경우 폰트 설치를 진행하여 준다.&lt;/p&gt;
&lt;h2&gt;Powerline fonts 설치&lt;/h2&gt;
&lt;p&gt;자세한 설치 방법은 &lt;a href=&quot;https://github.com/powerline/fonts&quot;&gt;Powerline fonts&lt;/a&gt;을 참고 하기 바라며 간단한 설치 방법을 공유 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 복사
git clone https://github.com/powerline/fonts.git --depth=1
# 설치
cd fonts
./install.sh
# 지우기
cd ..
rm -rf fonts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;설치가 완료되면 아까와 같이 iTerm2 에서 설정을 &lt;code&gt;cmd + ,&lt;/code&gt;를 눌러 환경설정에서 Profiles &amp;gt; Text 에서 Powerline폰트를 선택주면된다.&lt;br&gt;&lt;strong&gt;ZSH&lt;/strong&gt;로 터미널과 조금 더 친해져 보자~!!&lt;/p&gt;</description>
      <category>Setting</category>
      <category>iTerm2</category>
      <category>MAC OS</category>
      <category>Oh My ZSH</category>
      <category>zsh</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/11</guid>
      <comments>https://mishka.tistory.com/11#entry11comment</comments>
      <pubDate>Thu, 26 Aug 2021 00:35:49 +0900</pubDate>
    </item>
    <item>
      <title>Hexo 포스팅 스타일 설정하기</title>
      <link>https://mishka.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;hexo를 통해 포스팅을 하다보니 테마커스텀에 대한 욕구가 스멀스멀 솟아올랐다. ㅎㅎㅎ;;&lt;br /&gt;메인 이미지도 그리고 아이콘이나 위젯들의 위치도 이곳 저곳 옮겨보기도 하면서&lt;br /&gt;나름 나만의 스타일로 하나씩 바꿔나가는 재미가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 내용들은 현재 제가 사용하고 있는 icarus 테마를 기준으로 작성한것입니다.&lt;br /&gt;다른 테마에서는 다른 방법이 있을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 블로그를 조금씩 꾸미고 여러가지 포스팅들도 올리고 하다가 한 가지 의문이 생겼다.&lt;br /&gt;바로 포스트 리스트에서 사용하는 위젯들을 &lt;code&gt;_config.yml&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 data-ke-size=&quot;size16&quot;&gt;그래서 &lt;code&gt;_config.yml&lt;/code&gt;에 있는 설정들을 하나씩 뜯어보고 해당 기능이 있는지 찾아보았지만&lt;br /&gt;원하는 기능은 없는것 같아서 기운없이 여러 블로그를 돌아다니고 있는 도중에 내가 사용하는 테마를 같이 사용하는&lt;br /&gt;블로그에서 리스트와 포스트 상세페이지에 스타일이 다른 것을 발견하고는 다시 이것 저것 세팅을 바꿔보고&lt;br /&gt;해당 블로그와 코드도 비교해 보면서 방법을 찾기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러던 중 정말 우연히 내 포스트와 다른 점을 발견하게 됐고 방법을 찾았다.&lt;br /&gt;나와 같은 필요를 느끼고 있는 분들을 위해 그 방법을 간단하게 정리해 보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 포스팅방법이 궁금하신 분들은 지난번에 작성한 hexo 포스팅 방법을 참고하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지 실험끝에 발견한 방법은 그러한 세팅이 따로 존재하는 것이 아니라 포스팅을 작성할때&lt;br /&gt;머리말(Front matter)에 해당 파일의 정보를 입력해주면 되는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머리말은 지난 포스트에서 설명했듯이 포스트 생성시 포스트 최상단에 자동으로 생성이 됩니다.&lt;br /&gt;이곳에는 카테고리,태그,썸네일등을 지정하여 포스트마다 다르게 노출되는 값들을 입력하는 곳이라 생각하였고&lt;br /&gt;머리말을 이용하여 위젯이나 레이아웃을 변경할 수 있을것이라고는 미처 생각하지 못했었다.&lt;br /&gt;그렇게 한참 다른 곳을 해메이다가 우연히 머리말의 입력값이 다른 것을 발견하고는 유레카를 외쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포스트작성시 머리글에 카탈로그,카테고리,태그 클라우드 위젯을 추가하고 싶었고&lt;br /&gt;포스팅 우측에 고정으로 놓고 싶었다. 그래서 아래와 같은 설정을 머리말에 추가해 주었다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;toc: true
widgets:
  - type: toc
    position: right
  - type: category
    position: right
  - type: tagcloud
    position: right
sidebar:
  right:
    sticky: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 옵션을 살펴보면&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;toc: true
# 카탈로그 옵션을 사용하기 위해 true옵션을 사용하였다.

widgets:
  - type: toc
    position: right
  - type: category
    position: right
  - type: tagcloud
    position: right  
# 위젯의 타입을 지정하여주고 위치를 오른쪽으로 지정하였다.   

sidebar:
  right:
    sticky: true
# sticky 옵션은 true로 하여 우측에 고정을 시켜주었다.    &lt;/code&gt;&lt;/pre&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;이상으로 hexo 포스팅 스타일 설정하기 포스팅을 마칩니다. 해당글로 많은 분들이 조금이나마 쉽게 hexo 블로그를 접하고 꾸밀 수 있기를 바랍니다. 도움이 되었다면 댓글을 살포시 남겨주시면 감사하겠습니다.&lt;/p&gt;</description>
      <category>Programming</category>
      <category>hexo</category>
      <category>포스팅스타일</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/10</guid>
      <comments>https://mishka.tistory.com/10#entry10comment</comments>
      <pubDate>Thu, 26 Aug 2021 00:20:54 +0900</pubDate>
    </item>
    <item>
      <title>Hexo에 Disqus를 사용하여 댓글 기능 세팅하기</title>
      <link>https://mishka.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 블로그의 세팅중 한가지인 댓글 기능을 세팅하는 방법에 대해 포스팅해보겠습니다. 사실 hexo 테마들의 대부분은 여러 댓글 시스템들을 간편하게 세팅 할 수 있도록 되어있습니다.&lt;br /&gt;그 중에 &lt;code&gt;디스커스(Disqus)&lt;/code&gt;를 사용하여 &lt;span style=&quot;background-color: #9d9d9d;&quot;&gt;댓글&lt;/span&gt; 기능을 세팅해보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;디스커스(Disqus)란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스커스는 &lt;code&gt;소셜 댓글 서비스&lt;/code&gt;의 하나입니다. 소셜 댓글 서비스란 소셜미디어(SNS)를 활용한 댓글 시스템으로 페이스북,트위터 와 같은 SNS와 연동해서 댓글을 달 수 있게 만들어 주는 서비스입니다. 소셜 댓글 서비스를 활용하여 댓글을 달면 동시에 해당 댓글이 자신이 연동한 SNS에도 발행이 됩니다.&lt;br /&gt;별도의 댓글시스템을 구현할 필요없이 디스커스에서 제공하는 위젯을 설치함으로 사용 할 수 있는 것이 장점입니다.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치순서&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Disqus &lt;b&gt;회원가입&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Settings &amp;gt; &lt;b&gt;profile, Account&lt;/b&gt; 수정&lt;/li&gt;
&lt;li&gt;Add Disqus To Site &amp;gt; &lt;b&gt;I Want to comment on site&lt;/b&gt; 사이트 추가&lt;/li&gt;
&lt;li&gt;Account &amp;gt; &lt;b&gt;Username&lt;/b&gt; 확인 (shortname)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;_config.yml&lt;/b&gt; 설정 변경&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;회원가입&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Disqus 회원이 아니라면 회원가입이 필요합니다.&lt;br /&gt;&lt;a href=&quot;https://disqus.com&quot;&gt;Disqus 사이트&lt;/a&gt;에서 회원가입을 진행합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/disqus_main.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정보 수정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Settings 에 &lt;code&gt;profile&lt;/code&gt; 과 &lt;code&gt;Account&lt;/code&gt;에서 필요한 정보들을 수정해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/disqus_settings.jpg&quot; alt=&quot;&quot; /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;358&quot; data-filename=&quot;disqus_settings.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5ubjW/btrdaF4HD8F/DqQPD38sUOkmQvdXh1s3O0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5ubjW/btrdaF4HD8F/DqQPD38sUOkmQvdXh1s3O0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5ubjW/btrdaF4HD8F/DqQPD38sUOkmQvdXh1s3O0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5ubjW%2FbtrdaF4HD8F%2FDqQPD38sUOkmQvdXh1s3O0%2Fimg.jpg&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;358&quot; data-filename=&quot;disqus_settings.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사이트 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우측 상단의 &lt;code&gt;Add Disqus To Site&lt;/code&gt; 에 들어가서 &lt;code&gt;I Want to comment on site&lt;/code&gt; 를 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 메인에서 &lt;code&gt;GET STARTED&lt;/code&gt; 에 들어가서 &lt;code&gt;I Want to comment on site&lt;/code&gt; 를 클릭&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 정보들을 입력 후 &lt;code&gt;Select a plan&lt;/code&gt; 에서 &lt;code&gt;Basic&lt;/code&gt; 을 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;828&quot; data-filename=&quot;disqus_select_plan.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4EUmB/btrc8Aiba38/pdA3pwq5PvlPIi1FVr9bmk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4EUmB/btrc8Aiba38/pdA3pwq5PvlPIi1FVr9bmk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4EUmB/btrc8Aiba38/pdA3pwq5PvlPIi1FVr9bmk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4EUmB%2Fbtrc8Aiba38%2FpdA3pwq5PvlPIi1FVr9bmk%2Fimg.jpg&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;828&quot; data-filename=&quot;disqus_select_plan.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;img src=&quot;/img/disqus_select_plan.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Select Platform&lt;/code&gt; 단계에서는 맨 아래의 &lt;code&gt;universal Code&lt;/code&gt;를 선택해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/disqus_universal_code.jpg&quot; alt=&quot;&quot; /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;140&quot; data-filename=&quot;disqus_universal_code.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZKuEP/btrc7UVq9O9/6I28jDfuEvADcholxfb8Jk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZKuEP/btrc7UVq9O9/6I28jDfuEvADcholxfb8Jk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZKuEP/btrc7UVq9O9/6I28jDfuEvADcholxfb8Jk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZKuEP%2Fbtrc7UVq9O9%2F6I28jDfuEvADcholxfb8Jk%2Fimg.jpg&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;140&quot; data-filename=&quot;disqus_universal_code.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기는 테마마다 조금 다릅니다. 디스커스를 지원하는 테마라면 &lt;code&gt;configure&lt;/code&gt; 누르고 사이트 세팅을 마친다음&lt;br /&gt;_config.yml 의 설정만 변경 해주면 되고 안되어 있다면 소스를 추가해 주어야 합니다. 그 때 아래와 같은 코드를 사용합니다.&lt;br /&gt;&lt;a href=&quot;https://disqus.com/admin/install/platforms/universalcode/&quot;&gt;Installation instructions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;331&quot; data-filename=&quot;disqus_code.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6XqTG/btrc7OuCAim/vaXfhKQcKdDgGfJO2DiusK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6XqTG/btrc7OuCAim/vaXfhKQcKdDgGfJO2DiusK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6XqTG/btrc7OuCAim/vaXfhKQcKdDgGfJO2DiusK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6XqTG%2Fbtrc7OuCAim%2FvaXfhKQcKdDgGfJO2DiusK%2Fimg.jpg&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;331&quot; data-filename=&quot;disqus_code.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;img src=&quot;/img/disqus_code.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;계정정보 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Account 에서 &lt;code&gt;Username(shortname)&lt;/code&gt;을 확인하여 줍니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;_config.yml 설정&lt;/h2&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;comment:
    type: disqus
    shortname: disqus 계정이름&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 현재 제가 사용하는 &lt;code&gt;icarus&lt;/code&gt; 테마의 _config.yml의 양식이고 테마마다 조금씩 다를수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배포&lt;/h2&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;hexo d -g&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;imageblock alignLeft&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;380&quot; data-filename=&quot;disqus_comment.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7NaM8/btrc8hwBlLy/mjUmj633RXZkl5DKJ7axh0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7NaM8/btrc8hwBlLy/mjUmj633RXZkl5DKJ7axh0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7NaM8/btrc8hwBlLy/mjUmj633RXZkl5DKJ7axh0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7NaM8%2Fbtrc8hwBlLy%2FmjUmj633RXZkl5DKJ7axh0%2Fimg.jpg&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;380&quot; data-filename=&quot;disqus_comment.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오류체크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;icarus&lt;/code&gt;테마는 기본적으로 disqus 댓글 시스템을 지원하고 있어서 위와같이 세팅하면 정상적으로 댓글기능이 작동해야 하지만&lt;br /&gt;무엇이 문제였는지 시스템 자체는 연결되었는데 댓글을 작성하는 위젯이 제대로 작동하지 않았다 ㅠㅜ&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;155&quot; data-filename=&quot;disqus_error.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXqbSV/btrdevmAI3I/uT1BV1FAJlD04KoRAfTp9K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXqbSV/btrdevmAI3I/uT1BV1FAJlD04KoRAfTp9K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXqbSV/btrdevmAI3I/uT1BV1FAJlD04KoRAfTp9K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXqbSV%2FbtrdevmAI3I%2FuT1BV1FAJlD04KoRAfTp9K%2Fimg.jpg&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;155&quot; data-filename=&quot;disqus_error.jpeg&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;icarus&lt;/code&gt;테마에서 제공하는 포스팅에서도 comment Plugin 의 설정만을 변경해 주면된다고 나오는데 말이다.&lt;br /&gt;&lt;a href=&quot;https://blog.zhangruipeng.me/hexo-theme-icarus/Plugins/Comment/disqus-comment-plugin/&quot;&gt;Disqus Comment Plugin&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지를 시도해 보다 결국 소스코드 자체를 변경하는 것으로 해결하였다.&lt;br /&gt;icarus 테마의 경우 &lt;code&gt;disqus.ejs&lt;/code&gt; 파일의 내용을 &lt;code&gt;universal Code&lt;/code&gt;에서 제공하는 코드로 변경하니 정상적으로 작동되었다.&lt;br /&gt;테마마다 약간의 설정이 다를수 있으니 참고정도만 하시면 되겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;언어선택 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 disqus에서는 한글을 지원하지 않는 것으로 변경되었다고 합니다.&lt;br /&gt;그래서 편법으로 설정하는 방법이 있는데 편법이라 방법이 나와있는 포스팅을 공유하는 것으로 대체하겠습니다.&lt;br /&gt;&lt;a href=&quot;https://jungjoongi.com/2018/10/30/disqus-korean-setting/&quot;&gt;디스커스 한글 세팅을 해보자&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>Disqus</category>
      <category>hexo</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/9</guid>
      <comments>https://mishka.tistory.com/9#entry9comment</comments>
      <pubDate>Thu, 26 Aug 2021 00:09:01 +0900</pubDate>
    </item>
    <item>
      <title>Hexo 배포 원리에 따른 백업(backup) 방법</title>
      <link>https://mishka.tistory.com/8</link>
      <description>&lt;p&gt;Hexo를 이용해서 블로그를 만들고 관리 하다보니 문제점을 하나 발견했다.&lt;br&gt;바로 백업에 관한 문제인데 Hexo 블로그는 Github에 repository를 통해 구성되어 있으니&lt;br&gt;다른 곳에서 작업할때 그것을 clone해서 수정하면 된다고 생각했다.&lt;/p&gt;
&lt;p&gt;그러나 다른 곳에서 clone을 해보고는 문제가 있음을 알 수 있었다.&lt;br&gt;라이브되는 repository에 올라가는건 public 폴더의 내용이라 실제로 작업한 것은 repo에 push 되지 않고 로컬에만 저장되어있는 것이다. &lt;/p&gt;
&lt;p&gt;그래서 해결책으로 별도의 백업 repository 하나를 생성하여 별도로 백업을 하기로 했는데 이 방법도 문제가 있었다.&lt;br&gt;Hexo의 테마가 별도의 git을 가지고 있어서 하나의 백업 repository만으로는 완벽하게 백업 관리가 어려웠다 그래서 두개의 백업 repository와&lt;br&gt;한개의 라이브 repository를 가지고 총 3개의 repository를 가지게 되었다.&lt;/p&gt;
&lt;h2&gt;새 저장소 생성&lt;/h2&gt;
&lt;p&gt;기존의 블로그가 라이브(&lt;username&gt;.github.io) 되어있는 repository외에 2개의 저장소가 추가로 필요합나디.&lt;br&gt;&lt;code&gt;테마 백업용&lt;/code&gt;와 &lt;code&gt;포스팅 백업용&lt;/code&gt; 이렇게 2개 입니다.&lt;/p&gt;
&lt;p&gt;저는 제가 사용하는 테마의 이름인 icarus라는 저장소와 blog_backup 이라는 두 개의 저장소를 private로 만들어 줬습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2019.01.07일부로 Github의 private 저장소가 무료로 전환 되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;테마 백업&lt;/h2&gt;
&lt;p&gt;먼저 테마폴더를 백업해줍니다. 보통 테마를 설치 할때는 아래와 같은 명령어를 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/ppoffice/hexo-theme-icarus.git themes/icarus&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;물론 위와 같은 방법으로 설치해도 테마를 수정없이 사용하면 별다른 문제는 없습니다. 다만 테마의 들어가는 내용을 수정을 하게 되면 변경사항을 저장해야되고&lt;br&gt;그것을 한군데가 아니 여러곳에서 관리하러면 별도의 백업이 필요합나다.&lt;/p&gt;
&lt;p&gt;그래서 설치되어 있는 테마의 원격 저장소의 주소를 변경해줍니다. 아래 명령어는 해당 테마폴더에서 실행해야합니다.(cd themes/icarus)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 테마 폴더에서 원격 저장소를 확인합니다.
$ git remote -v

# 원격 저장소를 변경해 줍니다. (자신의 저장소 주소로 변경)
$ git remote set-url origin &amp;lt;저장소 주소&amp;gt;

# 변경된 원격 저장소를 확인합니다.
$ git remote -v

# 변경 사항 올려줍니다.
$ git commit -a -m &amp;#39;theme backup&amp;#39;
$ git push -u origin master
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;블로그 자료 백업&lt;/h2&gt;
&lt;p&gt;블로그 폴더를 git으로 초기화 시켜줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#git 초기화
git init

#현재 내용을 전부 커밋해 줍니다.
git commit -a
또는
git add .
git commit -m &amp;quot;커밋메세지&amp;quot;

#백업 repo에 연결하고 push
git remote add origin &amp;lt;저장소주소&amp;gt;
git push -u origin master&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 하면 각 repo에 테마백업과 포스팅 백업을 할 수 있습니다. 그러고 나서 hexo 명령어를 통해 배포해 줍니다. &lt;/p&gt;
&lt;h2&gt;배포하기&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hexo clean
hexo d -g&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;힘든 백업 작업이 끝났습니다. ㅎㅎㅎ 약간 번거롭지만 안전한게 좋아서 이렇게 세팅하긴 했습니다.&lt;br&gt;그리고 마지막으로 테마폴더의 내용을 변경하게 되면 루트위치에서 한번에 관리가 되지 않습니다. themes/icarus 위치에서 테마백업 repo에&lt;br&gt;push 해주고 다시 루트 위치로 돌아와서 별도의 push를 해줘야 정상적으로 백업관리가 됩니다.&lt;/p&gt;
&lt;p&gt;결과적으로 총 3개의 repo를 활용해서 블로그를 백업하고 라이브 하는 작업을 하였습니다. 먼가 조금 더 스마트한 방법이 있을것 같은데&lt;br&gt;아는게 없네요 ㅎㅎㅎ; 좋은 방법이 있으신 분은 댓글이나 메일로 남겨주시면 감사하겠습니다.&lt;/p&gt;</description>
      <category>Programming</category>
      <category>backup</category>
      <category>github</category>
      <category>hexo</category>
      <category>백업</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/8</guid>
      <comments>https://mishka.tistory.com/8#entry8comment</comments>
      <pubDate>Thu, 26 Aug 2021 00:05:09 +0900</pubDate>
    </item>
    <item>
      <title>Hexo 블로그에 테마 적용하기 - icarus테마</title>
      <link>https://mishka.tistory.com/7</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;hexo를 이용해서 github블로그를 만들었습니다. 이제는 나만의 색깔을 입혀볼 테마를 적용해 보겠습니다.&lt;br /&gt;블로그 세팅 방법은 지난 블로그를 참고하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;hexo블로그&lt;/code&gt;에 테마를 입히는 방법은 사실 아주 간단 합니다.&lt;br /&gt;git을 이용하여 &lt;code&gt;clone&lt;/code&gt;만 해주면 됩니다. 하지만 테마들마다 사용방법이나 설정법이 약간씩 다르고 자신의 색깔에 맞게&lt;br /&gt;커스텀을 하려고 한다면 점점 복잡해 집니다.&lt;br /&gt;테마마다 설정방법이 다르기 때문에 이번 포스팅에서는 제가 적용했었던 &lt;code&gt;icarus테마&lt;/code&gt;를 기준으로 설명하겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테마 고르기&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 자신의 블로그에 적용할 테마를 고릅니다.&lt;br /&gt;&lt;a href=&quot;https://hexo.io/themes/&quot;&gt;Hexo Themes&lt;/a&gt; 에서 자신이 원하는 테마를 골라줍니다.&lt;br /&gt;Hexo Themes에는 많은 개발자들이 hexo를 사용하여 만든 테마 목록들을 확인하고 다운받을 수 있습니다. 물론 별도로 만들수도 있지만 시간이 많이 들고 잘 만들어진것들이 많이 있으니 오픈소스를 적극 활용 해보기로 합니다. ㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 테마를 고를때 디자인, 반응형, 영문메뉴얼유무를 따져 보았고 그 중에 여러가지 테마를 적용해 보고 icarus테마를 골랐습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테마 적용하기&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 말했듯이 테마별로 적용 방법의 약간의 차이가 있을수 있으나 대체적으로 &lt;code&gt;git clone&lt;/code&gt; 을 이용하여 설치하는 방법을&lt;br /&gt;해당테마에서 설명해주고 있습니다.&lt;br /&gt;icarus테마도 &lt;a href=&quot;https://github.com/ppoffice/hexo-theme-icarus&quot;&gt;hexo-theme-icarus repository&lt;/a&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;터미널을 이용하여 hexo블로그가 있는 폴더에 들어가 줍니다. icarus테마를 &lt;code&gt;themes/icarus&lt;/code&gt; 경로에 clone 받아 줍니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;cd blog
git clone https://github.com/ppoffice/hexo-theme-icarus.git themes/icarus&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 입력해 주면 themes 디테토리 하위에 icarus 라는 디텍토리가 생기면서 해당 저장소의 내용을 clone 하여 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 blog 폴더 안에있는 &lt;code&gt;_config.yml&lt;/code&gt;에서 &lt;code&gt;theme&lt;/code&gt; 부분을 landscape에서 icarus로 수정해줍니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;# Extensions
## Themes: https://hexo.io/themes/
theme: icarus&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;b&gt;themes/icarus&lt;/b&gt; 폴더에 있는 &lt;code&gt;_config.yml&lt;/code&gt;에서 테마 설정을 변경 할 수 있습니다.&lt;br /&gt;블로그 폴더에도 &lt;code&gt;_config.yml&lt;/code&gt;에서 설정을 변경하였는데 테마 역시 &lt;code&gt;_config.yml&lt;/code&gt;를 사용하여 설정하여 준다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 폴더의 &lt;code&gt;_config.yml&lt;/code&gt;가 hexo 블로그의 전반적인 설정 이라면&lt;br /&gt;themes/테마 폴더의 &lt;code&gt;_config.yml&lt;/code&gt;는 hexo 블로그에 적용된 테마의 추가 설정이라고 보면된다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>Programming</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/7</guid>
      <comments>https://mishka.tistory.com/7#entry7comment</comments>
      <pubDate>Thu, 26 Aug 2021 00:02:14 +0900</pubDate>
    </item>
    <item>
      <title>Hexo 검색엔진 최적화(SEO) 시켜보기</title>
      <link>https://mishka.tistory.com/6</link>
      <description>&lt;p&gt;이번에 적용해 보려고 하는 것은 검색엔진 최적화(SEO)입니다.&lt;br&gt;다행히 Hexo에는 SEO관련 플러그인들이 많이 있었다.&lt;br&gt;그 중에 많이 쓰이고 해당 블로그에 적용한 플러그인들을 소개합니다.&lt;/p&gt;
&lt;h2&gt;SEO&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;SEO란?&lt;br&gt;영어로 Search Engine Optimization 의 약자이며, 검색엔진 최적화 라는 의미를 가지고 있습니다.&lt;br&gt;검색엔진에 맞게 사이트를 제작 및 운영하는 과정 전부를 뜻합니다.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2&gt;플러그인 설치 및 설정&lt;/h2&gt;
&lt;hr&gt;
&lt;h3&gt;- hexo-auto-canonical&lt;/h3&gt;
&lt;p&gt;메타 태그 중 canonical 속성은 대표 URL을 뜻합니다.&lt;br&gt;각 포스트마다 자동으로 표준 링크를 만들어 줍니다.&lt;/p&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 --save hexo-auto-canonical&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;설정&lt;/strong&gt;&lt;br&gt;HEAD에 위치 하도록 아래 내용을 추가해줍니다.&lt;/p&gt;
&lt;p&gt;ejs 의 경우&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&amp;lt;%- autoCanonical(config, page) %&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;jade 의 경우&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;| !{ autoCanonical(config, page) }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;- hexo-autonofollow&lt;/h3&gt;
&lt;p&gt;해당 포스트에서 참고하고 있는 &lt;u&gt;외부링크&lt;/u&gt;에 nofollw 속성을 자동으로 추가해줍니다.&lt;/p&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 hexo-autonofollow --save&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;설정&lt;/strong&gt;&lt;br&gt;&lt;br&gt;_config.yml 파일에 아래 내용을 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nofollow:
    enable: true
    exclude:
    - exclude_1.com
    - exclude_2.com&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;h3&gt;- hexo-generator-seo-friendly-sitemap&lt;/h3&gt;
&lt;p&gt;사이트 맵을 제출하면 크롤러가 효율적으로 크롤링을 할 수 있습니다.&lt;br&gt;자동으로 sitemap.xml을 생성해 줍니다.&lt;/p&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 hexo-generator-seo-friendly-sitemap --save&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;설정&lt;/strong&gt;&lt;br&gt;&lt;br&gt;_config.yml 파일에 아래 내용을 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sitemap:
  path: sitemap.xml
  tag: false
  category: false&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;path값을 지정한 root폴더에 sitemap.xml이 생성됩니다.&lt;br&gt;&amp;lt;사이트주소&amp;gt;/sitemap.xml 경로에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tag : sitemap에 tag 포함 여부를 결정합니다.&lt;/li&gt;
&lt;li&gt;category : sitemap에 category 포함 여부를 결정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;- hexo-generator-feed&lt;/h3&gt;
&lt;p&gt;자동으로 Atom 1.0또는 RSS2.0 피드를 생성해 줍니다.&lt;/p&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 hexo-generator-feed --save&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;설정&lt;/strong&gt;&lt;br&gt;&lt;br&gt;_config.yml 파일에 아래 내용을 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;feed:
  type: rss2
  path: rss2.xml
  limit: 20&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;네이버에서 Atom을 지원하지 않기 때문에 RSS2로 설정해 줍니다.&lt;/li&gt;
&lt;li&gt;type : 피드의 종류 (atom/rss2)&lt;/li&gt;
&lt;li&gt;path : 피드가 생성될 경로 (Default: atom.xml/rss2.xml)&lt;/li&gt;
&lt;li&gt;limit : 최신 포스트의 개수 설정 (0 또는 false 입력 시 모든 포스트)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;- hexo-generator-robotstxt&lt;/h3&gt;
&lt;p&gt;자동으로 robots.txt 파일을 생성해 줍니다.&lt;/p&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 hexo-generator-robotstxt --save&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;설정&lt;/strong&gt;&lt;br&gt;&lt;br&gt;_config.yml 파일에 아래 내용을 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;robotstxt:
  useragent: &amp;quot;*&amp;quot;
  allow:
    - /
  sitemap: https://[username].github.io/sitemap.xml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deploy를 해주신 후에&lt;br&gt;각 검색엔진에서 SEO설정을 해줍니다.&lt;/p&gt;</description>
      <category>Programming</category>
      <category>hexo</category>
      <category>SEO</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/6</guid>
      <comments>https://mishka.tistory.com/6#entry6comment</comments>
      <pubDate>Wed, 25 Aug 2021 23:51:03 +0900</pubDate>
    </item>
    <item>
      <title>Hexo 포스팅 방법</title>
      <link>https://mishka.tistory.com/5</link>
      <description>&lt;p&gt;이전 포스팅 에서 &amp;#39;Hexo 블로그의 세팅방법&amp;#39;과 간단한 글쓰는 방법에 대해 다뤄보았는데요 Hexo는 간단한 포스팅 외에도&lt;br&gt;다양한 기능의 포스팅 방법을 제공해 줍니다. Hexo에서 지원하는 것들은 무엇이 있는지 한번 살펴보겠습니다.&lt;/p&gt;
&lt;h2&gt;글쓰기(Writing)&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;일단 기본적인 명령어 부터 하나씩 살펴보겠습니다.&lt;br&gt;hexo 명령어를 사용하여 새로운 포스팅의 마크다운 파일을 생성해 줍니다. 레이아웃은 생략해도 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hexo new [layout] [포스트이름]
hexo new [포스트이름] # layout을 지정하지 않으면 기본으로 post을 생성합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 명령어를 실행하면 source/_posts 폴더에 포스트이름.md 으로 새로운 마크다운 파일이 생성됩니다.&lt;br&gt;파일명에 띄어쓰기가 있다면 하이픈(-)으로 표시됩니다.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;layout 에는 3종류가 있습니다. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;-post&lt;br&gt;-draft&lt;br&gt;-page &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;각기 다른 경로에 보관 됩니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;layout&lt;/th&gt;
&lt;th&gt;경로&lt;/th&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;post&lt;/td&gt;
&lt;td&gt;source/_posts&lt;/td&gt;
&lt;td&gt;기본 레이아웃&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;draft&lt;/td&gt;
&lt;td&gt;source/_drafts&lt;/td&gt;
&lt;td&gt;게시되지 않는 초안&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;page&lt;/td&gt;
&lt;td&gt;source&lt;/td&gt;
&lt;td&gt;새로운 페이지를 작성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Post (포스트)&lt;/h3&gt;
&lt;p&gt;기본 레이아웃으로 레이아웃 종류를 입력하지 않으면 자동으로 포스트로 인식합니다.&lt;br&gt;기본 레이아웃의 변경은 &lt;code&gt;_config.yml&lt;/code&gt; 의 default_layout 항목에서 변경 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hexo new post &amp;lt;포스트이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;page (페이지)&lt;/h3&gt;
&lt;p&gt;새글을 추가하는 것이아니라 별도의 페이지를 생성합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hexo new page &amp;lt;페이지이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;draft (초안)&lt;/h3&gt;
&lt;p&gt;포스트를 바로 발행 하는 것이 아닌 초안 형태로 저장합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hexo new draft &amp;lt;포스트이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;기본 레이아웃 변경을 &lt;code&gt;_config.yml&lt;/code&gt; 의 default_layout 항목에서 draft로 변경하면,&lt;br&gt;&lt;code&gt;hexo new &amp;lt;포스트이름&amp;gt;&lt;/code&gt; 명령어 실행시 post가 아닌 draft로 생성됩니다.&lt;/p&gt;
&lt;p&gt;초안으로 작성된 글은 로컬서버에서만 확인 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hexo server --draft&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;초안으로 작성된 글을 post로 변경할려면 수동으로 &lt;code&gt;_posts&lt;/code&gt;폴더로 옮길 수도 있지만&lt;br&gt;아래와 같은 hexo 명령어로 publish가 가능하다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hexo publish &amp;lt;포스트이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;스캐폴드 (Scaffolds)&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;포스트의 구조를 설정할때 사용합니다. &lt;code&gt;Scaffolds&lt;/code&gt;폴더 안에 각 md파일을 수정하여 설정을 변경할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;---
title: {{ title }}
date: {{ date }}
tags:
---&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;기본적인 구조는 위와 같이 되어있고 각 항목들을 추가하여 기본값을 설정해 줍니다.&lt;br&gt;저 같은 경우에는 태그, 카테고리, 썸네일 등을 추가로 작성해 놓았습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;---
title: {{ title }}
date: {{ date }}
categories:
tags: [ ]
thumbnail: /img/ 
---&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;머리말 (Front matter)&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;머리말(Front matter)은 포스트 생성시 포스트 최상단에 자동으로 생성되며 해당 파일의 정보를 입력하는 곳입니다. 형태는 &lt;code&gt;---&lt;/code&gt;로 구분되어 있습니다.&lt;/p&gt;
&lt;h3&gt;항목&lt;/h3&gt;
&lt;p&gt;사용하는 항목들을 스캐폴더에 미리 지정해 놓으면 편리하게 사용 할 수 있습니다.&lt;br&gt;&lt;a href=&quot;https://hexo.io/ko/docs/front-matter.html&quot;&gt;Docs : https://hexo.io/ko/docs/front-matter.html&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;설정&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;th&gt;기본값&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;layout&lt;/td&gt;
&lt;td&gt;레이아웃&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;title&lt;/td&gt;
&lt;td&gt;타이틀&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;date&lt;/td&gt;
&lt;td&gt;발행일&lt;/td&gt;
&lt;td&gt;파일이 생성된 날짜&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;updated&lt;/td&gt;
&lt;td&gt;갱신일&lt;/td&gt;
&lt;td&gt;파일이 업로드된 날짜&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;comments&lt;/td&gt;
&lt;td&gt;포스트에서 comment 기능을 사용할지 여부&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tags&lt;/td&gt;
&lt;td&gt;태그 (page에서는 사용 불가능)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;categories&lt;/td&gt;
&lt;td&gt;카테고리 (page에서는 사용 불가능)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;permalink&lt;/td&gt;
&lt;td&gt;포스트의 기본 permalink를 override합니다.&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;카테고리와 태그 (categories,tags)&lt;/h3&gt;
&lt;p&gt;카테고리와 태그는 Post와 Draft에서만 사용 가능합니다. 카테고리를 여러가지 지정할 경우 분류와 하위 분류의 계층적인 관계를 가집니다. 즉 위에 있는게&lt;br&gt;메인카테고리 아래에 있는게 서브카테고리가 됩니다. 태그는 순서와 상관없이 모두 같은 계층입니다.&lt;/p&gt;
&lt;p&gt;여러개를 작성할 경우 아래와 같이 사용 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;categories:
- 카테고리이름
- 서브카테고리이름
tags:
- 태그
- 태그
- 태그

#또는 아래와 같이도 사용 할 수 있습니다.
categories: [카테고리이름, 서브카테고리이름]
tags:[태그, 태그, 태그]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;머리말(Front matter)의 항목은 기본적으로 제공하는 것 외에 각 테마들마다 다양한 옵션들을 가지고 있습니다.자신에게 맞는 항목들을 찾아서 사용하시면 됩니다.&lt;/p&gt;
&lt;h3&gt;요약글&lt;/h3&gt;
&lt;p&gt;기본적으로 요약글 형태로 보여줄수 있는 기능을 제공합니다. &lt;code&gt;&amp;lt; !-- more --&amp;gt;&lt;/code&gt;을 사용해서 요약글을 만들수 있습니다. 테마에 따라 첫페이지에서 자동으로 요약글 형태로 만들어 주는 테마도 있지만&lt;br&gt;icarus 테마의 경우에는 &lt;code&gt;&amp;lt; !-- more --&amp;gt;&lt;/code&gt; 기능을 사용합니다. 사용방법은 포스트 본문에 첫페이지에서 보여줄 부분 밑에 &lt;code&gt;&amp;lt; !-- more --&amp;gt;&lt;/code&gt;을 추가하면 &lt;code&gt;read more&lt;/code&gt; 버튼이 생성되고 아래의 내용은 보여지지 않습니다.&lt;br&gt;사용하실 때 띄어쓰기는 제거해 주셔야 합니다.&lt;/p&gt;
&lt;p&gt;포스트의 작성이 끝나면 Generate와 Deploy 실행하여 업데이트 해주면 됩니다.&lt;br&gt;포스트의 적용이 바로 되지 않을 경우에는 Clean을 같이 실행 해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hexo clean
hexo d -g&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Programming</category>
      <category>hexo</category>
      <category>글쓰기</category>
      <category>블로그</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/5</guid>
      <comments>https://mishka.tistory.com/5#entry5comment</comments>
      <pubDate>Wed, 25 Aug 2021 23:48:16 +0900</pubDate>
    </item>
    <item>
      <title>Hexo 블로그 Github를 이용해 만들어보기</title>
      <link>https://mishka.tistory.com/4</link>
      <description>&lt;p&gt;&lt;del&gt;블로그를 만들고 꾸미다 보니 나같은 사람들이 많을 거라는 생각에서&lt;/del&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hexo를 이용해 Github 블로그 세팅 하는 방법들을 처음부터 하나씩 보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Hexo에서 티스토리로 이전하였다.)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hexo&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Hexo는 Node.js 기반의 정적 사이트 생성기(Static site generator)이다. 포스트의 경우 md파일로 작성이 가능하고 github를 이용하면 무료로&lt;br /&gt;블로그 운영이 가능하다. md로 작성하기 때문에 대부분에 IDE에서 작성하면서 미리보기를 할 수 있으며 node를 이용하면 로컬에서 확인 가능하다.&lt;br /&gt;공식 사이트에서는 빠르고 간단하고 파워풀한 블로그 프레임워크라고 소개하고 있다.&lt;br /&gt;npm을 통해 쉽게 설치가 가능하고 배포역시 쉽게 가능한 것이 장점이다.&lt;br /&gt;한글문서로도 잘 정리가 되어있어 참고하기 좋다. &lt;a href=&quot;https://hexo.io/ko/docs/&quot;&gt;Hexo 한글 튜토리얼&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치 전 준비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 Github를 이용하여 Hexo 블로그를 세팅 하려고 하기때문에 git, github, node 설치가 선행되어야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&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://git-scm.com/&quot;&gt;Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nodejs.org/en/&quot;&gt;Node.js(npm)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/&quot;&gt;Github 계정&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 설치 방법들은 간단하고 잘 쓰여진 글들이 많으니 참고하시기 바랍니다.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hexo 설치하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node.js를 설치하면 사용 할 수 있는 npm을 이용해서 간편히 설치 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;npm install hexo-cli -g&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;블로그 Setup&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령을 실행하여 간단하게 hexo를 초기화 할수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;hexo init &amp;lt;프로젝트이름&amp;gt;
cd &amp;lt;프로젝트이름&amp;gt;
npm install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화가 완료되면 아래와 같은 구조로 세팅됩니다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;.
├── _config.yml
├── package.json
├── scaffolds
├── source
|   ├── _drafts
|   └── _posts
└── themes&lt;/code&gt;&lt;/pre&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;_config.yml&lt;/code&gt;이 root경로에 생성되는데 해당 파일을 통해서 블로그에 대한 설정을 세팅할 수 있습니다.&lt;br /&gt;자세한 설명은 Hexo의 Cofiguration을 통해 확인해 볼수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cofiguration : &lt;a href=&quot;https://hexo.io/ko/docs/configuration&quot;&gt;https://hexo.io/ko/docs/configuration&lt;/a&gt;&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;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;hexo server
hexo s  #단축명령어&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 구동되면 아래와 같은 문구가 출력되며 &lt;a href=&quot;http://localhost:4000&quot;&gt;http://localhost:4000&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;Hexo is running at http://localhost:4000 . Press Ctrl+C to stop.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Github 블로그 호스팅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github에서 새로운 repository를 생성합니다.&lt;br /&gt;Repository name을 &lt;code&gt;username.github.io&lt;/code&gt;의 형식으로 기재하고 public으로 만들어 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;573&quot; data-filename=&quot;스크린샷 2021-08-25 오후 11.43.24.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGaEVL/btrc9dm6iUk/3u9sdDvFaK8wXIfU5sbnW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGaEVL/btrc9dm6iUk/3u9sdDvFaK8wXIfU5sbnW1/img.png&quot; data-alt=&quot;github에서 repository 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGaEVL/btrc9dm6iUk/3u9sdDvFaK8wXIfU5sbnW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGaEVL%2Fbtrc9dm6iUk%2F3u9sdDvFaK8wXIfU5sbnW1%2Fimg.png&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;573&quot; data-filename=&quot;스크린샷 2021-08-25 오후 11.43.24.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;github에서 repository 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&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;_config.yml&lt;/code&gt; 에서 &lt;code&gt;Deployment&lt;/code&gt; 항목을 설정해 준다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;# Deployment
deploy: 
  type: git
  repo: repository 주소
  branch: master &lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Github에 배포하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬에서 정상적으로 작동되었다면 hexo cli를 통해 Github에 쉽게 배포를 해보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정적리소스 생성&lt;/h3&gt;
&lt;pre class=&quot;verilog&quot;&gt;&lt;code&gt;hexo generate 
hexo g  #단축 명령어&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;public 폴더가 생성되어 배포가 가능하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배포하기&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;hexo deploy
hexo d  #단축 명령어&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 중 &lt;b&gt;오류&lt;/b&gt;가 발생 하면 아래 &lt;a href=&quot;#%EC%9D%B4%EC%8A%88%EC%B2%B4%ED%81%AC&quot;&gt;이슈체크&lt;/a&gt;를 확인&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Generate와 Deploy 동시실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 Generate와 Deploy를 동시실행 할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;verilog&quot;&gt;&lt;code&gt;hexo deploy --generate  #Deploy 전에 Generate를 해준다.
hexo d -g  #단축 명령어

hexo generate --deploy  #Generate 후에 Deploy를 해준다.
hexo g -d  #단축 명령어&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://username.github.io&quot;&gt;https://username.github.io&lt;/a&gt;&lt;/b&gt; 로 접속하면 deploy된 사이트가 확인 가능하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이슈체크&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배포시 아래와 같은 에러가 발생하면&lt;/h3&gt;
&lt;pre class=&quot;subunit&quot;&gt;&lt;code&gt;ERROR Deployer not found: git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;hexo-deployer-git&lt;/code&gt; 플러그인을 설치해준다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;npm install hexo-deployer-git --save&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;업데이트 시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간혹 업데이트가 바로 이루어 지지 않을 경우에는 아래와 같이 public을 clean시켜준 후 배포를 하면 된다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;hexo clean
hexo d -g&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;글쓰기&lt;/h3&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;hexo new post &amp;lt;포스트이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;source/_post 폴더에 포스트이름.md 으로 새로운 마크다운 파일이 생성됩니다.&lt;br /&gt;포스트내용을 작성한후에 다시한번 clean과 generate 그리고 deploy를 해주면 정상 반영된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 Hexo 블로그 만들기에 대한 포스팅을 마친다.&lt;br /&gt;잘못된 사항이나 궁금한 점은 댓글로 남겨주시면 감사하겠습니다.&lt;/p&gt;</description>
      <category>Programming</category>
      <category>github</category>
      <category>hexo</category>
      <category>블로그</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/4</guid>
      <comments>https://mishka.tistory.com/4#entry4comment</comments>
      <pubDate>Wed, 25 Aug 2021 23:40:58 +0900</pubDate>
    </item>
    <item>
      <title>Git의 기본적인 명령어</title>
      <link>https://mishka.tistory.com/3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;git의 기본적인 명령어들을 정리해보려고 합니다.&lt;br /&gt;모든 명령어들을 정리하는 것이 아닌 많이 쓰이는 명령어들을 위주로 정리 해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그전에 먼저 간단하게 git이란 무엇인가? 그리고 git에서 사용하는 용어들을 정리해 보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git이란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git은 컴퓨터 파일의 변경사항을 추적하고 여러 명의 사용자들 간에 해당 파일들의 작업을 조율하기 위한 분산 버전 관리 시스템이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/Git&quot;&gt;위키백과&lt;/a&gt;&lt;br /&gt;위키 백과에서는 위와 같이 말하고 있다. 그 중에서 주위깊게 보아야 할 단어들은 &lt;u&gt;&lt;b&gt;&quot;추적, 분산 버전 관리 시스템&quot;&lt;/b&gt;&lt;/u&gt; 정도이다.&lt;br /&gt;git은 2005년 리누스 토르발스가 리눅스 커널 개발을 위해 처음 개발하였다.&lt;br /&gt;많은 버전 관리 시스템(VCS : Version Control System)중에 하나인데 다른 버전 관리 시스템과 다른점은&lt;br /&gt;저장소를 분산해서 관리 한다는 점이다. 즉 중앙 저장소에 문제가 생겨도 로컬저장소를 이용하여 복원이 가능하다는 것이다.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git에서 사용하는 용어들&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Working tree : 현재 작업하고 있는 공간, git이 관리하고 있지만 아직 추적(track)하고 있지 않은 상태 (또는 Working Directory)&lt;/li&gt;
&lt;li&gt;Staging area : 커밋 하기전 staged 된 파일들이 있는 공간,수정한 파일을 커밋하기 전에 표시(add)해둔 공간&lt;/li&gt;
&lt;li&gt;Commit : 작업한 내용을 로컬 저장소에 저장하는 것을 의미합니다.&lt;/li&gt;
&lt;li&gt;Repository : 저장소를 의미 합니다. 저장소에는 2가지가 있습니다.&lt;br /&gt;-Local Repository : 본인 PC에 존재하는 저장소를 의미합니다.&lt;br /&gt;-Remote Repository : Github, Gitlab과 같은 원격저장소를 의미합니다.&lt;/li&gt;
&lt;li&gt;Push : 로컬저장소(Local Repository) 에서 Commit이 완료된 내용을 원격저장소에 업로드 하는 것.&lt;/li&gt;
&lt;li&gt;Clone : 원격저장소의 내용을 통째로 다운로드 하는 것을 말합니다.&lt;/li&gt;
&lt;li&gt;Branch : 가지 또는 분기점을 의미, 현재 상태를 복사하여 자신만의 branch를 만들수 있고 작업이 완료되면 merge를 통해 합칠 수 있다.&lt;/li&gt;
&lt;li&gt;Checkout : 특정 시점이나 브런치로 이동하는 것을 의미&lt;/li&gt;
&lt;li&gt;Fetch : master나 다른 branch에서 작업한 내용이 내 로컬저장소와 버전이 맞지 않을때 최신버전으로 업데이트&lt;/li&gt;
&lt;li&gt;Merge : 다른 branch의 내용을 현재 branch로 가져와 합치는 작업&lt;/li&gt;
&lt;li&gt;Pull : 원격저장소의 변경된 내용이 로컬저장소에 반영됩니다.(fetch + merge)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git에서 일어나는 상태변화&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;637&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RLd8T/btrdcPNpJcE/fslRWHi5ZoFmoiUmxGEPTk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RLd8T/btrdcPNpJcE/fslRWHi5ZoFmoiUmxGEPTk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RLd8T/btrdcPNpJcE/fslRWHi5ZoFmoiUmxGEPTk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRLd8T%2FbtrdcPNpJcE%2FfslRWHi5ZoFmoiUmxGEPTk%2Fimg.jpg&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;637&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림은 git을 사용하면서 일어나는 트랜젝션을 정리해 본 그림이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git 명령어&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본격적으로 git 명령어 들에 대해 알아보자&lt;br /&gt;모든 명령어를 나열하는 것은 아니고 주로 사용하는 명령어를 기준으로 써보고자 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git config&lt;/h3&gt;
&lt;pre class=&quot;verilog&quot;&gt;&lt;code&gt;git config --global --list   // 현재 설정 정보를 조회합니다.
git config --global user.name &quot;사용자명&quot;   // 사용자명을 등록합니다.
git config --global user.email &quot;이메일주소&quot;   // 사용자 이메일주소을 등록합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본명령어&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git init&lt;/h3&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;git init  // 깃 저장소를 초기화 한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 디텍토리에서 git 저장소를 생성합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git status&lt;/h3&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;git status  // 저장소 상태 보기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장소의 상태를 체크 합니다. Commit이 필요한 변경사항이 있는지, 현재 저장소의 어떤 브런치를 작업하고 있는지 등을 볼 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git diff&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;git diff  // 스테이징 영역과 현재 작업트리의 차이점을 보여준다.

옵션
HEAD  // 저장소, 스테이징 영역, 작업트리의 차이점을 모두 볼수 있다.
--cached  // 스테이징 영역과 저장소의 차이점을 볼 수 있다.
--stat  // 변경사항에 대한 통계를 볼 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git add&lt;/h3&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;git add &amp;lt;파일이름&amp;gt;  // 파일단위로 선택

git add .  // 모든 파일 추가
  또는
git add *

옵션 
-i  // 대화형모드 실행, 일부분만 선택하여 스테이징 가능
-p  // 대화형 모드 없이 바로 패치모드 사용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 파일 또는 변경된 모든 파일을 스테이징 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git commit&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;git commit
git commit -m &quot;커밋메세지&quot;
git commit -a  // 스테이지에 올리는 것과 커밋을 동시에 진행&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git revert&lt;/h3&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;git revert &amp;lt;커밋아이디&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 커밋에서 변경한 내용을 취소해서 새로운 커밋을 만듭니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git reset&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;git reset --&amp;lt;커밋아이디&amp;gt;  // 커밋 취소
git reset --hard &amp;lt;커밋아이디&amp;gt;  // 해당 위치로 되돌림
git reset --hard HEAD  // 마지막 커밋 상태로 되돌림&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git log&lt;/h3&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;git log

옵션
-3  // 출력할 커밋로그의 갯수를 지정 할 수 있습니다.
--oneline  // 한 줄로 간단하게 보여줍니다.
--graph  // 브런치 트리를 볼 수 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커밋로그들을 볼 수 있습니다. 여러가지 옵션들을 적절히 사용하면 자신이 원하는 로그만을 볼 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Branch 명령어&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;branch 목록&lt;/h3&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;git branch  // 로컬 브런치
git branch -v  // 해당 브런치의 commit ID도 볼 수 있다.
git branch -r  // 리모트 브런치
git branch -a  // 로컬, 리모트 포함된 모든 브런치 &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브런치목록을 보여줍니다. 옵션을 통해 원하는 저장소의 브런치를 볼 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;branch 생성&lt;/h3&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;git branch &amp;lt;브런치이름&amp;gt;  // 원하는 이름으로 브런치를 생성한다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;branch 삭제&lt;/h3&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;git branch -d &amp;lt;브런치이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git checkout&lt;/h3&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;git checkout &amp;lt;브런치이름&amp;gt;  // 활성 브런치 변경&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git merge&lt;/h3&gt;
&lt;pre class=&quot;cos&quot;&gt;&lt;code&gt;git merge &amp;lt;브런치이름&amp;gt;  // 브런치이름의 내용을 현재 브런치로 합친다.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원격저장소&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git clone&lt;/h3&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;git clone &amp;lt;저장소주소&amp;gt; &amp;lt;폴더명&amp;gt;  //원격저장소를 복제하여 저장소를 생성합니다.(폴더명은 생략가능)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git remote&lt;/h3&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;git remote add &amp;lt;저장소이름&amp;gt; &amp;lt;저장소주소&amp;gt;  // 원격저장소 연결
git remote -v  // 연결된 원격 저장소를 보여줍니다. 
git remote rm &amp;lt;저장소이름&amp;gt;  // 원격저장소를 제거합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git fetch&lt;/h3&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;git fetch &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격저장소의 변경사항을 가져와서 갱신합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git pull&lt;/h3&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;git pull 
git pull -rebase &amp;lt;저장소이름&amp;gt; &amp;lt;브런치이름&amp;gt;  // 커밋메세지를 남기지 않는다.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git push&lt;/h3&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;git push  // 원격저장소에 업로드 한다.
git push &amp;lt;저장소이름&amp;gt; &amp;lt;브런치이름&amp;gt;
git push origin master&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터 값을 주지 않으면 origin 저장소에 푸싱하며 현재 지역브런치와 같은 이름으로 브런치에 푸싱합니다.&lt;/p&gt;</description>
      <category>Git</category>
      <category>GIT</category>
      <category>명령어</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/3</guid>
      <comments>https://mishka.tistory.com/3#entry3comment</comments>
      <pubDate>Wed, 25 Aug 2021 23:17:17 +0900</pubDate>
    </item>
    <item>
      <title>마크다운 문법 기본</title>
      <link>https://mishka.tistory.com/2</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;마크다운(Markdown) 이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마크다운(Markdown)은 일반 텍스트 문서의 양식을 편집하는 문법입니다.&lt;br /&gt;README 파일이나 온라인 문서, 혹은 일반 텍스트 편집기로 문서 양식을 편집할 때 쓰입니다. 비교적 문법이 간단하다보니 여러곳에서 쓰이는것 같습니다.&lt;br /&gt;마크다운을 이용해 작성된 문서는 쉽게 HTML 등 다른 문서 형태로 변환이 가능합니다.&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4&quot;&gt;위키백과&lt;/a&gt;&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마크다운의 장점과 단점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문법이 쉽고 간결합니다.&lt;/li&gt;
&lt;li&gt;별도의 도구가 필요없다.&lt;/li&gt;
&lt;li&gt;지원하는 플랫폼과 프로그램이 다양하다.&lt;/li&gt;
&lt;li&gt;다양한 형태로 변환이 가능하다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;표준이 없고 그로 인해 사용자마다 문법이 다르다.&lt;/li&gt;
&lt;li&gt;모든 HTML 마크업을 대신 할 수 없다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마크다운 문법(Syntax)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제목 (Header)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML의 &lt;code&gt;&amp;lt; h1 &amp;gt;&lt;/code&gt;부터 &lt;code&gt;&amp;lt; h6 &amp;gt;&lt;/code&gt;으로 변환되며 제목을 표현할 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;# 제목1
## 제목2
### 제목3
#### 제목4
##### 제목5
###### 제목6&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수평선 (Horizontal rule)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML의 &lt;code&gt;&amp;lt; hr &amp;gt;&lt;/code&gt; 태그로 변환됩니다.&lt;br /&gt;수평선을 표시하는 방법은 여러가지가 있다.&lt;br /&gt;각 기호를 3번 이상 입력하는 방법으로 사용한다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;--- (Hyphens)
*** (Asterisks)
___ (Underscores)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인용문구 (Blockquote)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML의 &lt;code&gt;&amp;lt; blockquote &amp;gt;&lt;/code&gt; 태그로 변환됩니다.&lt;br /&gt;중첩해서 사용이 가능합니다.&lt;br /&gt;인용안에서 마크다운 문법을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;&amp;gt; 안용문구를 나타낼때 사용합니다.
&amp;gt;&amp;gt; 중첩해서 사용이 가능합니다.
&amp;gt;&amp;gt;&amp;gt; ## 인용의 인용&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;목록 (List)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML의 &lt;code&gt;&amp;lt; ol &amp;gt;&lt;/code&gt;,&lt;code&gt;&amp;lt; ul &amp;gt;&lt;/code&gt;,&lt;code&gt;&amp;lt; li &amp;gt;&lt;/code&gt; 태그로 변환됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;순서있는 목록 (ol)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서있는 목록은 숫자와 점을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 첫번째
2. 두번째
3. 세번째&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*특이한 점은 어떤 번호를 입력해도 순서는 내림차순으로 정의가 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;순서없는 목록 (ul)&lt;/h4&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;- 대쉬 (hyphen)
* 별표 (asterisks)
+ 더하기 (plus sign)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;강조 (Emphasis)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML의 &lt;code&gt;&amp;lt; em &amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt; strong &amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt; del &amp;gt;&lt;/code&gt; 태그로 변환됩니다.&lt;br /&gt;&lt;u&gt;밑줄&lt;/u&gt; 은 &lt;code&gt;&amp;lt; u &amp;gt;&amp;lt; /u &amp;gt;&lt;/code&gt; 태그를 사용하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;이텔릭체: *텍스트* 또는 _텍스트_ 를 사용
두껍게: **텍스트** 또는 __텍스트__ 를 사용
취소선: ~~텍스트~~ 를 사용
&amp;lt; u &amp;gt;밑줄&amp;lt; /u &amp;gt; 은  &amp;lt; u &amp;gt;&amp;lt; /u &amp;gt; 태그를 사용&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;링크 (Link)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML의 &lt;code&gt;&amp;lt; a &amp;gt;&lt;/code&gt; 태그로 변환됩니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;[링크](링크주소)
[링크](링크주소 &quot;링크설명- 생략가능&quot;)

문서안에서 [참조링크] 사용가능합니다.
[참조링크노출설명][참조링크] 의 형태로 사용
[참조링크]: 참조링크주소 &quot;링크설명 - 생략가능&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이미지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML의 &lt;code&gt;&amp;lt; img &amp;gt;&lt;/code&gt; 태그로 변환됩니다.&lt;br /&gt;링크와 비슷하지만 앞에 !가 붙습니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;![대체텍스트](링크 &quot;링크설명&quot;)&lt;/code&gt;&lt;/pre&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;pre class=&quot;markdown&quot;&gt;&lt;code&gt;[![대체텍스트](링크 &quot;링크설명&quot;)](링크주소)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTML 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마크다운 문법 뿐 아니라 원시 HTML문법을 사용할 수 있습니다.&lt;/p&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;</description>
      <category>Markdown</category>
      <category>markdown</category>
      <category>MD</category>
      <category>기본</category>
      <category>마크다운</category>
      <category>문법</category>
      <author>Mishka</author>
      <guid isPermaLink="true">https://mishka.tistory.com/2</guid>
      <comments>https://mishka.tistory.com/2#entry2comment</comments>
      <pubDate>Tue, 4 May 2021 19:04:58 +0900</pubDate>
    </item>
  </channel>
</rss>