<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jae_han_e.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 23 Jan 2026 02:03:04 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jae_han_e.log</title>
            <url>https://velog.velcdn.com/images/jae_han_e/profile/30d18af2-a16f-4e69-90d6-0f8fa8d47986/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jae_han_e.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jae_han_e" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[AI가 개발자를 대체할 수 있을까?]]></title>
            <link>https://velog.io/@jae_han_e/wh7bsvsd</link>
            <guid>https://velog.io/@jae_han_e/wh7bsvsd</guid>
            <pubDate>Fri, 23 Jan 2026 02:03:04 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>생성형 AI가 개발에 도입되던 초반만 하더라도, AI는 개발 생산성을 높여주는 도구일 뿐
설계와 판단의 영역은 개발자의 몫이라는 의견이 대다수였다.🤓</p>
<p>하지만 그로부터 불과 몇 년 사이에 모델의 성능은 눈에 띄게 향상 되었고
AI는 단순 보조를 넘어, 문제를 이해하고 해결하는 방법을 제안하는 단계에 접어들었다.
또한, 국내외 기업에서는 인력 규모를 조정하거나 AI를 활용(개발)할 수 있는 인재를 선별하기 시작했다.🕴</p>
<p>이러한 변화를 지켜보며 <strong><code>&quot;AI는 어디까지 발전할 수 있으며, 그 과정에서 개발자의 역할은 어떻게 바뀌게 될까?&quot;</code></strong> 라는 생각이 들었다.
이 질문에 대한 답을 찾는 과정에서 <strong>&quot;Open AI가 제시한 AGI로 가는 5단계 로드맵&quot;</strong> 을 발견했다.😀</p>
<p>이 로드맵을 기준으로 현재 AI의 위치와 한계를 정리해 보고
향후 개발자의 역할에 대해 생각해보고자 한다.🧐</p>
<h1 id="agi란-무엇인가">AGI란 무엇인가?</h1>
<p><strong>A</strong>rtificial <strong>G</strong>eneral <strong>I</strong>ntelligence 의 약자로
하나의 모델이 특정 도메인에 한정되지 않고 새로운 문제를 이해하고
학습하고, 해결할 수 있는 <strong><code>범용 인공지능</code></strong> 이다.🧑🏻‍🏫</p>
<p>현재 사용하고 있는 AI는 코드는 잘 작성해 주지만 <strong>&quot;왜 이 시스템이 필요한지&quot;</strong> 는 판단하지 않고
오류는 잘 해결해주지만 <strong>&quot;이 설계가 맞는지&quot;</strong> 는 제대로 판단하지 못한다.</p>
<p>AGI는 인간과 동등한 수준의 지능, 창의성을 갖추고
<strong><code>스스로 문제를 정의하고 해결하는 것을 목표</code></strong> 로 하고 있다.💻</p>
<h1 id="agi로-가는-5단계-로드맵from-open-ai">AGI로 가는 5단계 로드맵(from. Open AI)</h1>
<p>Open AI가 제시한 5단계는 업무 단위로 AI가 어디까지 자동화를 할 수 있을지를
가늠하게 해주는 로드맵으로 볼 수 있다.</p>
<p>개발자의 관점에서
☝🏻 AI가 코드 생성을 넘어 문제 해결까지 할 수 있는가?
✌🏻 사람 없이도 스스로 일을 끝낼 수 있는가?
🤟🏻 결정과 책임의 영역까지 침범하는가?</p>
<p>를 생각하며 각 단계를 살펴보자 😎</p>
<h3 id="1단계-conversational-ai">1단계 <strong>Conversational AI</strong></h3>
<p>AI는 대화형 도구로서 <strong>검색 + 문서읽기 + 보일러플레이트 작성 등의 역할을 도와 생산성을 올려준다.</strong>
무엇을 만들지, 어떤 방식이 적절한지는 개발자의 판단에 달려있다.😀</p>
<ul>
<li>주요내용</li>
<li>반복 코드 생성 (CRUD, DTO/VO, API 라우팅, 컴포넌트 등)</li>
<li>정규식, SQL, 스크립트 작성 보조</li>
<li>라이브러리 사용 예시, 코드리뷰</li>
</ul>
<h3 id="2단계-reasoners">2단계 <strong>Reasoners</strong></h3>
<p><strong>복잡한 문제를 구성 요소로 분해하고, 원인과 결과의 관계를 분석해 해결 방안을 논리적으로 추론해 제시한다.</strong>
이제는 코드만 제공해주는 도구를 넘어 디버깅과 분석을 돕는 동료에 가까워진다.
하지만 이 추론이 항상 옳지는 않기 때문에 판단과 책임은 개발자에게 남아 있다.😃</p>
<ul>
<li>주요내용</li>
<li>복잡한 버그 발생 경로 추론 및 원인 후보군 정리</li>
<li>성능 테스트 결과 예측</li>
<li>테스트 실패 원인 분석</li>
</ul>
<h3 id="3단계-agents">3단계 <strong>Agents</strong></h3>
<p>하나의 작업 단위를 스스로 끝내는 실행의 주체가 되는 단계이다.
업무를 부여하면 <strong>계획 &gt; 실행 &gt; 검증 &gt; 반영 을 자율적으로 수행한다.</strong>
이에따라 결과에 대한 통제와 검증이 더 중요해지며, 개발자의 역할이 작성자에서 감독자로 이동하는 지점이 된다.🫢</p>
<ul>
<li>주요내용</li>
<li>브랜치 생성 &gt; 코드 수정 &gt; 테스트 &gt; PR 생성 까지 자동화</li>
<li>코드베이스 분석 후 사이드 이펙트를 고려한 이슈 해결</li>
</ul>
<h3 id="4단계-innovators">4단계 <strong>Innovators</strong></h3>
<p><strong>기존 해결책을 조합하는 수준을 넘어, 스스로 학습한 내용을 바탕으로 새로운 접근 방식과 설계 대안을 제시한다.</strong>
하지만, AI가 제시하는 대안이 기술적으로 타당하더라도 조직의 상황, 일정, 비즈니스 우선순위 까지 고려한 것은 아니다.
따라서, 의사결정의 선택지를 넓혀주는 조언자에 가깝다.😊</p>
<ul>
<li>주요내용</li>
<li>성능과 비용 최적화에 대한 창의적 전략 제시</li>
<li>장기적인 확장성을 고려한 아키텍처 제시</li>
<li>장애 예방을 위한 기존 시스템 보완점 제시</li>
</ul>
<h3 id="5단계-organizations">5단계 <strong>Organizations</strong></h3>
<p><strong>개별 작업이나 기능을 넘어 하나의 조직처럼 행동하는 단계이다.</strong>
기존 주어진 요구사항을 수행하는 것이 아니라 스스로 문제를 정의하고
우선순위를 정해 설계, 개발, 개선을 하나의 흐름으로 관리할 수 있다. 🥸</p>
<p>이론적으로는
☝🏻 어떤 기능을 만들어야 하는지 판단하고
✌🏻 일정과 리스크를 고려해 계획을 수립하여
🤟🏻 개발과 배포, 운영까지 스스로 수행하는
AI조직이 가능해진다.</p>
<p>이 단계에 이르면 비로소 &quot;AI가 개발자를 대체할 수 있는가?&quot;
라는 질문이 현실적인 의미를 갖는다.</p>
<p>하지만, 법적 책임, 윤리, 통제, 신뢰의 문제가 남아있다.</p>
<ul>
<li>장애가 발생했을 때 책임은 누구에게 있는가?</li>
<li>잘못된 의사결정을 어떻게 되돌릴 것인가?</li>
<li>AI의 판단 과정을 어떻게 설명하고 검증할 것인가?</li>
</ul>
<p>이와 같은 문제들이 해결되지 않는 한 단기간에 현실화되긴 어려울 것이라고 생각한다.</p>
<h2 id="정리">정리</h2>
<p>이 로드맵을 기준으로 살펴보면, AI가 단기간에 개발자를 ㄹ완전히 대체할 가능성은 높지 않아 보인다.
다만, AI는 이미 코드 작성을 넘어 분석과 실행의 영역까지 진입하고 있으며
그에 따라 개발자의 역할은 분명히 변화하고 있다.</p>
<p>개발자는 더 이상 모든 코드를 직접 작성하는 사람이 아니라 AI가 수행한 작업을
검토하고 방향을 조율하며 결과에 책임을 지는 역할로 이동하고 있다.
이 과정에서 문제를 정의하고 설계 의도를 판단하는 역량은 더욱 중요해 질 것이다.</p>
<p>즉, <strong>AI와 경쟁하는 개발자가 아니라, AI를 전제로 사고하고 설계할 수 있는 개발자가 되어야 할 것이다.</strong> 💪🏻💪</p>
<h2 id="참고">참고</h2>
<p><a href="https://briansolis.com/2024/08/ainsights-openai-defines-five-stages-to-track-progress-toward-human-level-intelligence/">AGI로 가는 5단계 로드맵</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js 병렬 라우팅으로 안드로이드 Back 버튼 UX 개선하기]]></title>
            <link>https://velog.io/@jae_han_e/Next.js-%EB%B3%91%EB%A0%AC-%EB%9D%BC%EC%9A%B0%ED%8C%85%EC%9C%BC%EB%A1%9C-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Back-%EB%B2%84%ED%8A%BC-UX-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jae_han_e/Next.js-%EB%B3%91%EB%A0%AC-%EB%9D%BC%EC%9A%B0%ED%8C%85%EC%9C%BC%EB%A1%9C-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Back-%EB%B2%84%ED%8A%BC-UX-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 12 Jan 2026 08:32:07 GMT</pubDate>
            <description><![CDATA[<h1 id="문제의-인식">문제의 인식</h1>
<h2 id="상황-📝">상황 📝</h2>
<p>모바일 웹 또는 WebView 환경에서 팝업이나 모달을 띄운 상태로 안드로이드 Back 버튼을 누르면 사용자가 기대하는 동작은 대부부 아래와 같다.</p>
<p><code>&quot;현재 화면에 떠 있는 팝업이나 모달이 닫힌다.&quot;</code></p>
<p>하지만 실제로는 <strong>팝업이 닫히지 않고, 이전 페이지로 이동하는 현상이 발생</strong>했다. 🚨</p>
<p>이는 단순한 UX 불편을 넘어</p>
<ul>
<li>사용자가 작업하던 맥락이 끊긴다 (데이터가 초기화됨)</li>
<li>다시 페이지에 진입해야 하는 상황이 발생한다.</li>
</ul>
<h2 id="원인-분석-🧐">원인 분석 🧐</h2>
<h3 id="1-팝업--모달은-히스토리에-쌓이지-않는다">1. 팝업 &amp; 모달은 히스토리에 쌓이지 않는다.</h3>
<p>대부분의 경우, 팝업이나 모달은 다음과 같이 구현한다.</p>
<ul>
<li>useState 를 이용한 조건부 렌더링</li>
<li>CSS(z-index, position)로 화면 위에 오버레이</li>
</ul>
<p>이 경우, 라우팅과 관계가 없으므로 <strong>브라우저 히스토리에 아무것도 쌓이지 않는다.</strong> 🙅🏻‍♂️</p>
<h3 id="2-안드로이드-back-버튼의-동작">2. 안드로이드 Back 버튼의 동작</h3>
<p>안드로이드 시스템에서 Back 버튼을 실행하면 (버튼 터치 or 모션)
웹 관점에서는 <code>window.history.back()</code>이 수행된다.</p>
<p>즉, 히스토리에 쌓여있는 스택을 기준으로 이전 엔트리로 이동한다. 🔙</p>
<h1 id="해결방안-💡">해결방안 💡</h1>
<p>Next.js의 <code>병렬 라우팅</code>을 활용해서 <strong>팝업과 모달을 페이지</strong>로 만들어 준다.</p>
<p>페이지로 렌더링되기 때문에 히스토리에 쌓여 <code>window.history.back()</code> 으로 제어가 가능하다.</p>
<p>이번 포스팅에서는 <code>버튼 팝업</code>과, <code>바텀업 다이얼로그</code>를 구현해 볼 예정이다.😎</p>
<p>먼저, 병렬 라우팅이 무엇인지 간단하게 알아보자 📖</p>
<h2 id="병렬-라우팅-parallel-routes-이란">병렬 라우팅 (Parallel Routes) 이란?</h2>
<p>병렬 라우팅은 특정 경로에 들어 왔을때 하나 이상의 페이지를</p>
<p>동시 또는 조건부로 렌더링 할 수 있게 해준다. 
<img src="https://velog.velcdn.com/images/jae_han_e/post/89044ba6-c800-43a8-a79b-0b0ae8468134/image.png" alt="">
이와 같이 컴포넌트 안에 자식 컴포넌트 두개를 두는 방식이 아니라, 병렬 라우팅을 통해 등록한 페이지들이 자동으로 렌더링 되는 방식이다.</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/c5f632ae-a837-4ebb-9754-c74a81b143c3/image.png" alt="">
각각의 컴포넌트이기 때문에 각자 설정한 error와 loading을 설정할 수 있고 독립적으로 Stream 되어진다.</p>
<h3 id="convention-🧑🏻💻">Convention 🧑🏻‍💻</h3>
<p>병렬 라우트는 폴더명 앞에 <code>@</code>를 붙여 만든다. 그리고 같은 레벨의 layout에 props로 전달된다.</p>
<p>디렉토리를 만들었다고 해서 URL 에 포함되는 것은 아니다.</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/2b95f877-fa40-4cac-814f-6ea7961f10c0/image.png" alt=""></p>
<p>위 폴더구조에서 <code>layout.js</code> 는 <code>@analytics</code> 와 <code>@team</code> 슬롯을 props로 전달받아 children과 함께 렌더링 할 수 있게 된다.</p>
<pre><code class="language-javascript">export default function Layout(props: {
  children: React.ReactNode;
    // 이런식으로 slot들을 전달받는다.
  analytics: React.ReactNode;
  team: React.ReactNode;
}) {
  return (
    &lt;&gt;
      {props.children}
      {props.team}
      {props.analytics}
    &lt;/&gt;
  );
}</code></pre>
<p>더 자세한 내용은 [Next.js App Router]
(<a href="https://velog.io/@jae_han_e/Next.js-App-Router2">https://velog.io/@jae_han_e/Next.js-App-Router2</a>) 에서 확인할 수 있다.</p>
<h1 id="구현-🧑🏻💻">구현 🧑🏻‍💻</h1>
<h2 id="디렉토리-구조-및-레이아웃-설정">디렉토리 구조 및 레이아웃 설정</h2>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/c01f6b3e-8053-4c34-b867-e2afd4aa7960/image.png" alt=""></p>
<p><code>@modal</code> 디렉토리 안에 <strong>bottomDialog</strong>와 <strong>buttonDialog</strong> 페이지를 생성한다.</p>
<p>App 라우팅으로 <code>/bottomDialog</code> 와 <code>/buttonDialog</code> 경로로 접근이 가능하다.</p>
<pre><code class="language-javascript">// app/layout.tsx
export default function RootLayout({
  children,
  modal,
}: Readonly&lt;{
  children: React.ReactNode;
  modal: React.ReactNode;
}&gt;) {
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;body id=&quot;container&quot; className=&quot;antialiased max-w-tablet mx-auto&quot;&gt;
        &lt;Suspense fallback={&lt;LoadingScreen /&gt;}&gt;
            &lt;ToastProvider&gt;
              {children}
              {modal}
            &lt;/ToastProvider&gt;
        &lt;/Suspense&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<p>루트 레이아웃에 생성한 <code>modal</code> 을 연결해 준다.</p>
<p>이제❗️ 기존 페이지들과 <strong>modal</strong> 내부 페이지들이 병렬로 라우팅 되어진다. 🙌🏻</p>
<h2 id="☝🏻-버튼팝업">☝🏻 버튼팝업</h2>
<p>전역 상태관리 라이브러리인 <code>zustand</code> 로 버튼 팝업에 대한 정보를 관리한다.</p>
<p>☝🏻 팝업 정보를 <strong>store에 업데이트</strong> 시킨다.
✌🏻 <strong>router.push()</strong> 로 팝업 페이지를 띄운다.
🤟🏻 <strong>store 값을 참조</strong>해 팝업을 렌더링 한다.</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/8a7dd2d0-bc8f-4758-99f6-dff4f807835e/image.png" alt=""></p>
<p>결과를 먼저 확인해 보면, 기존 <code>localhost:3000</code> 페이지와
<code>localhost:3000/buttonDialog</code> 페이지가 병렬로 라우팅 되어 있다.</p>
<ul>
<li>store 생성 하기<pre><code class="language-javascript">// buttonInfoDto.ts
</code></pre>
</li>
</ul>
<p>import { z } from &quot;zod&quot;;</p>
<p>export const buttonInfoDto = z
  .object({
    // 팝업 내용
    description: z.string().default(&quot;&quot;),</p>
<pre><code>// 팝업 버튼 정보
buttonList: z
  .array(
    z.object({
      buttonName: z.string().default(&quot;&quot;),
      onClick: z.function().default(() =&gt; {}),
    })
  )
  .default([]),</code></pre><p>  })
  .default({});</p>
<p>export type buttonInfoType = z.infer<typeof buttonInfoDto>;</p>
<p>export const initialButtonInfo: buttonInfoType = buttonInfoDto.parse({});</p>
<pre><code>```javascript
// useBottonPopupStore.ts

import { create } from &quot;zustand&quot;;
import {buttonInfoType, initialButtonInfo} from &#39;@/shared/model/buttonInfoDto&#39;;

interface ButtonPopupState {
  buttonInfo: buttonInfoType;
  setButtonInfo: (buttonInfo: buttonInfoType) =&gt; void;
  isOpen: boolean;
  setIsOpen: (isOpen: boolean) =&gt; void;
}

export const useButtonPopupStore = create&lt;ButtonPopupState&gt;((set) =&gt; ({
  buttonInfo: initialButtonInfo,
  setButtonInfo: (info) =&gt; set({buttonInfo:info}),
  isOpen: false,
  setIsOpen: (isOpen: boolean) =&gt; set({isOpen: isOpen}),
}));
</code></pre><p>store에는 버튼 정보가 들어있는 <code>buttonInfo</code> 와 <code>isOpen</code> 값이 들어 있다. </p>
<ul>
<li>팝업 정보 업데이트 하기<pre><code class="language-javascript">// /app/page.tsx
</code></pre>
</li>
</ul>
<p>export default function MainPage() {
  const {setButtonInfo} = useButtonPopupStore()
  const { push, back } = useRouter();</p>
<p>  const handleOnButtonClick = (num: number) =&gt; {
    // 팝업 정보 설정
    if(num === 1){
      setButtonInfo({
        description: &quot;버튼 한개 팝업입니다.&quot;,
        buttonList: [
          {
            buttonName: &quot;확인&quot;,
            onClick:()=&gt; back()
          }
        ]
      })
    }else{
      setButtonInfo({
        description: &quot;버튼 두개 팝업입니다.&quot;,
        buttonList: [
          {
            buttonName: &quot;취소&quot;,
            onClick:()=&gt; back()
          },
          {
            buttonName: &quot;확인&quot;,
            onClick:()=&gt; back()
          },</p>
<pre><code>    ]
  })
}

// 팝업 띄우기
push(&quot;/buttonDialog&quot;, { scroll: false });</code></pre><p>  }
...</p>
<p>  return (
    <div className="mt-10 mx-auto flex gap-3">
      &lt;BottomButton title=&quot;One Button Popup&quot; onClick={()=&gt;handleOnButtonClick(1)}  /&gt;
      &lt;BottomButton title=&quot;Two Button Popup&quot; onClick={()=&gt;handleOnButtonClick(2)}  /&gt;
      <BottomButton title="Bottom Dialog" onClick={handleOnBottomDialogClick}  />
    </div>
  );
}</p>
<pre><code>
* 팝업 화면
```javascript
// /app/@modal/buttonDialog/page.tsx

export default function ButtonModal() {
  const router = useRouter();
  const { buttonInfo, isOpen } = useButtonPopupStore();
  const [isOneButton, setIsOneButton] = useState(false);
  const {setIsOpen} = useButtonPopupStore()
  const pathname = usePathname();

  const closeModal = () =&gt; {
    router.back();
  };

  useEffect(() =&gt; {
    setIsOneButton(buttonInfo?.buttonList?.length === 1);
  }, [buttonInfo?.buttonList?.length]); // 의존성 추가

  useEffect(() =&gt; {
    if (pathname.includes(&quot;/buttonDialog&quot;)) {
      setIsOpen(true);
    } else {
      setIsOpen(false);
    }
  }, [pathname]);

  return (
    &lt;Transition appear show={isOpen} as={Fragment}&gt;
      &lt;Dialog as=&quot;div&quot; className=&quot;relative z-50&quot; onClose={closeModal}&gt;
        &lt;TransitionChild
          as={Fragment}
          enter=&quot;ease-out duration-300&quot;
          enterFrom=&quot;opacity-0&quot;
          enterTo=&quot;opacity-100&quot;
          leave=&quot;ease-in duration-200&quot;
          leaveFrom=&quot;opacity-100&quot;
          leaveTo=&quot;opacity-0&quot;
        &gt;
          &lt;div className=&quot;fixed left-0 top-0 right-0 bottom-0 bg-black/25&quot; /&gt;
        &lt;/TransitionChild&gt;

        &lt;div className=&quot;fixed left-0 top-0 right-0 bottom-0 overflow-y-auto&quot;&gt;
          &lt;div className=&quot;flex min-h-full items-center justify-center p-12 text-center&quot;&gt;
            &lt;TransitionChild
              as={Fragment}
              enter=&quot;ease-out duration-300&quot;
              enterFrom=&quot;opacity-0 scale-95&quot;
              enterTo=&quot;opacity-100 scale-100&quot;
              leave=&quot;ease-in duration-200&quot;
              leaveFrom=&quot;opacity-100 scale-100&quot;
              leaveTo=&quot;opacity-0 scale-95&quot;
            &gt;
              &lt;DialogPanel
                className=&quot;w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-4 text-left align-middle shadow-xl transition-all&quot;
                aria-labelledby=&quot;dialog-title&quot;
                aria-describedby=&quot;dialog-description&quot;
              &gt;
                &lt;div id=&quot;dialog-description&quot; className=&quot;mt-2 whitespace-pre-line text-center&quot;&gt;
                  &lt;p className=&quot;text-by01-15-21-400 text-kn-900&quot;&gt;{buttonInfo?.description}&lt;/p&gt;
                &lt;/div&gt;

                &lt;div className={`flex mt-4 h-[3rem] ${isOneButton ? &quot;justify-center&quot; : &quot;&quot;}`}&gt;
                  {buttonInfo?.buttonList?.map((button, index) =&gt; {
                    return (
                      &lt;button
                        key={button.buttonName}
                        type=&quot;button&quot;
                        id={button.taggingId}
                        className={`inline-flex flex-1 justify-center items-center border border-transparent py-2 px-1 text-base font-medium ${
                          isOneButton
                            ? &quot;bg-ss-800 rounded-md text-white&quot;
                            : index === 0
                              ? &quot;bg-kg-400 text-kn-1000 w-full rounded-tl-md rounded-bl-md&quot;
                              : &quot;bg-ss-800 text-white w-full -ml-[2px] rounded-tr-md rounded-br-md&quot;
                        }
                        `}
                        onClick={button.onClick}
                      &gt;
                        {button.buttonName}
                      &lt;/button&gt;
                    );
                  })}
                &lt;/div&gt;
              &lt;/DialogPanel&gt;
            &lt;/TransitionChild&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/Dialog&gt;
    &lt;/Transition&gt;
  );
}</code></pre><p>전역 데이터로 팝업 데이터를 관리하기 때문에
팝업을 띄우고자 하는 화면에서 <strong>store 값을 업데이트</strong> 시키고
<code>router.push(&#39;/buttonDialog&#39;)</code> 해주면 된다. 👍🏻</p>
<h2 id="✌🏻바텀-다이얼로그">✌🏻바텀 다이얼로그</h2>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/59a874de-44d7-4781-b6f8-d21f12598949/image.png" alt=""></p>
<pre><code class="language-javascript">// /app/@modal/bommomDialog/categoryDetail

export default function ButtonModal() {
  const router = useRouter();
  const [isOpen, setIsOpen] = useState&lt;boolean&gt;(false);
  const [selectedCategoryId, setSelectedCategoryId] = useState(undefined);

  const [detailCategoryList, setDetailCategoryList] = useState([{categoryDetailId:1, categoryDetailName:&quot;카테고리1&quot;},{categoryDetailId:2, categoryDetailName:&quot;카테고리2&quot;}, {categoryDetailId:3, categoryDetailName:&quot;카테고리3&quot;}, {categoryDetailId:4, categoryDetailName:&quot;카테고리4&quot;}, {categoryDetailId:5, categoryDetailName:&quot;카테고리5&quot;}]);

  useEffect(() =&gt; {
    setIsOpen(true);
  }, []);

  const closeModal = () =&gt; {
    setIsOpen(false);
    setTimeout(() =&gt; {
      router.back();
    }, 200);
  };

  const handleOnSelect = () =&gt; {

    setIsOpen(false);

    setTimeout(() =&gt; {
      router.back();
    }, 200);
  };

  return (
    &lt;BottomDialog isOpen={isOpen} onClose={closeModal} title=&quot;바텀업 다이얼로그&quot;&gt;
      &lt;&gt;
        &lt;div className=&quot;flex flex-col gap-5 mt-4 max-h-[200px] pb-3 overflow-y-auto noneScrollBar dialog-body&quot;&gt;
          {/* 카테고리 항목 렌더링 */}
          {detailCategoryList?.map((item) =&gt; {
            return (
              &lt;div
                key={item.categoryDetailId}
                className=&quot;flex justify-between px-1&quot;
                onClick={() =&gt; setSelectedCategoryId(item.categoryDetailId)}
              &gt;
                &lt;span className=&quot;text-sb01-16-21-400 text-kn-1000 font-normal&quot;&gt;{item.categoryDetailName}&lt;/span&gt;
                {selectedCategoryId === item.categoryDetailId &amp;&amp; &lt;NextImage src={CHECKED} alt=&quot;Checked&quot; width={18} height={18} /&gt;}
              &lt;/div&gt;
            );
          })}
        &lt;/div&gt;
        &lt;BottomButton onClick={() =&gt; handleOnSelect()} title={CONFIRM} className=&quot;mb-[10px] mt-4&quot; /&gt;
      &lt;/&gt;
    &lt;/BottomDialog&gt;
  );
}</code></pre>
<p>버튼 팝업과 마찬가지로, <code>router.push(&#39;/bottomDialog/categoryDetail&#39;);</code>
로 이동하면 병렬라우팅 되어진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js 환경변수]]></title>
            <link>https://velog.io/@jae_han_e/Next.js-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@jae_han_e/Next.js-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98</guid>
            <pubDate>Mon, 05 Jan 2026 06:59:18 GMT</pubDate>
            <description><![CDATA[<h1 id="환경변수의-필요성">환경변수의 필요성</h1>
<p>환경변수(Environment Variaable)란 애플리케이션 <strong><code>실행 환경에 따라 달라질 수 있는 설정 값</code></strong> 을 의미한다.😊</p>
<h2 id="☝️-보안성-강화">☝️ 보안성 강화</h2>
<p>소스코드에 민감한 정보를 하드코딩해 사용하게 되면
무단 액세스 또는 데이터 유출과 같은 보안 위험이 발생할 수 있다.👿</p>
<p>Ex ) <strong>외부 서비스 API Key</strong>, <strong>Secret Key</strong>, <strong>Token</strong> 등 ...</p>
<h2 id="✌️-환경-분리">✌️ 환경 분리</h2>
<p>개발(Development), 스테이징(Staging), 운영(Production) 등
각 환경에 맞는 설정을 하는데 사용한다.</p>
<p>Ex) <strong>API 엔드포인트</strong>, <strong>데이터베이스 URL</strong>, <strong>스토리지 URL</strong> 등 ...</p>
<h2 id="👌-유지보수성-향상">👌 유지보수성 향상</h2>
<p>특정 값을 코드에 하드코딩하지 않고 변수로 관리하여
애플리케이션 자체를 수정하지 않고도 동작을 변경할 수 있게 한다.</p>
<p>Ex) <strong>외부 서비스 API 주소</strong>, <strong>Base URL</strong>, <strong>로그 레벨</strong> 등 ...</p>
<blockquote>
<p>🚨 <strong>주의사항</strong>
클라이언트로 전달되는 환경변수는 반드시 공개되어도
괜찮은 값만 사용해야 한다.</p>
</blockquote>
<h1 id="환경변수-설정-방법-📝">환경변수 설정 방법 📝</h1>
<h2 id="1-processenvnode_env-변수">1. process.env.NODE_ENV 변수</h2>
<p>Next.js가 구동 환경에 따라 자동으로 생성해주는 환경변수이다.
서버와 클라이언트에서 모두 참조할 수 있다.😁</p>
<ul>
<li>development: 개발환경 ( npm run dev로 실행한 경우 )</li>
<li>production: 배포환경 ( npm run build, next start 로 실행한 경우)</li>
<li>test: 테스트 환경 (Jest or Vitest 를 실행 한 경우)</li>
</ul>
<pre><code class="language-javascript">// next.config.ts
console.log(&quot;$$$$ NODE_ENV: &quot;, process.env.NODE_ENV);

// npm run dev
$$$$ NODE_ENV:  development

// npm run build
$$$$ NODE_ENV:  production
</code></pre>
<h2 id="2-env-파일의-종류와-우선순위🏆">2. env 파일의 종류와 우선순위🏆</h2>
<p>구동 환경별 환경 변수를 정의하는 파일이다.
해당 파일에 정의한 환경변수는 <code>process.env.변수명</code> 으로 로드할 수 있다.</p>
<ul>
<li><strong>.env</strong>: 모든 환경에서 공통으로 적용할 default 환경 변수를 정의한다.</li>
<li><strong>.env.development</strong>: 개발 환경에서 적용</li>
<li><strong>.env.production</strong>: 배포 / 빌드 환경에서 적용</li>
<li><strong>.env.local</strong> 가장 우선순위가 높아 다른 파일에 영향을 받지 않음</li>
</ul>
<p>우선순위는
<code>.env.local</code> &gt; <code>.env.${mode}</code> &gt; <code>.env</code> 순으로 높다 </p>
<blockquote>
<p><strong>.env.local 파일의 우선순위가 가장 높은 이유 💡</strong>
개발에 필요한 개인의 로컬 환경변수로 사용되며
팀 공통 설정을 로컬에서 안전하게 덮어쓸 때 사용한다.
그래서<code>.env.local은 기본적으로 .gitignore에 포함</code>된다.</p>
</blockquote>
<pre><code class="language-javascript">//.env
# 모든 환경에서 기본으로 사용하는 값
NEXT_PUBLIC_API_URL=https://api.env.com
NEXT_PUBLIC_APP_NAME=next-env-example

//.env.development
# 개발 환경에서만 덮어쓰는 값
NEXT_PUBLIC_API_URL=https://api.env.development.com

//.env.local
NEXT_PUBLIC_API_URL=https://api.env.local.com

// src/app/page.tsx
export default function Page() {
  console.log(&#39;$$$$ API URL:&#39;, process.env.NEXT_PUBLIC_API_URL);
  console.log(&#39;$$$$ APP NAME:&#39;, process.env.NEXT_PUBLIC_APP_NAME);

  return &lt;div&gt;Check console&lt;/div&gt;;
}

// npm run dev
$$$$ API URL: https://api.env.local.com
$$$$ APP NAME: next-env-example
</code></pre>
<h2 id="3-next_public_-접두사의-의미">3. <code>NEXT_PUBLIC_</code> 접두사의 의미</h2>
<p>Next.js에서는 기본적으로 서버에서만 환경변수를 참조할 수 있다.
서버와 클라이언트 모두에서 환경변수를 참조하려면 변수명 앞에 <code>NEXT_PUBLIC</code> 를 붙여주어야 한다.😯</p>
<h2 id="4-nextconfig-의-env-옵션">4. next.config 의 env 옵션</h2>
<p>next.config.js의 env 옵션에 선언한 값도 process.env로 접근할 수 있다.</p>
<pre><code class="language-javascript">...
const nextConfig = {
  env: {
    VERSION: &quot;1.2.1.5&quot;,
    BUILD_TIME: new Date().toISOString(),
  },
};

export default nextConfig;</code></pre>
<p>다만, 이 방식은 모든 값이 클라이언트 번들에 포함되며,
<strong><code>NEXT_PUBLIC_</code> 접두사와 상관없이 노출된다.</strong> 🚨</p>
<p>따라서 보안이 필요한 환경변수 관리 용도로는 적합하지 않으며,</p>
<p>빌드 설정과 관련된 상수 값을 전달할 때만 제한적으로 사용하는 것이 좋다.👍</p>
<h1 id="마무리">마무리</h1>
<p>환경변수는 단순한 설정 값처럼 보이지만,
보안, 배포, 유지보수와 깊게 연결된 중요한 요소다.</p>
<p>Next.js의 환경변수 규칙을 정확히 이해하고 사용한다면
보다 안전하고 확장성 있는 프로젝트를 만들 수 있다.</p>
<blockquote>
<p>참조
<a href="https://m.blog.naver.com/twilight_teatime/223158347614">https://m.blog.naver.com/twilight_teatime/223158347614</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue.js 기본 개념]]></title>
            <link>https://velog.io/@jae_han_e/Vue.js-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@jae_han_e/Vue.js-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Fri, 11 Jul 2025 08:10:13 GMT</pubDate>
            <description><![CDATA[<h1 id="vuejs-란--🧐">Vue.js 란 ? 🧐</h1>
<p>사용자 인터페이스(UI)를 개발하기 위한 자바스크립트 프레임워크로 Evan You 에 의해 개발 되었다.
React에 이어 2번째로 대중적인 프론트엔드 자바스크립트 프레임워크이며 React에 비해 러닝커브가 낮고 직관적인 문법, 뛰어난 성능, 높은 사용자 경험 등으로 개발자들 사이에서 인기 있는 프레임워크이다.</p>
<h1 id="주요-특징-💡">주요 특징 💡</h1>
<h2 id="1-통합-프레임워크">1. 통합 프레임워크</h2>
<p>앞서 설명한 <a href="https://velog.io/@jae_han_e/React-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90">React</a> 와 다르게 프레임워크이므로 <strong>어플리케이션 구성에 필요한 요소들을 자체적으로 제공</strong>한다.</p>
<p>예를 들어 React는 전역 상태 관리를 위해 <code>Redux</code>, <code>Recoil</code>, <code>Zustand</code> 등 추가 라이브러리를 설치해야 하지만 Vue는 <strong>자체적으로 상태관리 도구인 Vue jsx를 제공</strong>한다.</p>
<h2 id="2-가상virtual-dom">2. 가상(Virtual) DOM</h2>
<p>기존 렌더링의 경우, 화면에 변화가 생기면 모든 요소의 스타일을 다시 계산해 새로 그려주었는데
Vue는 가상DOM을 사용해 <strong>변경된 부분만 실제DOM에 그려주기 때문에 효율적</strong>이다. ✨</p>
<h3 id="2-1-최초-렌더링-initial-rendering">2-1. 최초 렌더링 (Initial Rendering)</h3>
<p>최초 렌더링에서는 <code>Diffing</code> 과정을 거치진 않지만 Virtual DOM을 먼저 만들고 실제 DOM으로 변환한다.</p>
<p>** 2-1-1. Vue 인스턴스 생성 및 앱 마운트**</p>
<pre><code class="language-javascript">import { createApp } from &#39;vue&#39;
import App from &#39;./App.vue&#39;

createApp(App).mount(&#39;#app&#39;)</code></pre>
<p><code>createApp(App)</code>으로 Vue <strong>앱을 생성하고</strong>, <code>.mount(&#39;#app&#39;)</code> 을 통해 <strong>HTML의 특정 요소(div#app)에 앱을 연결</strong>한다.</p>
<p>** 2-1-2. 컴포넌트 초기화**
루트 컴포넌트인 <code>&lt;App /&gt;</code> 부터 시작해서 하위 컴포넌트들을 트리 구조로 <strong>재귀적으로 초기화</strong> 한다.</p>
<p>** 2-1-3. 템플릿을 렌더 함수로 컴파일**
<code>.vue</code> 파일의 <code>&lt;template&gt;</code> 내용을 내부적으로 <strong>JavaScript의 redner() 함수로 컴파일</strong> 한다.</p>
<pre><code class="language-javascript">// Before
&lt;template&gt;
  &lt;p&gt;{{ message }}&lt;/p&gt;
&lt;/template&gt;

// After
render() {
  return h(&#39;p&#39;, null, this.message)
}
</code></pre>
<p>** 2-1-4. VNode(Virtual DOM Node) 트리 생성**
<code>render()</code> 함수는 가상의 DOM 구조인 VNode 트리를 생성한다.
이 트리는 실제 DOM을 직접 조작하지 않고 <strong>메모리 상에 있는 가벼운 객체 트리</strong>이다.</p>
<p>** 2-1-5. VNode → 실제 DOM 변환 (Mount)**
생성된 VNode 를 <strong>실제 브라우저 DOM으로 변환</strong>한다.
이 과정을 <code>마운트</code> 라고 부르며, 실제 html 요소가 만들어져 <code>#app</code> 에 삽입된다.</p>
<pre><code class="language-javascript">// html element

&lt;div id=&quot;app&quot;&gt;
  &lt;p&gt;Hello Vue!&lt;/p&gt;
&lt;/div&gt;
</code></pre>
<p>** 2-1-6. DOM 삽입 후 mounted 훅 실행**
렌더링이 완료되면, 컴포넌트의 <code>mounted()</code> 훅이 호출된다.
이 때부터 DOM 접근이 가능해진다.</p>
<pre><code class="language-javascript">mounted() {
  console.log(&#39;컴포넌트가 마운트되었습니다!&#39;);
}
</code></pre>
<h3 id="2-2-반응형-데이터-변경-reactivity-trigger">2-2. 반응형 데이터 변경 (Reactivity Trigger)</h3>
<p>상태가 변경되면 Vue의 <strong><code>반응형 시스템</code>이 이를 감지하고 리렌더링 시킴</strong></p>
<p><strong>2-2-1. 변경된 데이터와 연결된 컴포넌트 찾기</strong>
Vue는 어떤 데이터가 어떤 컴포넌트에서 아용되는지 추적하고 있기 때문에
데이터가 사용된 템플릿/컴포넌트만 <strong>리렌더링 대상</strong>으로 지정한다.</p>
<p><strong>2-2-2. 새로운 Virtual DOM(VNode) 트리 생성</strong>
변경된 데이터를 기준으로 새 <code>render()</code> 함수가 실행되며, 새로운 <code>VNode</code>가 생성된다.</p>
<p><strong>2-2-3. 이전 VNode와 비교 (Diffing)</strong>
이전에 만들어 놓은 VNode와 새로운 VNode를 비교해서 변경된 부분을 찾아낸다.</p>
<pre><code class="language-javascript">example)
- &lt;p&gt;Count: 0&lt;/p&gt;
+ &lt;p&gt;Count: 1&lt;/p&gt;
</code></pre>
<p><strong>2-2-4. 실제 DOM 업데이트 (Patch)</strong>
변경된 부분만 실제 DOM에 적용한다. </p>
<h3 id="2-3-렌더-파이프라인">2-3. 렌더 파이프라인</h3>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/767a4050-9ff1-4b14-a9c6-8ac74f3d789e/image.png" alt=""></p>
<p>☝🏻 <strong>컴파일</strong>
Vue의 템플릿은 <code>렌더 함수</code>로 컴파일 된다.
<strong>렌더함수는 Virtual DOM 트리를 반환하는 함수</strong>이며 빌드 단계에서 수행할 수 있고, 런타임 컴파일러를 사용해 실시간으로 수행할 수 있다.(리렌더링)</p>
<p>✌🏻 <strong>마운트</strong>
런타임 렌더러가 렌더 함수의 호출 결과물인 Virtual DOM 트리를 순회하고, <strong>실제 DOM 노드를 생성</strong>.
반응형 의존성 데이터들을 추적 시작.</p>
<p>🤟🏻 <strong>패치</strong>
마운트 시 사용된 의존성이 변경되면, Virtual DOM을 새로 생성하고 Diffing 과정을 통해 <strong>실제 DOM을 업데이트 시킨다</strong>.</p>
<h2 id="3-컴포넌트-기반-아키텍처">3. 컴포넌트 기반 아키텍처</h2>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/e0df5b73-193c-4a81-93d0-d0c8b97a7cd5/image.png" alt=""></p>
<p>웹 어플리케이션의 <strong>복잡한 UI를 재사용 가능한 단위로 분할</strong>하는 방식을 말한다.
각각의 컴포넌트는 자체적으로 상태와 속성을 가지고 있으며 독립적으로 존재한다.
이는 <strong>UI를 계층적으로 구조화하여 코드의 가독성을 높이고 유지보수를 용이</strong>하게 할 수 있다.</p>
<blockquote>
<p>컴포넌트 개발 시, <strong>구성요소 간의 의존성을 최소화</strong> 시켜야 복잡도가 낮고 재사용성을 높일 수 있는 장점이 있다. 😀</p>
</blockquote>
<h2 id="4-상대적으로-낮은-러닝-커브">4. 상대적으로 낮은 러닝 커브</h2>
<h3 id="4-1-html과-유사한-템플릿-문법">4-1. HTML과 유사한 템플릿 문법</h3>
<p>Vue의 <code>&lt;template&gt;</code> 영역은 기존 HTML과 거의 똑같은 구조로 작성할 수 있어
<strong>입문자들도 어렵지않게 UI를 구성</strong>할 수 있다.</p>
<pre><code class="language-javascript">// example.vue
&lt;template&gt;
  &lt;div&gt;
    &lt;h1&gt;안녕하세요, {{ name }}님!&lt;/h1&gt;
    &lt;input v-model=&quot;name&quot; placeholder=&quot;이름을 입력하세요&quot; /&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>기존 HTML을 다뤄본 개발자라면 Vue 템플릿도 쉽게 이해할 수 있다. 😀</p>
<h3 id="4-2-sfcsingle-file-component-구조">4-2. SFC(Single File Component) 구조</h3>
<p><code>.vue</code> 파일에 <code>&lt;template&gt;</code>, <code>&lt;script&gt;</code>, <code>&lt;style&gt;</code>이 분리된 <strong>직관적인 구조</strong>이다.</p>
<pre><code class="language-javascript">&lt;!-- HelloWorld.vue --&gt;
&lt;template&gt;
  &lt;h2&gt;안녕하세요, {{ user }}&lt;/h2&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
  data() {
    return {
      user: &#39;Vue 초보자&#39;
    };
  }
}
&lt;/script&gt;

&lt;style scoped&gt;
h2 {
  color: teal;
}
&lt;/style&gt;
</code></pre>
<blockquote>
<p>참고
<a href="https://ko.vuejs.org/guide/extras/rendering-mechanism">Vue 공식문서</a>
<a href="https://www.elancer.co.kr/blog/detail/171?seq=171">elancer</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 기본 개념]]></title>
            <link>https://velog.io/@jae_han_e/React-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@jae_han_e/React-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Tue, 08 Jul 2025 06:48:11 GMT</pubDate>
            <description><![CDATA[<h1 id="react-란-">React 란 ?</h1>
<p>페이스북에서 개발한 자바스크립트 라이브러리이며, 프론트엔드 개발에서 가장 많이 채택되고 있다.
프레임워크인 <strong>Vue</strong>, <strong>Angular</strong> 와 달리 라이브러리 이므로 확장성이 좋고
<strong>SPA(Single Page Application)</strong> 방식으로 동작하기 때문에 높은 사용자 경험을 제공한다.</p>
<h3 id="❗️프레임워크--라이브러리">❗️프레임워크 &amp; 라이브러리</h3>
<h4 id="1-프레임워크">1. 프레임워크</h4>
<p>애플리케이션의 흐름과 제어를 관리하며 개발에 필요한 기능을 제공해 준다. 
<strong>개발자는 프레임워크의 규칙과 인터페이스에 따라 코드를 작성</strong>해야 한다.</p>
<h4 id="2-라이브러리">2. 라이브러리</h4>
<p>개발을 위해 재사용 가능한 코드의 집합이며, 특정 기능을 수행하는 함수, 클래스, 모듈 등으로 구성된다. <strong>개발자가 코드의 흐름과 제어를 직접 관리</strong>하며 필요한 기능을 호출하여 사용할 수 있다.</p>
<p>즉, 프레임 워크인 <strong>Vue</strong>, <strong>Angular</strong>는 애플리케이션 개발의 전체적인 구조와 규칙을 제공하기 때문에 개발자는 제한된 환경에서 개발을 해야 한다.</p>
<p>반면, 리액트는 애플리케이션의 전체 구조나 아키텍처를 결정하지 않으며, <strong>사용자 인터페이스(UI) 개발에 초점을 맞춘 라이브러리</strong> 이다.</p>
<blockquote>
<p>‼️ SPA (<strong>S</strong>ingle <strong>P</strong>age ** A**pplication)
어플리케이션을 구성하는 다양한 페이지를 하나의 페이지인 것처럼 처리하는 기술
최초 진입 시, 전체 화면 정보를 로드하고 사용자 인터렉션에 따라 <code>필요한 부분만 비동기로 로딩</code>하는 방식
예를들어, Header 와 Navigator 같이 화면마다 고정되는 영역은 로딩하지 않고 Contents 부분만 로딩한다.
☝🏻 필요한 리소스만 부분 로딩하기 때문에 빠르고 서버 부담이 적다.
✌🏻 화면 전환 시 깜빡임 없이 부드럽게 이동한다.</p>
</blockquote>
<h1 id="주요-특징-💡">주요 특징 💡</h1>
<h2 id="1-컴포넌트-기반-아키텍처">1. 컴포넌트 기반 아키텍처</h2>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/e0df5b73-193c-4a81-93d0-d0c8b97a7cd5/image.png" alt=""></p>
<p>웹 어플리케이션의 <strong>복잡한 UI를 재사용 가능한 단위로 분할</strong>하는 방식을 말한다.
각각의 컴포넌트는 자체적으로 상태와 속성을 가지고 있으며 독립적으로 존재한다.
이는 <strong>UI를 계층적으로 구조화하여 코드의 가독성을 높이고 유지보수를 용이</strong>하게 할 수 있다.</p>
<blockquote>
<p>컴포넌트 개발 시, <strong>구성요소 간의 의존성을 최소화</strong> 시켜야 복잡도가 낮고 재사용성을 높일 수 있는 장점이 있다. 😀</p>
</blockquote>
<h2 id="2-가상virtual-dom">2. 가상(Virtual) DOM</h2>
<p>기존 렌더링의 경우, 화면에 변화가 생기면 모든 요소의 스타일을 다시 계산해 새로 그려주었는데
리액트는 가상DOM을 사용해 <strong>변경된 부분만 실제DOM에 그려주기 때문에 효율적</strong>이다. ✨</p>
<h3 id="react-렌더링-과정">React 렌더링 과정</h3>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/6922cdab-c2a1-44b5-b33c-aaeab8bd1122/image.png" alt=""></p>
<p>☝🏻 업데이트가 발생하면, <strong>변경된 화면 정보가 담긴 가상돔(Next VDOM)을 생성</strong> 한다.</p>
<p>✌🏻 업데이트 <strong>이전의 내용을 담고있는 가상돔(Prev VDOM)과 비교 과정을 통해 업데이트가 필요한 Element를 파악</strong>한다. (<code>Diffing 과정</code>)</p>
<p>🤟🏻 <strong>이 Element 들만 실제 돔(Actual DOM)에 업데이트</strong> 시킨다(<code>Reconcil 과정</code>)</p>
<p>❗️ 리액트 렌더링이 효과적인 이유는, <strong>변경된 Element 들만 집단화 시켜 딱 한번만 실제 돔에 반영</strong>하기 때문이다.</p>
<p>자세한 렌더링 과정은 <a href="https://velog.io/@jae_han_e/%EB%A6%AC%EC%95%A1%ED%8A%B8-React-%EB%A0%8C%EB%8D%94%EB%A7%81-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4">React 렌더링 프로세스</a>를 참고❗️</p>
<h2 id="3-jsx-기반-문법-사용">3. JSX 기반 문법 사용</h2>
<p>React는 UI를 컴포넌트 단위로 구성하게 되는데, <strong>JSX 문법을 사용해 함수형 컴포넌트를 HTML 처럼 표현</strong>할 수 있도록 도와준다.</p>
<blockquote>
<p>❗️ JSX(<strong>J</strong>ava<strong>S</strong>cript <strong>X</strong>ML)
React에서 컴포넌트를 만들 때 사용하는 JavaScript 확장 문법이다.
JavaScript 코드 안에 HTML 태그처럼 보이는 문법을 작성할 수 있으며, 더 직관적이고 선언적으로 표현할 수 있다.</p>
<blockquote>
<p>Example</p>
</blockquote>
</blockquote>
<pre><code class="language-javascript">function welcome(props){
  return &lt;h1&gt;Hello, {props.name}! &lt;/h1&gt;
}</code></pre>
<h2 id="4-react-hook-제공">4. React Hook 제공</h2>
<p>리액트 훅은 <strong>컴포넌트 내에서 다양한 기능을 사용할 수 있게 해주는 일정의 함수 API</strong> 이다.
이를 통해 <strong>로직을 간결하게</strong> 만들 수 있고 <strong>컴포넌트간 상태를 공유</strong> 하거나 <strong>불필요한 리렌더링을 방지</strong>해 최적화 시킬 수 있다.</p>
<h3 id="대표적인-react-hook">대표적인 React Hook</h3>
<ul>
<li><code>useState</code>: 컴포넌트 안에서 상태(State)를 관리해 준다.</li>
<li><code>useEffect</code>: 컴포넌트가 리렌더링 되거나 특정 변수가 업데이트 될 때마다 특정 작업을 수행하도록 설정할 수 있다.</li>
<li><code>useRef</code>: 기존의 getElementById 와 같이 Dom Element에 접근할 수 있다.</li>
<li><code>useMemo</code>: 연산이 오래 걸리는 함수의 호출 결과를 캐싱하고, 동일한 param 이 입력되면 캐시 데이터를 반환한다.</li>
</ul>
<h2 id="5풍부한-생태계">5.풍부한 생태계</h2>
<p>Next.js, React Router, Recoil, zustand, Redux 등 <strong>다양한 오픈소스와의 연계가 가능</strong>하다.</p>
<p>또한, 활발하고 다양한 커뮤니티를 가지고 있어 개발 인사이트를 쉽게 얻을 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 프로젝트 ESLint & Prettier 설정]]></title>
            <link>https://velog.io/@jae_han_e/React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-ESLint-Prettier-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@jae_han_e/React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-ESLint-Prettier-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Thu, 26 Jun 2025 06:49:10 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>프로젝트에서 일관된 코드 스타일과 가독성, 유지보수성을 높이기 위해 팀만의 코드 컨벤션을 따르는 것이 필수적이다.</p>
<p>이를 위해 많은 개발자들은 <strong><code>ESLint</code></strong> 와 <strong><code>Prettier</code></strong> 를 활용해 코드 품질을 관리하고 자동으로 포멧팅 되도록 설정한다.</p>
<p><code>React + TypeScript</code> 프로젝트에서 <strong>ESLint</strong>와 <strong>Prettier</strong> 를 적용하는 방법에 대해 알아보자 ❗️</p>
<h1 id="🔍-eslint-와-prettier-란">🔍 ESLint 와 Prettier 란?</h1>
<h2 id="1-eslint">1. ESLint</h2>
<p>JavaScript &amp; TypeScript 코드에서 <code>문법 오류</code>, <code>잠재적인 버그</code>, <code>코드 스타일 위반</code> 등을 찾아주는 <strong>정적 코드분석 도구</strong>이다.</p>
<p>필요에 따라 커스터마이징이 가능해, 조직 스타일에 따라 룰을 설정할 수 있다.</p>
<h2 id="2-prettier">2. Prettier</h2>
<p><code>들여쓰기</code>, <code>줄 바꿈</code>, <code>세미콜론</code> 등 코드 스타일을 자동으로 정리해주는 <strong>코드 포맷터</strong>이다.</p>
<p>코드의 일관된 포멧을 유지하는데 도움을 주며 정해진 옵션 내에서 설정이 가능하다.</p>
<h1 id="🤔-왜-사용해야-할까">🤔 왜 사용해야 할까?</h1>
<ul>
<li><strong>코드 품질 향상</strong>: 잘못된 코드 사전 예방</li>
<li><strong>협업 효율성 증가</strong>: 일관된 코드 포멧팅</li>
<li><strong>손쉬운 사용</strong>: 파일 저장 시 자동 검사 (IDE 설정 필요)</li>
</ul>
<h1 id="⚙️-eslint--prettier-설정-방법">⚙️ ESLint &amp; Prettier 설정 방법</h1>
<h2 id="1-react--typescript-프로젝트-생성--with-cra-">1. React + TypeScript 프로젝트 생성 ( with CRA )</h2>
<pre><code class="language-typescript">npx create-react-app eslint-prettier --template typescript</code></pre>
<h2 id="2-eslint--prettier-설치">2. ESLint &amp; Prettier 설치</h2>
<pre><code class="language-typescript">cd eslint-prettier
npm install -D eslint prettier eslint-config-prettier eslint-plugin-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser</code></pre>
<h3 id="✅-패키지-종류">✅ 패키지 종류</h3>
<p>ESLint 와 Prettier를 함께 사용하고, 여기에 TypeScript 까지 포함되면 설정에 있어 충돌이 발생 할 수 있다. 이를 해결하고 각 도구들이 잘 작동하기 위해 추가로 패키지를 설정해 준다.</p>
<p>1️⃣ eslint-config-prettier
ESLint의 포맷 관련 규칙을 비활성화하여, Prettier와 충돌을 방지한다.</p>
<p>＞ <strong>포맷팅은 Prettier가 알아서 할꺼니까 ESLint는 검사하지마~*</strong></p>
<p>2️⃣ eslint-plugin-prettier
Prettier를 ESLint의 규칙처럼 실행할 수 있게 해준다.
즉, ESLint가 코드 검사를 할 때 Prettier 포멧팅도 함께 검사한다.</p>
<p>＞ <strong>eslint --fix 명령으로 Prettier 포맷까지 자동 수행</strong></p>
<p>3️⃣ @typescript-eslint/parser
ESLint가 TypeScript 문법을 이해할 수 있도록 도와주는 파서 이다.</p>
<p>4️⃣ @typescript-eslint/eslint-plugin
TypeScript에 특화된 ESLint 규칙을 제공한다.</p>
<blockquote>
<p>🚨 Conflict Peer Dependency
<img src="https://velog.velcdn.com/images/jae_han_e/post/17195cfc-b43b-4fc4-9189-95b2a1b2c529/image.png" alt="">
@typescript-eslint/eslint-plugin@5.62.0 버전을 설치함에 있어 @typescript-eslint/parser@8.35.0 버전과 의존성 충돌이 발생했다.</p>
</blockquote>
<pre><code class="language-typescript">npm install @typescript-eslint/eslint-plugin@5.62 @typescript-eslint/parser@^5</code></pre>
<p>동일 버전으로 설치해 충돌 해결!</p>
<h2 id="3-설정파일-생성-및-구성">3. 설정파일 생성 및 구성</h2>
<h3 id="✅-eslintrccjs-파일-생성">✅ eslintrc.cjs 파일 생성</h3>
<p>root 경로에 파일을 생성한다.</p>
<pre><code class="language-typescript">module.exports = {
  // TypeScript 코드를 분석할 수 있도록 파서를 설정
  parser: &#39;@typescript-eslint/parser&#39;,

  parserOptions: {
    ecmaVersion: 2020, // 최신 ECMAScript 문법 사용 허용
    sourceType: &#39;module&#39;, // import/export 문법 사용 허용
    ecmaFeatures: {
      jsx: true, // JSX 문법 사용 허용 (React)
    },
  },

  // React 버전 자동 감지
  settings: {
    react: {
      version: &#39;detect&#39;,
    },
  },

  // 코드가 실행될 환경 설정
  env: {
    browser: true, // 브라우저 환경 (window, document 등 사용 가능)
    es2021: true, // ES2021 문법 사용 가능
  },

  // 사용할 기본 규칙 세트 확장
  extends: [
    &#39;eslint:recommended&#39;, // ESLint 기본 추천 규칙
    &#39;plugin:react/recommended&#39;, // React 관련 규칙
    &#39;plugin:@typescript-eslint/recommended&#39;, // TypeScript 관련 추천 규칙
    &#39;plugin:prettier/recommended&#39;, // Prettier와 충돌되는 ESLint 규칙 제거 + Prettier 플러그인 활성화
  ],

  // 사용할 플러그인 (추가 규칙 제공)
  plugins: [&#39;react&#39;, &#39;@typescript-eslint&#39;],

  // 개별 규칙을 커스터마이징
  rules: {
    // React 17+ 부터는 import React 생략 가능하므로 해당 규칙 끔
    &#39;react/react-in-jsx-scope&#39;: &#39;off&#39;,

    // 필요에 따라 추가 규칙 설정 가능 예:
    // &#39;@typescript-eslint/explicit-function-return-type&#39;: &#39;warn&#39;,
  },
}</code></pre>
<h3 id="✅-prettierrc-파일-생성">✅ .prettierrc 파일 생성</h3>
<p>root 경로에 파일을 생성한다.</p>
<pre><code class="language-typescript">{
  // 작은따옴표(single quote) 사용 여부 (true: &#39; &#39;, false: &quot; &quot;)
  &quot;singleQuote&quot;: true,

  // 문장의 끝에 세미콜론(;)을 붙일지 여부 (true: 붙임, false: 생략)
  &quot;semi&quot;: false,

  // 객체나 배열 마지막 요소 뒤에 쉼표(trailing comma)를 붙일지 여부
  // &quot;es5&quot; = ES5에서 허용되는 곳만 추가, &quot;all&quot; = 가능한 모든 곳에 추가
  &quot;trailingComma&quot;: &quot;all&quot;,

  // 한 줄 최대 글자 수. 초과하면 자동 줄바꿈
  &quot;printWidth&quot;: 80,

  // 들여쓰기 폭 (공백 수)
  &quot;tabWidth&quot;: 2,

  // 탭 대신 공백 사용 여부 (true: 공백, false: 탭)
  &quot;useTabs&quot;: false,

  // JSX 안의 속성에 따옴표 스타일 (true: &#39; &#39;, false: &quot; &quot;)
  &quot;jsxSingleQuote&quot;: true,

  // JSX 태그가 여러 줄일 경우 닫는 태그를 새 줄에 둘지 여부
  &quot;jsxBracketSameLine&quot;: false,

  // 화살표 함수에서 인자가 하나일 때 괄호 사용 여부
  // &quot;avoid&quot;: 하나일 때 괄호 생략, &quot;always&quot;: 항상 괄호 사용
  &quot;arrowParens&quot;: &quot;always&quot;,

  // 객체 속성 정렬 시 key: value 간격 유지 여부
  &quot;bracketSpacing&quot;: true,

  // HTML, JSX, Vue 등에서 self-closing 태그 사용 여부
  &quot;htmlWhitespaceSensitivity&quot;: &quot;css&quot;,

  // 코드 맨 끝에 개행 문자 추가 여부
  &quot;endOfLine&quot;: &quot;lf&quot;
}
</code></pre>
<h3 id="✅-eslintignore-파일-생성">✅ .eslintignore 파일 생성</h3>
<p>검사 예외처리 파일로 root에 생성한다.</p>
<pre><code class="language-typescript">node_modules
build</code></pre>
<h3 id="✅-prettierignore-파일-생성">✅ .prettierignore 파일 생성</h3>
<p>검사 예외처리 파일로 root에 생성한다.</p>
<pre><code class="language-typescript">node_modules
build</code></pre>
<h2 id="4-packagejson-스크립트-추가">4. package.json 스크립트 추가</h2>
<pre><code class="language-typescript">{
  ...
  &quot;scripts&quot;:{
    ...
    &quot;lint&quot;: &quot;eslint \&quot;src/**/*.{js,jsx,ts,tsx}\&quot;&quot;,
    &quot;lint:fix&quot;: &quot;eslint --fix \&quot;src/**/*.{js,jsx,ts,tsx}\&quot;&quot;
    ...

  }
  ...
}</code></pre>
<h2 id="5-ide-와-연동하기-webstorm">5. IDE 와 연동하기 (WebStorm)</h2>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/6ca4379f-848e-4a2f-b374-94b626fb8dc7/image.png" alt=""></p>
<p>1️⃣ cmd + , 를 눌러 Setting 화면 열기
2️⃣ prettier 검색 후 메뉴 이동
( Language &amp; Frameworks &gt; JavaScript &gt; Prettier )
3️⃣ 위와 같이 설정 후 저장</p>
<h2 id="6-확인하기">6. 확인하기</h2>
<p>[ Before ] _ app.tsx</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/218d24e4-4579-4f35-bc2b-96d8bf495b48/image.png" alt=""></p>
<p>[ After ] _ app.tsx
<img src="https://velog.velcdn.com/images/jae_han_e/post/1a897be5-8721-4554-80a6-304f2cddadc4/image.png" alt=""></p>
<p><code>저장</code> or <code>npm run lint:fix</code> 을 하게 되면 설정한 Prettier 규칙에 맞게 포멧팅이 자동으로 이루어진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 (React) 렌더링 프로세스]]></title>
            <link>https://velog.io/@jae_han_e/%EB%A6%AC%EC%95%A1%ED%8A%B8-React-%EB%A0%8C%EB%8D%94%EB%A7%81-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4</link>
            <guid>https://velog.io/@jae_han_e/%EB%A6%AC%EC%95%A1%ED%8A%B8-React-%EB%A0%8C%EB%8D%94%EB%A7%81-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4</guid>
            <pubDate>Fri, 30 Aug 2024 06:17:35 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>리액트를 사용하면서 무심코 넘어갔던 렌더링 방법에 대해 정리해보고자 한다.</p>
<p>일상적으로 <code>&#39;렌더링 한다&#39;</code> 라고 하면 HTML, CSS, JavaScript 를 가지고 화면을 그리는 과정을 의미한다. </p>
<p>하지만, 리액트에서의 렌더링은 <strong>화면을 그리는데 필요한 <code>DOM Tree</code> 를 구성하는 과정</strong>을 뜻한다.</p>
<p>리액트는 자체 렌더링 프로세스를 가지고 있어 성능적으로 좋은 퍼포먼스를 내게 해준다. 😃</p>
<p>렌더링 프로세스는 크게 3개의 단계로 나눌 수 있다.</p>
<p> <strong><code>1. 트리거 단계 (Trigger Phase)</code></strong>
 <strong><code>2. 렌더 단계 (Render Phase)</code></strong>
 <strong><code>3. 커밋 단계 (Commit Phase)</code></strong> </p>
<p> <strong>각 단계에서 하는 작업</strong>에 대해 알아보자❗️</p>
<h1 id="☝🏻-트리거-단계-trigger-phase">☝🏻 트리거 단계 (Trigger Phase)</h1>
<p>트리거 단계는 렌더링이 발생하는 시점의 단계이다.</p>
<p>리액트에서 렌더링이 발생하는 조건은 크게 두가지로 나눌 수 있다. 🤓</p>
<h4 id="1-초기-렌더링">1. 초기 렌더링</h4>
<p>사용자가 사이트에 처음 진입하면서 발생하며 서버에서 리소스를 받아온다.</p>
<h4 id="2-리렌더링">2. 리렌더링</h4>
<p>초기 렌더링 이후 발생하는 모든 렌더링을 <code>리렌더링</code> 이라 한다.</p>
<h3 id="-리렌더링-발생-조건-📝-">[ 리렌더링 발생 조건 📝 ]</h3>
<ul>
<li><p>useState() 의 setter의 실행으로 <code>state 값이 변경 되는 경우</code></p>
</li>
<li><p>부모 컴포넌트에서 전달받은  <code>props 값이 변경되는 경우</code></p>
</li>
<li><p>부모 컴포넌트가 리렌더링 되면 자식 컴포넌트들은 전부 리렌더링 된다.</p>
</li>
</ul>
<p>✤ Class 컴포넌트는 제외</p>
<h1 id="✌🏻렌더-단계-render-phase">✌🏻렌더 단계 (Render Phase)</h1>
<p>트리거가 발생하면 <strong>DOM에 그려질 컴포넌트들을 호출 하는 과정</strong>이다.</p>
<p>이 결과로 컴포넌트 정보가 담긴 <code>React Element</code> 의 집합체인 <code>Virtual DOM</code> 을 만든다. 📑</p>
<h3 id="2-1-컴포넌트-호출">2-1. 컴포넌트 호출</h3>
<p>컴포넌트들은 주로 JSX 구문으로 작성되며, JavaScript 가 컴파일되고 배포 준비가 되는 시점에 React.createElement() 호출로 변하게 된다. 😎</p>
<p><code>createElement</code> 는 일반적인 JS 객체 형식의 <code>React Element</code> 를 반환하는데, 이 엘리먼트는 생성하고자하는 UI 정보에 대한 객체 값이다.</p>
<pre><code class="language-javascript">// 개발된 컴포넌트는
return &lt;SomeComponent a={42} b=&quot;testing&quot;&gt;Text here&lt;/SomeComponent&gt;

// 컴파일 이후 아래와 같은 호출식으로 변환된다.
return React.createElement(SomeComponent, {a: 42, b: &quot;testing&quot;}, &quot;Text Here&quot;)

// 호출된 이후에는 React Element로 나타난다.
{type: SomeComponent, props: {a: 42, b: &quot;testing&quot;}, children: [&quot;Text Here&quot;]}</code></pre>
<h4 id="-초기-렌더링-">[ 초기 렌더링 ]</h4>
<p>서버에서 리소스를 받아와 ReactDOM의 <strong>render() 함수를 실행시시켜 root 컴포넌트를 호출</strong>한다.</p>
<pre><code class="language-javascript">ReactDOM.createRoot(document.getElementById(&#39;root&#39;) as HTMLElement).render(
  &lt;App /&gt;
);</code></pre>
<p><code>render()</code> 함수가 호출되면 리액트는 <code>createElement()</code>로 <code>&lt;APP/&gt;</code> 컴포넌트를 React Element로 만든다.</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/55fe65ed-b82e-4ed0-be86-f77f2e50ce9f/image.png" alt=""></p>
<blockquote>
<p>💡초기 렌더의 경우 만들어지는 <code>Virtual DOM</code> 이 <code>Actual DOM</code> 과 동기화 된다.</p>
</blockquote>
<h4 id="-리렌더링-">[ 리렌더링 ]</h4>
<p>상태 <strong>업데이트가 발생한 컴포넌트만 호출 하고, 변경된 부분만 계산</strong>하게 된다.</p>
<p>만약, 자식 컴포넌트가 존재하면 재귀적으로 호출한다.</p>
<p>컴포넌트 호출이 완료되면, 생성된 React Element 들을 모아 <code>Next Virtual DOM</code> 을 생성한다.</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/f8fd9da4-52ff-4eb7-b02b-d908024fcd1c/image.png" alt=""></p>
<p>만들어진 Next Virtual DOM은 <code>Diffing(재조정)</code> 과정을 통해  <code>Prev Virtual DOM</code> 과 어떤 요소와 속성들이 변했는지를 파악하고</p>
<p>커밋 단계에서 처리한다.</p>
<blockquote>
<p>Diffing 에관한 자세한 설명은 <a href="https://www.moonkorea.dev/React-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%9E%AC%EC%A1%B0%EC%A0%95">여기</a> 를 참고하자 ❗️</p>
</blockquote>
<h1 id="🤟🏻-커밋-단계-commit-phase">🤟🏻 커밋 단계 (Commit Phase)</h1>
<p>렌더 단계에서 발견한 변경 사항들을 <code>Actual DOM</code> 에 적용하는 단계이다.</p>
<p>이때, <strong>변경사항이 있는 노드만 반영</strong>한다. 🫢</p>
<p>Actual DOM 이 변경되면 브라우저는 이를 감지하고 <a href="https://velog.io/@jae_han_e/%EC%9B%B9-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%A1%9C%EB%94%A9-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0">이 과정</a>
을 반복한다.</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/c82ee55b-833b-4a66-9aea-1d059ed01bb8/image.png" alt=""></p>
<p>리액트 렌더링 시스템이 이렇게 설계된 이유는 <strong><code>Repainting</code> 작업을 최소화하기 위해서이다.</strong></p>
<p><strong>동시에 발생한 업데이트를 모아(in 가상돔) DOM 을 한번만 업데이트 시키기 때문에 빠른 속도로 화면이 렌더링 될 수 있다.</strong> 👍🏻</p>
<blockquote>
<p>참조
[NE(O)RDINARY CONFERENCE - 이정환]
(<a href="https://www.youtube.com/watch?v=N7qlk_GQRJU">https://www.youtube.com/watch?v=N7qlk_GQRJU</a>)
[moonkorea]
(<a href="https://www.moonkorea.dev/React-%EB%A0%8C%EB%8D%94%EB%8B%A8%EA%B3%84-%EC%BB%A4%EB%B0%8B%EB%8B%A8%EA%B3%84">https://www.moonkorea.dev/React-%EB%A0%8C%EB%8D%94%EB%8B%A8%EA%B3%84-%EC%BB%A4%EB%B0%8B%EB%8B%A8%EA%B3%84</a>)
[@coddingyun]
(<a href="https://velog.io/@coddingyun/React-%EB%A6%AC%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%A1%B0%EA%B1%B4%EA%B3%BC-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95">https://velog.io/@coddingyun/React-%EB%A6%AC%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%A1%B0%EA%B1%B4%EA%B3%BC-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95</a>)
[minsug]
(<a href="https://blog.minsug.dev/2024/04/react-rendering">https://blog.minsug.dev/2024/04/react-rendering</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 사이트 로딩 & 렌더링 과정 이해하기]]></title>
            <link>https://velog.io/@jae_han_e/%EC%9B%B9-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%A1%9C%EB%94%A9-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jae_han_e/%EC%9B%B9-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%A1%9C%EB%94%A9-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 28 Aug 2024 08:13:10 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>우리가 사이트로 이동하기 위해 브라우저에 URL 주소를 입력하게 되면 실제 페이지가 화면에 나타게 되는데</p>
<p>이 과정에 대해 정리하려고 한다. 📝</p>
<p>URL 주소를 입력하게 되면 크게 아래와 같은 과정이 발생한다.</p>
<p> <strong><code>1. 웹 서버와 연결하기</code></strong> &gt; <strong><code>2. 화면 정보 요청하기</code></strong> &gt; <strong><code>3. 응답 파일 해석하기</code></strong> &gt; <strong><code>4. 화면 그리기</code></strong></p>
<p>이제 이 과정들을 상세하게 파헤쳐보자. 😀</p>
<h1 id="☝🏻-웹-서버와-연결하기">☝🏻 웹 서버와 연결하기</h1>
<p>원하는 페이지를 보기 위해서는 가장 먼저, 페이지 정보를 가지고 있는 <code>웹 서버</code>와 연결되어야 한다.</p>
<h3 id="1-1-서버-주소-찾기--dnsdomain-name-system">1-1. 서버 주소 찾기 : DNS(Domain Name System)</h3>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/28bd9029-94ad-4639-b737-d287c3284943/image.png" alt=""></p>
<p>우리가 URL에 <code>https://www.naver.com</code> 과 같이 문자로 된 도메인 주소를 입력하게 되면 DNS 서버를 통해 <code>192.168.x.x</code> 와 같은 IP 로 변경되어 찾아간다🚶🏻</p>
<blockquote>
<p>** DNS 서버 💻**
사용자가 입력한 도메인 네임을 인터넷 통신 규격인 IP로 바꾸어 주는 역할을 한다.
처음 방문하는 사이트의 경우 외부 DNS 서버를 통해 IP를 받아오게 되고, 로컬 DNS 서버에 저장해둔다.</p>
</blockquote>
<h3 id="1-2-서버와-연결하기--tcp-3-way-handshake">1-2. 서버와 연결하기 : TCP 3-way Handshake</h3>
<p>서버의 주소를 알아냈다면 이제 통신을 위해 연결해야 한다. 이때 <code>3-way Handshake</code> 기법을 사용하게 된다. 🤝</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/f13f448e-729c-4726-b1e3-604e620f0b61/image.png" alt=""></p>
<h4 id="3-way-handshake-과정-✏️">3-way Handshake 과정 ✏️</h4>
<ol>
<li>클라이언트가 서버와 연결을 맺기 위해 <code>SYN</code> 를 보낸다.</li>
<li>서버가 SYN를 받고, 응답 신호인 <code>ACK</code> 와 <code>SYN</code> 패킷을 보낸다.</li>
<li>클라이언트는 잘 받았다는 <code>ACK</code> 응답 신호를 보내면서 연결이 맺어진다.</li>
</ol>
<h1 id="✌🏻-화면정보-요청하기">✌🏻 화면정보 요청하기</h1>
<p>서버와 데이터 통신을 하기위한 연결을 맺은 후 <code>HTML</code> , <code>CSS</code> 등과 같은 화면 정보를 요청해야 하는데 이때, <code>HTTP &amp; HTTPS</code> 프로토콜을 사용하게 된다.
<img src="https://velog.velcdn.com/images/jae_han_e/post/0be564c2-5148-4e62-9ccc-abc7792d28e1/image.png" alt=""></p>
<blockquote>
<p><strong>HTTP ( Hyper Text Transfer Protocol )</strong>
Hyper Text(HTML)을 전송하기 위한 프로토콜로 클라이언트에서 <code>request</code> 를 보내면 서버에서 <code>response</code>로 응답하는 통신 구조를 갖는다.</p>
</blockquote>
<h1 id="🤟🏻-응답-파일-해석하기">🤟🏻 응답 파일 해석하기</h1>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/83c3ed69-865f-43c0-bebc-721a307d0c74/image.png" alt="">
응답받은 파일을 렌더링하기 전, <code>Render Tree</code> 로 만드는 과정에 대해 알아보자❗️</p>
<h3 id="3-1-dom--cssom-만들기">3-1. DOM &amp; CSSOM 만들기</h3>
<p>브라우저는 소스코드로 된 HTML 응답을 받으면 <code>구문분석(Parsing)</code> 을 통해 브라우저가 이해할 수 있는 <code>Dom Tree</code> 와 <code>CSSOM</code> 으로 바꿔주어야 한다.🤓</p>
<h4 id="dom--document-object-model-">&lt; DOM ( Document Object Model ) &gt;</h4>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/b6dcaf9b-e4cd-4bac-a851-d8ad60d7e3ad/image.png" alt=""></p>
<p>브라우저는 응답 HTML을 <code>DOM(Document Object Model)</code> 트리를 만든다.</p>
<h4 id="cssom--css-object-model-">&lt; CSSOM ( CSS Object Model )&gt;</h4>
<p>DOM과 만찬가지로 CSS도 브라우저가 이해할 수 있는 형식으로 만들어야 한다. 
<img src="https://velog.velcdn.com/images/jae_han_e/post/a4ad7189-3d26-4255-aef8-1f15a40472d5/image.png" alt="">
CSSOM은 웹 페이지를 꾸미는 스타일의 총 집합니다. 색상, 크기, 위치, 배치 방법 등의 정보가 있으며 <strong>DOM과 다른 스레드에서 작업하기 때문에 서로 영향을 받지 않는다.</strong> 😃</p>
<h4 id="javascript-실행">&lt; Javascript 실행 &gt;</h4>
<p>Javascript 는 브라우저가 HTML 파싱 중 <code>&lt;script&gt;</code> 태그를 만나면 실행된다.</p>
<p>JS가 실행되면 DOM 구조를 변경할 수 있기 때문에 코드를 해석하고 실행이 완료될 때까지 DOM 생성을 멈추게 한다. 🫷🏻</p>
<h4 id="render-tree">&lt; Render Tree &gt;</h4>
<p>만들어진 DOM 과 CSSOM 을 합쳐서 <code>Render Tree</code> 를 만들면 파싱이 끝나게 된다.</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/ee7cb672-5f8b-4171-834e-01c7379f5ee6/image.png" alt=""></p>
<p>브라우저가 최종적으로 그릴 요소와 스타일 정보는 들어있지만 DOM의 <code>script</code> , <code>meta</code>와 CSS의 <code>display: none</code> 속성의 요소는 들어있지 않는다. ❌</p>
<h3 id="파싱-전체-과정-✏️">파싱 전체 과정 ✏️</h3>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/5ff5669a-06d0-4865-9598-5ec09bd3b0dc/image.png" alt=""></p>
<h1 id="🖖🏻-화면-그리기">🖖🏻 화면 그리기</h1>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/41b4495f-4f48-48c0-a6f4-639f7d323e5c/image.png" alt=""></p>
<p>위에서 만든 Render Tree 를 그리는 과정 <code>Layout</code> &gt; <code>Paint</code> &gt; <code>Composite</code> 순으로 진행된다.
실제로 화면이 그려지는 과정이기 때문에 네트워크 과정보다 시간이 더 오래 소요된다. 🕗</p>
<h3 id="4-1-layout--reflow-📐">4-1. Layout &amp; Reflow 📐</h3>
<p>Layout은 웹 페이지를 기준으로 <strong>각 요소들의 크기와 위치, 간격을 결정하는 작업이다.</strong></p>
<p>이 과정이 처음 일어날 때에는 <code>Layout</code> 이라고 하지만 특정 액션으로 인해 재계산이 되는 경우에는 <code>Reflow</code> 라고 부른다.</p>
<blockquote>
<p>*<em>Reflow *</em>
Reflow 가 발생하면 <code>paint</code> 와 <code>Composite</code> 이 다시 이루어지기 때문에 계산이 가장 많이 든다.</p>
</blockquote>
<h3 id="4-2-paint--repaint-🎨">4-2. Paint &amp; RePaint 🎨</h3>
<p>Paint는 <strong>텍스트, 색상, 테두리, 그림자 등 요소의 시각적 부분을 화면에 그리는 과정이다.</strong></p>
<p>Reflow 가 발생해서 다시 paint 하는 것을 <code>RePaint</code> 라고 한다.</p>
<h3 id="4-3-composite-🛠️">4-3. Composite 🛠️</h3>
<p>Layout 과정 중 HTML, CSS 속성에 따라 요소들이 서로 다른 레이어에 그려질 때가 있다. 
보통 <code>&lt;video&gt;</code>, <code>&lt;canvas&gt;</code> 태그나 CSS의 <code>position</code>, <code>z-index</code> 속성에 의해 발생한다.</p>
<p><strong>이렇게 레이어가 2개 이상 생성될 때, 각 레이어를 하나로 합치는 과정을 <code>Composite</code> 라고 한다.</strong></p>
<h1 id="전체-과정-요약-✏️">전체 과정 요약 ✏️</h1>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/ab60901f-b06f-45be-bdb8-2773eb7224b7/image.png" alt=""></p>
<h1 id="리렌더링-🔄">리렌더링 🔄</h1>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/ab496011-d35b-4081-8c4d-512cf3f983fa/image.png" alt=""></p>
<p>이벤트가 발생해서 화면이 다시 그려져야 할 경우에는 <code>Javascript</code> 가 <code>DOM</code> 을 직접적으로 변경하게 되고 새로운 <code>Render Tree</code>가 만들어져 그려지게 된다. ( with. <strong>Reflow &amp; RePainting</strong> )</p>
<p>이는 시간이 오래걸리는 작업임으로 성능에 큰 영향을 끼치게 되므로
개발을 할 때 특히 더 신경써야 한다.</p>
<h3 id="안좋은-예-👺">안좋은 예 👺</h3>
<pre><code class="language-javascript">&lt;script&gt;
   // 클릭 하게되면 ul 태그 안에 li 태그를 넣어주기때문에
  // 총 3천번의 RePainting 이 발생하게 된다.
 // 실행시간: 4,500 ms
  const onClick = () =&gt;{
    const $ul = document.getElementById(&#39;ul&#39;)
    for(let i = 0; i &lt; 3000; i++){
      $ul.innerHTML += `&lt;li&gt;${i}&lt;/li&gt;`; // ❌
    }
  }
&lt;/script&gt;
&lt;body&gt;
  &lt;button onClick=&quot;onClick()&quot;&gt;리스트 추가하기&lt;/button&gt;
  &lt;ul id=&quot;ul&gt;&lt;/ul&gt;
&lt;/body&gt;</code></pre>
<h3 id="좋은-예-👼🏻">좋은 예 👼🏻</h3>
<pre><code class="language-javascript">&lt;script&gt;
   // 업데이트 할 내용을 모아 한번만 반영한다.
  // 실행시간: 250 ms
  const onClick = () =&gt;{
    const $ul = document.getElementById(&#39;ul&#39;)
    let list = &quot;&quot;

    for(let i = 0; i &lt; 3000; i++){
      list += `&lt;li&gt;${i}&lt;/li&gt;`;
    }

    $ul.innerHTML = list  // ⭕️
  }
&lt;/script&gt;
&lt;body&gt;
  &lt;button onClick=&quot;onClick()&quot;&gt;리스트 추가하기&lt;/button&gt;
  &lt;ul id=&quot;ul&gt;&lt;/ul&gt;
&lt;/body&gt;</code></pre>
<blockquote>
<p>React JS 는 별도의 <a href="https://velog.io/@jae_han_e/%EB%A6%AC%EC%95%A1%ED%8A%B8-React-%EB%A0%8C%EB%8D%94%EB%A7%81-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4">렌더링 프로세스</a>를 가지고 있어 이러한 작업을 자동적으로 처리해준다.</p>
</blockquote>
<blockquote>
<p>참조
[MDN Web Docs]
(<a href="https://developer.mozilla.org/ko/docs/Web/Performance/How_browsers_work">https://developer.mozilla.org/ko/docs/Web/Performance/How_browsers_work</a>)
[개발지망생]
(<a href="https://opendeveloper.tistory.com/entry/FrontEnd-%EC%A7%80%EC%8B%9D%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%9B%90%EB%A6%AC%EC%99%80-%EC%88%9C%EC%84%9C%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B3%A0%EB%A0%A4%EC%82%AC%ED%95%AD">https://opendeveloper.tistory.com/entry/FrontEnd-%EC%A7%80%EC%8B%9D%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%9B%90%EB%A6%AC%EC%99%80-%EC%88%9C%EC%84%9C%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B3%A0%EB%A0%A4%EC%82%AC%ED%95%AD</a>)
[imqa.io]
(<a href="https://blog.imqa.io/webpage_loading_process/">https://blog.imqa.io/webpage_loading_process/</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[FSD(Feature Sliced Design) 아키텍처]]></title>
            <link>https://velog.io/@jae_han_e/FSDFeature-Sliced-Design-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98</link>
            <guid>https://velog.io/@jae_han_e/FSDFeature-Sliced-Design-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98</guid>
            <pubDate>Sat, 20 Jul 2024 06:42:53 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>프론트엔드에서 많이 사용하고 있는 아키텍처 중 하나인 <code>FSD</code>에 대해 정리해보려고 한다.</p>
<p><code>FSD</code> 는 <strong>Feature Sliced Design</strong> 의 약자로, 기능 분할 설계를 의미하며</p>
<p>이론과 함께 FSD로 설계된 <a href="https://github.com/noveogroup-amorgunov/nukeapp">오픈소스</a>를 통해 알아가보자 ✏️</p>
<h1 id="구성요소">구성요소</h1>
<p>FSD는 <code>Layer(레이어)</code>, <code>Slice(슬라이스)</code>, <code>Segment(세그먼트)</code> 로 구성된다. 😀
<img src="https://velog.velcdn.com/images/jae_han_e/post/46ee23d9-4c18-4d05-b913-db9cf2170330/image.png" alt=""></p>
<h3 id="☝🏻-layer">☝🏻 Layer</h3>
<p>레이어는 최상위 디렉토리이며, 프로젝트에 따라 최대 7개까지 사용할 수 있다.</p>
<p>각 레이어들은 어플리케이션 전반에 영향을 미칠 수 있는 고유한 역할에 따라 구분되며 </p>
<p><strong><code>상위 레이어는 하위 레이어를 의존성으로 가질 수 있다.</code></strong> 😃</p>
<ul>
<li><code>app</code> : 어플리케이션 로직이 초기화 되는 곳으로 프로바이더, 라우터, 전역스타일, 전역 타입 등이 정의되어 진다.</li>
<li><code>process</code>: 등록과 같이 여러 화면에 걸친 복잡한 프로세스를 처리 한다.</li>
<li><code>page</code>: 어플리케이션의 실제 화면들이 정의된다.</li>
<li><code>widgets</code>: 화면에 사용되는 독립적인 UI 컴포넌트들이 정의된다.</li>
<li><code>feature</code>: 좋아요, 신고, 작성 등과 같이 인터렉션이 필요한 기능이 정의된다.</li>
<li><code>entities</code>: 게시물, 리뷰와 같이 화면에 나타나는 엔티티 들이 정의된다.</li>
<li><code>shared</code>: axios, UI키트 등 여러 화면에서 공통으로 사용하는 기능들이 정의된다. </li>
</ul>
<h3 id="✌🏻-slice">✌🏻 Slice</h3>
<p>슬라이스는 두번째 계층으로 이름이 정해져있진 않고, 보통 비즈니스 도메인으로 이름이 정해진다.</p>
<p>만약, 상품을 판매하는 어플리케이션이라면 <strong>cart</strong>, <strong>category</strong>, <strong>product</strong>, <strong>wishlist</strong> 등이 올 수 있다.</p>
<p>슬라이스 내부에서는 비즈니스와 연관된 로직을 작성할 수 있으며</p>
<p>✅ <code>app</code> 과 <code>shared</code> 레이어에서는 어플리케이션의 전체적인 코드를 작성하므로 <code>Slice 를 갖지 않는다.</code></p>
<h3 id="🤟🏻-segment">🤟🏻 Segment</h3>
<p>세그먼트는 세번째 계층으로, 슬라이스를 목적에 따라 나눠 놓은 것이다.</p>
<p>일반적으로 사용하는 세그먼트들은 아랭와 같다 </p>
<ul>
<li><code>api</code>: 백앤드 api를 호출하는 코드</li>
<li><code>ui</code>: 슬라이스의 UI 컴포넌트</li>
<li><code>model</code>: 비즈니스 로직</li>
<li><code>lib</code>: 슬라이스에서 사용하는 보조 기능</li>
<li><code>const</code>: 슬라이스에서 사요하는 상수</li>
</ul>
<h2 id="💡-공개-api">💡 공개 API</h2>
<p>각 Slice 와 Segment는 index 파일을 갖으며, 이 파일을 통해 요소들을 외부로 export 시킨다.</p>
<p>즉, 진입점 역할을 하며 이를 공개 API 라고 부른다.</p>
<p>어플리케이션은 공개 API에 정의된 기능만 사용할 수 있다.</p>
<h1 id="nuke-app-📱">Nuke App 📱</h1>
<p>FSD로 설계된 <a href="https://github.com/noveogroup-amorgunov/nukeapp">Nuke App</a> 이다.
<img src="https://velog.velcdn.com/images/jae_han_e/post/1d46967e-f6d7-4a89-a883-5aa866b4afe7/image.png" alt=""></p>
<p>먼저, 첫번째 요소인 <code>Layer</code> 를 살펴보면
<img src="https://velog.velcdn.com/images/jae_han_e/post/519684d5-27c1-4eca-939b-3607e053179b/image.png" alt=""></p>
<p><code>app</code>, <code>pages</code>, <code>widgets</code>, <code>features</code>, <code>entities</code>, <code>shared</code> 로 구서되어 있고</p>
<p>두번째 요소인 <code>Slices</code>는 비즈니스 도메인 이름을 따
<img src="https://velog.velcdn.com/images/jae_han_e/post/e4b792a7-654c-40ab-bea0-f0617a25b666/image.png" alt=""></p>
<p>이와 같이 구성되어있다.</p>
<p>세번째 요소인 <code>segments</code> 는 <code>api</code>, <code>lib</code>, <code>model</code> 로 구성되어 있고</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/14d565c3-2c54-411a-8125-e6ec267ca8e1/image.png" alt=""></p>
<p>index.ts 를 통해 밖으로 export 한다.</p>
<pre><code class="language-typescript">//index.ts
export { type Cart, type CartItem } from &#39;./model/types&#39;
export {
  cartSlice,
  selectProductsInCart,
  selectProductInCart,
  selectTotalQuantity,
  selectCartTotalPrice,
  addOneItem,
  removeOneItem,
  removeItem as removeProductFromCart,
  clearCartData,
  incVersion,
} from &#39;./model/slice&#39;
export { mapCartItemDto } from &#39;./lib/mapCartItemDto&#39;
export { cartApi } from &#39;./api/cartApi&#39;
export { type CartItemDto } from &#39;./api/types&#39;
</code></pre>
<p>FSD 아키텍처를 화면에 표현하면 아래와 같다 😁
<img src="https://velog.velcdn.com/images/jae_han_e/post/254d693a-2a9a-4017-964c-d4e298256d34/image.png" alt=""></p>
<blockquote>
<p>참조
[emewjin.log]
(<a href="https://emewjin.github.io/feature-sliced-design/#%EC%9E%A5%EC%A0%90">https://emewjin.github.io/feature-sliced-design/#%EC%9E%A5%EC%A0%90</a>)
[dante.log]
(<a href="https://velog.io/@jay/fsd">https://velog.io/@jay/fsd</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript Study(8) - 유동적인 타입 설정]]></title>
            <link>https://velog.io/@jae_han_e/TypeScript-Study8-%EC%9C%A0%EB%8F%99%EC%A0%81%EC%9D%B8-%ED%83%80%EC%9E%85-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@jae_han_e/TypeScript-Study8-%EC%9C%A0%EB%8F%99%EC%A0%81%EC%9D%B8-%ED%83%80%EC%9E%85-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sun, 07 Jul 2024 10:45:27 GMT</pubDate>
            <description><![CDATA[<h1 id="타입-설정">타입 설정</h1>
<p><code>유동적인 타입 설정</code>이란, 기본 타입이나 별칭 또는 인터페이스로 만든 원래 존재하던 타입들을 </p>
<p>상황에 따라 유동적으로 다른 타입으로 변환하는 것을 말하고, </p>
<p>이전에 정리했던 <code>제네릭</code> 도 그 방법 중 하나이며</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/021569cb-ae08-4a39-9f94-dda9130d495f/image.png" alt=""></p>
<p>순서대로 사용법을 정리해보고자 한다.</p>
<h1 id="인덱스드-엑세스-타입">인덱스드 엑세스 타입</h1>
<p><code>객체</code>, <code>배열</code>, <code>튜플</code> 타입으로부터 <strong>특정 프로퍼티나 특정 요소의 타입만 추출</strong>하는 것이다.</p>
<h2 id="객체에서-사용하기">객체에서 사용하기</h2>
<pre><code class="language-typescript">interface Post {
  title: string;
  content: string;
  author: {
    id: string;
    name: string;
    age: number;
  }
}

const post: Post = {
  title: &#39;제목&#39;,
  content: &#39;본문&#39;,
  author:{
    id: &#39;1&#39;,
    name: &#39;jaehan&#39;,
    age: 31
  }
}

// author 출력
const printAuthorInfo = (author: ??? ) =&gt; {
  console.log(`${author.name} - ${author.age}`)
}</code></pre>
<p><code>post</code> 객체의 <code>author</code> 정보를 출력해주는 함수 <code>printAuthorInfo</code> 가 있다.</p>
<p>이때, 매개변수 <code>author</code> 의 타입을 지정해 주어야하는데 </p>
<pre><code class="language-typescript">const printAuthorInfo = (author: {id: string, name: string, age: number} ) =&gt; {
  console.log(`${author.name} - ${author.age}`)
}</code></pre>
<p>이렇게 직접 타입을 지정해주어도 문제는 없지만</p>
<p>원본 타입인 <code>Post</code> 의 <code>author</code> 가 변경 될 경우 <code>author</code> 를 매개변수로 갖는 모든 함수들을 일일이 수정해 주어야 한다.</p>
<p>이때, <code>인덱스드 엑세스 타입</code>을 사용하면 간단하게 해결할 수 있다.</p>
<pre><code class="language-typescript">const printAuthInfo = (author: Post[&#39;author&#39;]) =&gt; { // ✅
  console.log(`${author.name} - ${author.age}`)
}</code></pre>
<p>한단계 더 들어가 <code>author</code>의 <code>name</code> 타입에 접근하고 싶다면</p>
<pre><code class="language-typescript">const printAuthorInfo = (author: Post[&#39;author&#39;][&#39;name&#39;]) =&gt; { // ✅
  console.log(`${author}`)
}
</code></pre>
<p>이렇게 사용하면 된다.</p>
<h2 id="배열에서-사용하기">배열에서 사용하기</h2>
<p>배열 타입에서 하나의 요소 정보를 가져오기 위해서는</p>
<p><code>배열타입 [number]</code> 형식으로 사용하면 된다.</p>
<pre><code class="language-typescript">type PostList = {
  title: string;
  content: string;
  author: {
    id: string;
    name: string;
    age: number;
  }
}[]


const post: PostList[number] = { // ✅
  title: &#39;제목&#39;,
  content: &#39;본문&#39;,
  author:{
    id: &#39;1&#39;,
    name: &#39;jaehan&#39;,
    age: 31
  }
}

const printAuthorInfo = (author: PostList[number][&#39;author&#39;]) =&gt; { // ✅
  console.log(`${author.name} - ${author.age}`)
}</code></pre>
<p><code>PostList[number]</code> 는 <strong>PostList 배열 타입의 하나의 요소의 타입을 가져온다</strong>는 뜻이다.</p>
<h1 id="keyof-연산자">keyof 연산자</h1>
<p><code>keyof</code> 는 객체 타입에서만 사용이 가능한 연산자이다.</p>
<p>특정 객체 타입의 프로퍼티 키들을 <strong>스트링 리터럴 유니온 타입으로 추출하는 연산자</strong>이다.</p>
<p>말이 어려운데, 코드를 보면 간단하다.</p>
<pre><code class="language-typescript">interface Person {
  name: string;
  age: number;
}

// 객체와 key 값을 받아 return
const getPropertyKey = (person: Person, key: keyof Person) =&gt;{// ✅
  return person[key]
}

const person: Person ={
  name: &#39;jaehan&#39;,
  age: 31
}

getPropertyKey(person, &#39;age&#39;)</code></pre>
<p>위의 코드에서 매개변수 key의 타입을 <code>keyof Person</code> 으로 선언함으로써 <code>&#39;name&#39; | &#39;age&#39;</code> 로 설정된다. </p>
<p>이는 <code>Person</code> 타입의 프로퍼티 키들을 스트링 리터럴 유니온 타입으로 추출 된 모습이다.</p>
<h1 id="mapped-타입">Mapped 타입</h1>
<p><strong>기존의 객체 타입으로부터 새로운 객체 타입을 만들 때 사용</strong>한다.</p>
<pre><code class="language-typescript">interface Person {
  name: string;
  age: number;
}

type ReadonlyPerson = {
  readonly [key in keyof Person]?: Person[key] // ✅
}</code></pre>
<p>뭔가 복잡해 보이지만 하나하나 뜯어보면 간단하다.</p>
<p><code>Person</code> 타입과 동일한 타입을 만드는데, <strong>모든 프로퍼티에 readonly 와 Optional 을 추가</strong>해준 것이다.</p>
<p>프로퍼티 키는 <code>keyof</code> 연산자로 받아오고 각각의 타입은 <code>?: Person[key]</code>로 정의 한 것이다.</p>
<h1 id="템플릿-리터럴-타입">템플릿 리터럴 타입</h1>
<p><strong>기존의 스트링 리터럴 타입을 기반으로 정해진 패턴</strong>의 문자열만 포함하는 타입이다.</p>
<pre><code class="language-typescript">type Color = &#39;red&#39; | &#39;black&#39; ;

type Animal = &#39;dog&#39; | &#39;cat&#39; ;

type ColoredAnimal = `${Color}-${Animal}`
</code></pre>
<p>이렇게 선언 할 경우
<code>ColoredAnimal</code> 타입은 <code>&#39;red-dog&#39; | &#39;red-cat&#39; | &#39;black-dog&#39; | &#39;black-cat&#39;</code> 로 된다.</p>
<blockquote>
<p>참고
[한 입 크기로 잘라먹는 타입스크립트] (<a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript Study(7) - 제네릭 ( Generic )]]></title>
            <link>https://velog.io/@jae_han_e/TypeScript-Study7-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@jae_han_e/TypeScript-Study7-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%95%A8%EC%88%98</guid>
            <pubDate>Sun, 30 Jun 2024 07:20:02 GMT</pubDate>
            <description><![CDATA[<h1 id="제네릭-함수">제네릭 함수</h1>
<p><code>타입 변수</code>를 통해 <code>매개변수의 타입</code>과 <code>반환값의 타입</code>을 지정할 수 있는 함수를 말한다.</p>
<p>하나 이상의 타입을 사용하려면 <code>UNION 타입</code> 이나 <code>any 타입</code> 을 사용했어야 했는데,</p>
<p>타입 변수를 사용하면 함수가 호출되는 시점에 타입이 지정되므로 더 안전하게 사용할 수 있다.</p>
<pre><code class="language-typescript">function func&lt;T&gt;(value: T): T {
  return value;
}

// Arrow Function
const func2 = &lt;T&gt;(n1:T):T =&gt; n1

let num = func(10); // number 타입
</code></pre>
<p>함수 이름 뒤에 타입변수 <code>&lt;T&gt;</code> 를 붙여주고,  매개변수와 반환값의 타입을 이 타입변수 T로 설정해서 사용한다.</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/f6875f0d-5136-411d-a970-8c6679265f5f/image.png" alt=""></p>
<p>이처럼 함수를 호출하는 시점에서 매개변수 타입인 <code>number</code> 가 타입변수 <code>T</code> 에 전달된다.</p>
<h3 id="다중-타입변수-사용">다중 타입변수 사용</h3>
<p>타입변수는 여러개 사용이 가능하다.</p>
<pre><code class="language-typescript">function swap&lt;T, U&gt;(a: T, b: U) { //  T: string, U: number
  return [b, a];
}

const [a, b] = swap(&quot;1&quot;, 2);</code></pre>
<h2 id="복잡한-메서드-타입-정의">복잡한 메서드 타입 정의</h2>
<h3 id="map-method">map Method</h3>
<p>map 과 동일한 기능을 수행하는 함수를 선언해보자</p>
<pre><code class="language-typescript">function map&lt;T&gt;(arr: T[], callback: (item: T) =&gt; T): T[] {
  let result = [];
  for (let i = 0; i &lt; arr.length; i++) {
    result.push(callback(arr[i]));
  }
  return result;
}</code></pre>
<p>매개변수 <code>arr</code> 로 전달받은 배열을 순회하면서 <code>callback</code> 함수를 실행시켜 새로운 배열을 리턴해 주는 형식이다.</p>
<pre><code class="language-typescript">const arr = map([1,2,3], (item) =&gt; item * 2) // [2, 4, 6]</code></pre>
<p>호출되는 시점에 <code>타입변수 T</code> 는 <code>number</code> 로 정의된다.</p>
<p>하지만 아래의 경우 에러가 발생하게 된다.</p>
<pre><code class="language-typescript">const arr = map([1,2,3], (it) =&gt; it.toString()); // ❌</code></pre>
<p>함수의 리턴 타입이 <code>number[]</code>로 설정되었는데, <code>string[]</code> 리턴 값이 만들어지기 때문이다. </p>
<pre><code class="language-typescript">function map&lt;T, U&gt;(arr: T[], callback: (item: T) =&gt; U): U[] {
  (...)
}

const arr = map([1,2,3], (it) =&gt; it.toString()); // ✅ 성공</code></pre>
<p>위에서 적어놓은 다중타입을 설정하면 해결할 수 있다.</p>
<h2 id="제내릭-인터페이스--타입">제내릭 인터페이스 &amp; 타입</h2>
<p>타입변수를 활용하면 타입과, 인터페이스에도 적용할 수 있다.</p>
<pre><code class="language-typescript">interface KeyPair&lt;K, V&gt; {
  key: K;
  value: V;
}

type Map2&lt;V&gt; = {
  [key: string]: V;
};

let stringMap2: Map2&lt;string&gt; = {
  key: &quot;string&quot;,
};</code></pre>
<p>타입변수 K, V를 사용해서 인터페이스와 티입을 선언했다.</p>
<pre><code class="language-typescript">let keyPair: KeyPair&lt;string, number&gt; = {
  key: &quot;key&quot;,
  value: 0,
};

let keyPair2: KeyPair&lt;boolean, string[]&gt; = {
  key: true,
  value: [&quot;1&quot;],
};

let stringMap2: Map2&lt;string&gt; = {
  key: &quot;string&quot;,
};</code></pre>
<blockquote>
<p>참고
[한 입 크기로 잘라먹는 타입스크립트] (<a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript Study(6) - 인터페이스]]></title>
            <link>https://velog.io/@jae_han_e/TypeScript-Study6-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@jae_han_e/TypeScript-Study6-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Sun, 30 Jun 2024 06:29:22 GMT</pubDate>
            <description><![CDATA[<h1 id="인터페이스-interface">인터페이스 (Interface)</h1>
<p>기존의 <code>타입 별칭</code> 처럼, 타입에 이름을 지어주는 또다른 문법이다.</p>
<p>객체 구조를 정의하는데 특화된 문법이며 <code>상속</code>, <code>합침</code> 등 <strong>특수한 기능을 제공</strong> 해준다.</p>
<p>문법은 아래와 같다.</p>
<pre><code class="language-typescript">// 타입 별칭
type Person2 = {
  readonly name: string;
  age?: number;
  sayHi: () =&gt; void;
}

// 인터페이스
interface Person {
  readonly name: string;
  age?: number;
  sayHi: () =&gt; void;
}

const p1: Person = {
  name: &#39;jaehan&#39;,
  age: 31,
  sayHi: () =&gt; console.log(&#39;hi&#39;)
}
</code></pre>
<h2 id="인터페이스-확장">인터페이스 확장</h2>
<p>인터페이스 간의 상속을 통해 중복된 프로퍼티를 정의하지 않도록 도와주는 문법이다.</p>
<pre><code class="language-typescript">interface Animal {
  name: string;
  age: number;
}

interface Dog {
  name: string;
  age: number;
  isBark: boolean;
}

interface Cat {
  name: string;
  age: number;
  isScratch: boolean;
}

interface Chicken {
  name: string;
  age: number;
  isFly: boolean;
}</code></pre>
<p>이처럼 <code>Dog</code>, <code>Cat</code>, <code>Chicken</code> 인터페이스에 name과 age 가 중복되어 선언되어있다.</p>
<p>이때 <code>extends</code> 문법을 활용해서 간단하게 표현할 수 있다.</p>
<pre><code class="language-typescript">interface Animal {
  name: string;
  color: string;
}

interface Dog extends Animal {
  breed: string;
}

interface Cat extends Animal {
  isScratch: boolean;
}

interface Chicken extends Animal {
  isFly: boolean;
}</code></pre>
<p> 상속받은 프로퍼티를 재정의 할 수 있긴하지만 약간의 제약이 따른다.</p>
<p> <code>Dog</code> 인터페이스에서 <code>name</code> 프로퍼티 타입을 재정의 한다고 가정해보자</p>
<pre><code class="language-typescript">interface Animal {
  name: string;
  color: string;
}

interface Dog extends Animal {
  name: &quot;doldol&quot;; // 타입 재 정의
  breed: string;
}</code></pre>
<p>이처럼 프로퍼티를 재정의 할 때에는 <strong>원본 프로퍼티의 서브타입만 허용</strong>한다.</p>
<p>string 리터럴 타입인 <code>name: &#39;doldol&#39;</code>은 원본 프로퍼티 <code>name: string</code> 의 서브 타입이다.</p>
<pre><code class="language-typescript">interface Animal {
  name: string;
  color: string;
}

interface Dog extends Animal {
  name: number; // ❌ number는 string의 서브타입이 아님
  breed: string;
}</code></pre>
<h2 id="인터페이스-다중-확장">인터페이스 다중 확장</h2>
<p>여러개의 인터페이스를 확장하는 것도 가능하다</p>
<pre><code class="language-typescript">interface DogCat extends Dog, Cat {}

const dogCat: DogCat = {
  name: &quot;&quot;,
  color: &quot;&quot;,
  breed: &quot;&quot;,
  isScratch: true,
};</code></pre>
<h2 id="인터페이스-선언-합침">인터페이스 선언 합침</h2>
<p>타입 별칭은 동일한 스코프 내에 중복된 이름으로 선언할 수 없지만 인터페이스는 가능하다.</p>
<pre><code class="language-typescript">type Person = {
  name: string;
};

type Person = {     //❌
  age: number;
};
----------------------
interface Person {
  name: string;
}

interface Person { // ✅
  age: number;
}

&gt;&gt; Result
interface Person {
  name: string;
  age: number;
}</code></pre>
<blockquote>
<p>참고
[한 입 크기로 잘라먹는 타입스크립트] (<a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript Study(5) - 함수 타입]]></title>
            <link>https://velog.io/@jae_han_e/TypeScript-Study5-%ED%95%A8%EC%88%98-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@jae_han_e/TypeScript-Study5-%ED%95%A8%EC%88%98-%ED%83%80%EC%9E%85</guid>
            <pubDate>Sat, 22 Jun 2024 13:07:15 GMT</pubDate>
            <description><![CDATA[<h1 id="함수-타입">함수 타입</h1>
<p>함수에서 타입을 저의 할 때에는 <code>매개변수</code> 의 타입을 지정해주고 <code>반환값</code> 에 맞는 타입을 지정해주면 된다.</p>
<p>( 전에 배운 <code>타입추론</code> 에 의해 함수의 반환값 타입은 생략이 가능하다. 😎 )</p>
<pre><code class="language-typescript">function func(a: number, b: number) {
  return a + b;

 // arrow function
const add = (a: number, b: number) =&gt; a + b;
}</code></pre>
<h2 id="선택적-매개변수">선택적 매개변수</h2>
<p>함수에서 매개변수를 받을때 필수로 받지 않아도 되는 경우에 사용한다.</p>
<pre><code class="language-typescript">function introduce(name = &quot;jaehan&quot;, weight?: number) {
  console.log(`name : ${name}`);
  console.log(`weight : ${weight}`);
}

introduce(&quot;jaehan&quot;, 65); // ✅ 성공

introduce(&quot;jaehan&quot;); // ✅ 성공</code></pre>
<h3 id="주의-사항">주의 사항</h3>
<ul>
<li><code>undefined</code> 값이 올 수 있으므로, 값이 있는지 확인 후 사용해야 한다.</li>
</ul>
<pre><code class="language-typescript">function introduce(name = &quot;jaehan&quot;, weight?: number) {
  console.log(`name : ${name}`);
  if (!!weight) { // 조건문이 없으면, Object is possibly &#39;undefined&#39;. 에러 발생
    console.log(`weight : ${weight + 10}`);
  }
}

introduce(&quot;jaehan&quot;, 65); // ✅ 성공
</code></pre>
<ul>
<li><p>선택적 매개변수는 필수 매개변수 앞에 올 수 없다.</p>
<pre><code class="language-typescript">function introduce(name = &quot;jaehan&quot;, weight?: number, age: number) { // ❌error

console.log(`name : ${name}`);
if (!!weight) {
  console.log(`weight : ${weight + 10}`);
}
}</code></pre>
</li>
</ul>
<h2 id="rest-매개변수">rest 매개변수</h2>
<p>매개변수로 보내는 숫자들을 모두 더해주는 함수를 만들어보자</p>
<p>몇 개의 숫자가 올지 모르기 때문에 rest 매개변수를 사용한다.</p>
<pre><code class="language-typescript">function getSum(...rest: number[]) { // [1,2,3] / [1,2,3,4] 형태로 전달된다.
  let sum = 0;
  rest.forEach((it) =&gt; (sum += it));
  return sum;
}

getSum(1, 2, 3)    // 6
getSum(1, 2, 3, 4) // 10</code></pre>
<h2 id="함수-타입-표현식">함수 타입 표현식</h2>
<p>함수도 타입 별칭을 만들어서 타입을 지정할 수 있는데, 이를 <code>함수 타입 표현식</code> 이라고 부른다.</p>
<pre><code class="language-typescript">type Operation = (a: number, b: number) =&gt; number;

const add: Operation = (a, b) =&gt; a + b;
const sub: Operation = (a, b) =&gt; a - b;
const multiply: Operation = (a, b) =&gt; a * b;
const divide: Operation = (a, b) =&gt; a / b;</code></pre>
<h2 id="호출-시그니처">호출 시그니처</h2>
<p>함수 타입 표현식과 동일하지만, 표현하는 방법이 조금 다르다.</p>
<pre><code class="language-typescript">type Operation2 = {
  (a: number, b: number): number;
};

const add2: Operation2 = (a, b) =&gt; a + b;
const sub2: Operation2 = (a, b) =&gt; a - b;
const multiply2: Operation2 = (a, b) =&gt; a * b;
const divide2: Operation2 = (a, b) =&gt; a / b;</code></pre>
<h1 id="함수-오버로딩">함수 오버로딩</h1>
<p>동일한 이름의 함수를 <code>매개변수의 갯수</code> 나 <code>타입</code> 에 따라 다르게 선언하는 것을 말한다.</p>
<p>💡 <strong>JavaScript 에서는 사용이 불가</strong>하고 <strong>TypeScript 에서만 사용 가능하다.</strong> </p>
<p>함수 오버로딩을 구현하려면 <code>오버로드 시그니쳐</code> 라는 것을 만들어줘야한다.</p>
<pre><code class="language-typescript">// fnc1. 매개변수가 1개이면 매개변수에 20을 곱하기
// fnc2. 매개변수가 3개이면 모든 매개변수 더하기

// 매개변수 별 함수 선언: 오버로드 시그니쳐 선언
function func(a: number): void;
function func(a: number, b: number, c: number): void;

// 구현부 : 구현 시그니처
function func(a: number, b?: number, c?: number) {
  if (typeof b === &quot;number&quot; &amp;&amp; typeof c === &quot;number&quot;) {
    console.log(a + b + c);
  } else {
    console.log(a * 20);
  }
}

func(1);
func(1,2,3);</code></pre>
<blockquote>
<p>참고
[한 입 크기로 잘라먹는 타입스크립트] (<a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript Study(4) - 타입 추론 & 타입 단언 & 타입 가드]]></title>
            <link>https://velog.io/@jae_han_e/TypeScript-Study4-%ED%83%80%EC%9E%85-%EC%B6%94%EB%A1%A0-%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8</link>
            <guid>https://velog.io/@jae_han_e/TypeScript-Study4-%ED%83%80%EC%9E%85-%EC%B6%94%EB%A1%A0-%ED%83%80%EC%9E%85-%EB%8B%A8%EC%96%B8</guid>
            <pubDate>Sat, 22 Jun 2024 05:39:44 GMT</pubDate>
            <description><![CDATA[<h1 id="타입-추론">타입 추론</h1>
<p><code>TypeScript</code> 는 타입이 정의되어있지 않은 변수의 타입을 자동으로 추론해주는 기능이 있다. 😀</p>
<p>이러한 기능으로 모든 변수에 일일이 타입을 정의하지 않아도 돼서 간결한 코드를 만들 수 있다.</p>
<p>어떠한 상황에서 타입 추론이 가능하며, 어떤 원리로 추론을 하는 지에 대해 알아보자❗️</p>
<h2 id="타입-추론이-가능한-상황">타입 추론이 가능한 상황</h2>
<h3 id="☝🏻-변수-선언">☝🏻 변수 선언</h3>
<p>변수의 <strong><code>초기값</code></strong> 을 기준으로 타입을 추론한다.</p>
<pre><code class="language-typescript">let a = 10; // number 타입으로 추론

let b = &quot;hello&quot;; // string 타입으로 추론

let c = { // id, name, profile, urls 프로퍼티가 있는 객체 타입으로 추론
  id: 1,
  name: &quot;jaehan&quot;,
  profile: {
    nickname: &quot;winterlood&quot;,
  },
  urls: [&quot;https://velog.io/@jae_han_e/posts&quot;],
};</code></pre>
<h3 id="✌🏻-구조분해-할당">✌🏻 구조분해 할당</h3>
<pre><code class="language-typescript">let { id, name, profile } = c; 
// id: number, namme: string, profile: {nickname: string}

let [one, two, three] = [1, &quot;hello&quot;, true];
// one: number, two: string, three: boolean
</code></pre>
<h3 id="🤟🏻-함수-선언">🤟🏻 함수 선언</h3>
<p>함수의 <strong><code>return 값</code></strong> 을 기준으로 타입을 추론한다.
param 의 경우 <strong><code>default 값</code></strong> 으로 param의 타입을 추론한다.</p>
<pre><code class="language-typescript">function func() { // 반환값이 string 타입으로 추론된다
  return &quot;hello&quot;;
}

function func(message = &quot;hello&quot;) { // message: string
  return &quot;hello&quot;;
}</code></pre>
<h1 id="타입단언--type-assertion-">타입단언 ( Type Assertion )</h1>
<p>선언한 값이 타입과 맞지 않더라도 <code>as Type</code> 을 통해 타입을 단언해 에러를 해결할 수 있다.</p>
<pre><code class="language-typescript">type Person = {
  name: string;
  age: number;
};

let person: Person = {}; // ❌ error!
person.name = &quot;&quot;;
person.age = 23;</code></pre>
<p>Person 타입으로 선언된 person 을 빈객체 <code>{}</code> 로 초기화 하고
이후에 값을 넣어주려고 위와 같이 사용하면 에러가 발생한다.
( <code>name</code> 과 <code>age</code> 는 필수값이기 때문에 error ! )</p>
<pre><code class="language-typescript">type Person = {
  name: string;
  age: number;
};

let person = {} as Person; // ✅ OK
person.name = &quot;&quot;;
person.age = 23;</code></pre>
<p>이러한 경우에는 빈 객체 <code>{}</code> 를 <code>Person</code> 타입이라고 타입 단언을 해주면 에러가 사라진다.</p>
<p>앞에서 설명한 <strong>초과 프로퍼티</strong> 에러도 해결할 수 있다.</p>
<pre><code class="language-typescript">type Dog = {
  name: string;
  color: string;
};

let dog: Dog = {
  name: &quot;돌돌이&quot;,
  color: &quot;brown&quot;,
  breed: &quot;진도&quot;, // 초과 프로퍼티 ( 원래는 error! )
} as Dog</code></pre>
<p>하지만 <strong>모든 경우에서 타입 단언을 사용할 수 있는 것은 아니다</strong>.</p>
<h2 id="타입-단언의-규칙">타입 단언의 규칙</h2>
<p>타입 단언의 경우 <code>변수 = Value as Type</code> 문법으로 사용한다.</p>
<p>이 때,  Value 가 Type 의 <strong><code>부모타입</code></strong> 이거나 <strong><code>서브타입</code></strong> 인 경우에만 사용이 가능하다.</p>
<pre><code class="language-typescript">let num1 = 10 as never;   // ✅
let num2 = 10 as unknown; // ✅

let num3 = 10 as string;  // ❌ error!</code></pre>
<p><code>never</code> 는 모든 타입의 자식타입이고, <code>unknown</code> 은 모든 타입의 부모타입니다.</p>
<h1 id="타입-가드--type-guard-">타입 가드 ( Type Guard )</h1>
<p>Union Type 으로 선언되었을 때, 타입과 맞지 않는 함수를 사용 할 경우 에러가 발생할 수 있다.</p>
<pre><code class="language-typescript">function func(value: number | string) {
  // Type이 정해지지 않음
  value.toFixed() // ❌ error!
  value.toUpperCase() // ❌ error!
}</code></pre>
<p>이때 <strong><code>타입 가드</code></strong> 를 사용해서 특정 타입임을 보장해주어야 한다.</p>
<p><code>typeof</code> , <code>instanceof</code>, <code>in</code> 을 사용해서 타입을 보장하는 방법에 대해 알아보자!</p>
<ul>
<li><code>typeof A === B</code> :  A의 타입이 B인지 확인</li>
<li><code>A instanceof B</code> :  A가 B의 요소인지 확인한다.<ul>
<li>B에는 <strong>타입이 오면 안되고 <code>Class</code> 가 와야한다.</strong></li>
</ul>
</li>
<li><code>A in B</code>: A 프로퍼티가 B에 있는지를 확인한다.<ul>
<li>B에는 <strong><code>null</code></strong> 이나 <strong><code>undefined</code></strong> 값이 들어오면 안된다.</li>
</ul>
</li>
</ul>
<pre><code class="language-typescript">type Person = {
  name: string;
  age: number;
};

function func(value: number | string | Date | null | Person) {
  if (typeof value === &quot;number&quot;) {
    // number 타입인 경우에만 실행
    console.log(value.toFixed());
  } else if (typeof value === &quot;string&quot;) {
    // string 타입인 경우에만 실행
    console.log(value.toUpperCase());
  } else if (value instanceof Date) {
    //Date 타입인 경우에만 실행
    console.log(value.getTime());
  } else if (value &amp;&amp; &quot;age&quot; in value) {
    // value 에 age 프로퍼티가 있는 경우에만 실행.
    // value 는 null or undefined 가 오면 완되므로 &#39;value &amp;&amp;&#39; 추가
    console.log(`${value.name}은 ${value.age}살 입니다`)
  }
}</code></pre>
<p><code>if(value instanceof Person)</code> 을 사용하지 못하는 이유는 Person 이 Class 가 아닌 Type이기 때문이다.</p>
<blockquote>
<p>참고
[한 입 크기로 잘라먹는 타입스크립트] (<a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript Study(3) - Union 타입 & Intersection 타입]]></title>
            <link>https://velog.io/@jae_han_e/TypeScript-Study3-Union-%ED%83%80%EC%9E%85-Intersection-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@jae_han_e/TypeScript-Study3-Union-%ED%83%80%EC%9E%85-Intersection-%ED%83%80%EC%9E%85</guid>
            <pubDate>Sat, 22 Jun 2024 03:29:16 GMT</pubDate>
            <description><![CDATA[<h1 id="union-타입">Union 타입</h1>
<p>타입간의 합집합 개념으로, 두개 이상의 타입을 <code>|</code> 기호로 연결해 사용한다. 😀</p>
<pre><code class="language-typescript">// Union 타입
let a: string | number | boolean;

a = 1;
a = &quot;hello&quot;;
a = true;

// Union 배열
let arr: (number | string | boolean)[] = [1, &quot;hello&quot;, true];</code></pre>
<p>하지만 객체 타입의 경우 주의 해야할 점이 있다.</p>
<pre><code class="language-typescript">// Union 객체
type Dog = {
  name: string;
  color: string;
};

type Person = {
  name: string;
  language: string;
};

type Union1 = Dog | Person;

let union1: Union1 = { // ✅성공
  name: &quot;&quot;,
  color: &quot;&quot;,
};

let union2: Union1 = { // ✅성공
  name: &quot;&quot;,
  language: &quot;&quot;,
};

let union3: Union1 = { // ✅ 성공
  name: &quot;&quot;,
  color: &quot;&quot;,
  language: &quot;&quot;,
};</code></pre>
<p>위와 같이 3가지 경우에 대해서는 정상적으로 처리된다.</p>
<p>하지만 아래의 경우 처럼 <code>name</code> 값만 정의 할 경우 에러가 발생한다. 🚨</p>
<pre><code class="language-typescript">let union4: Union1 = { // ❌error
  name: &quot;&quot;,
};</code></pre>
<p>위에서 선언한 union 객체들을 집합으로 표현하면 다음과 같다
<img src="https://velog.velcdn.com/images/jae_han_e/post/8d60433e-6191-473e-a61f-5ef614046358/image.png" alt=""></p>
<p><code>union4</code> 객체의 경우 <code>name</code> 값만 갖고있기 때문에 <code>Person</code> 타입도, <code>Dog</code> 타입도 아니기 때문에 에러가 발생한 것이다.</p>
<h1 id="intersection-타입">Intersection 타입</h1>
<p>타입간의 교집합 개념으로, 두개 이상의 타입을 <code>&amp;</code> 기호로 연결해 사용한다.</p>
<pre><code class="language-typescript">let variable: number &amp; string; // never 타입으로 추론된다.
let variable2: boolean &amp; number; // never 타입으로 추론된다.</code></pre>
<p>위와 같이 기본 타입들을 가지고 Intersection 타입으로 만들면 <strong>교집합이 없기 때문에</strong> <code>never 타입</code>으로 추론된다.</p>
<pre><code class="language-typescript">type Dog = {
  name: string;
  color: string;
};

type Person = {
  name: string;
  language: string;
};

type Intersection = Dog &amp; Person;

let intersection1: Intersection = { // ✅ 성공
  name: &quot;&quot;,
  color: &quot;&quot;,
  language: &quot;&quot;,
};

let intersection2: Intersection = { // ❌error
  name: &quot;&quot;,
  language: &quot;&quot;,
};
</code></pre>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/d7fcf81c-33dd-45a8-a7a4-90eda987db5b/image.png" alt=""></p>
<p><code>name</code>, <code>color</code>, <code>language</code> 값 하나라도 빠지면 에러가 발생한다.</p>
<blockquote>
<p>참고
[한 입 크기로 잘라먹는 타입스크립트] (<a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript Study(2) - 타입 호환]]></title>
            <link>https://velog.io/@jae_han_e/TypeScript-Study2-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@jae_han_e/TypeScript-Study2-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Mon, 17 Jun 2024 11:31:31 GMT</pubDate>
            <description><![CDATA[<h1 id="타입-호환성">타입 호환성</h1>
<p><code>타입 호환성</code> 이란, A, B 두개의 타입이 존재할 때 A타입의 값을 B 타입으로 취급해도 괜찮은지 판단하는 것을 의미한다.</p>
<p>아래의 타입 계층도와 같이 타입스크립트가 제공하는 기본 타입들간에는 <code>부모-자식</code> 관계가 존재한다. 👨‍👩‍👧‍👦
<img src="https://velog.velcdn.com/images/jae_han_e/post/81d16010-cad7-41df-95b7-3c4e680c677e/image.png" alt=""></p>
<p><code>자식 타입</code> 값을 <code>부모 타입</code> 값으로 취급하는 <strong>업 캐스팅</strong> 은 가능하고 그 반대인 <strong>다운 캐스팅</strong> 은 불가능하다.</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/2332f610-9e82-4553-b147-e7c3e52f33a0/image.png" alt=""></p>
<p>이와 같이 타입간의 관계에 따라 호환이 가능하다. 😀</p>
<h2 id="객체-타입의-호환성">객체 타입의 호환성</h2>
<p>객체 타입에서 호환성을 따질 때에는 <code>Property</code> 를 참고하면 된다.</p>
<pre><code class="language-typescript">type Animal = {
  name: string;
  color: string;
};

type Dog = {
  name: string;
  color: string;
  breed: string;
};

let animal: Animal = {
  name: &quot;기린&quot;,
  color: &quot;yellow&quot;,
};

let dog: Dog = {
  name: &quot;돌돌이&quot;,
  color: &quot;brown&quot;,
  breed: &quot;진도&quot;,
};

animal = dog; // ✅ OK
dog = animal; // ❌ NO</code></pre>
<p>위의 예시와 같이, <code>Animal</code> 타입 변수 animal에 <code>Dog</code> 타입변수 dog 를 할당하는 것만 가능하다.</p>
<p><code>Dog</code> 타입의 Property가 더 많아 부모처럼 생각할 수 있지만 그 반대이다.</p>
<p><code>Animal</code> 타입은 <code>name</code> 과 <code>color</code> 를 갖는 모든 객체를 포괄한다고 생각하면 이해하기 쉽다. 😀</p>
<h3 id="초과-프로퍼티">초과 프로퍼티</h3>
<p>한가지 주의해야 할 사항이 있다. </p>
<p>위에서 설명한 타입의 호환성은 <code>값을 할당할 때에만 가능</code>하고, <code>선언할 때에는 에러</code>가 발생한다. 🤓</p>
<pre><code class="language-typescript">type Animal = {
  name: string;
  color: string;
};

type Dog = {
  name: string;
  color: string;
  breed: string;
};

let animal: Animal = {
  name: &quot;기린&quot;,
  color: &quot;yellow&quot;,
};

let dog: Dog = {
  name: &quot;돌돌이&quot;,
  color: &quot;brown&quot;,
  breed: &quot;진도&quot;,
};

animal = dog; // ✅ OK

let animal2: Animal = {  
  name: &quot;돌돌이&quot;,
  color: &quot;brown&quot;,
  age: 7   // ❌ NO
}</code></pre>
<blockquote>
<p>참고
[한 입 크기로 잘라먹는 타입스크립트] (<a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript Study(1) - 타입의 종류]]></title>
            <link>https://velog.io/@jae_han_e/TypeScript-Study-1</link>
            <guid>https://velog.io/@jae_han_e/TypeScript-Study-1</guid>
            <pubDate>Sat, 15 Jun 2024 13:57:49 GMT</pubDate>
            <description><![CDATA[<h1 id="typescript-란❓">TypeScript 란❓</h1>
<p><code>TypeScript</code> 는 기존의 <code>JavaScript</code>를 더 안전하게 사용할 수 있도록 <code>Type</code> 관련된 여러 기능들이 추가된 언어이다.</p>
<p>Tpye 이 추가되면서 좀 더 엄격한 문법이 적용되지만 <strong><code>컴파일 과정에서 에러를 잡아내므로</code></strong> 런타임 환경에서 <strong>안정적이고 버그 발생 가능성을 낮출 수 있다.</strong>  😎</p>
<h1 id="typescript-동작원리">TypeScript 동작원리</h1>
<p>대부분의 프로그래밍 언어로 작성된 코드들은 <code>컴파일</code> 과정을 거쳐 컴퓨터가 이해할 수 있는 <code>바이트 코드</code>로 변환되어 실행된다.</p>
<p>컴파일 과정을 살펴보면, 프로그래밍 언어는 바이트 코드로 변환전 <code>AST(추상 문법 트리)</code> 로 변환되어진다. 🤓</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/36891941-104a-4fa4-8422-930d90433458/image.png" alt=""></p>
<p>** TypeScript 는 이 시점에 타입 검사를 실행한다.**
<code>타입에러가 발생하는 경우</code> 컴파일 에러를 일으켜 실행을 종료하고 <code>에러가 없는 경우</code> JavaScript 코드로 변환된다. 😀</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/e4bb0ee0-bc01-49b6-9ccb-c9fba93ec8af/image.png" alt=""></p>
<blockquote>
<p>💡 즉, TypeScript 는 컴파일 과정에서 타입 검사를 함으로써 런타임에 발생하는 버그를 사전에 방지할 수 있다.</p>
</blockquote>
<h1 id="컴파일-옵션-설정">컴파일 옵션 설정</h1>
<p><code>tsconfig.json</code> 파일에 TypeScript 컴파일 옵션을 설정 할 수 있다. 📝</p>
<pre><code class="language-javascript">{
  &quot;compilerOptions&quot;:{
    // 컴파일 결과 생성되는 자바스크립트 버전 설정
    &quot;target&quot;: &quot;ESNext&quot;, // 최신버전

    // 모듈 시스템(import, export) 을 설정
    &quot;module&quot;: &quot;ESNext&quot;, 
    // 컴파일 결과 생성되는 자바스크립트 코드의 위치 설정
    &quot;outDir&quot;: &quot;dist&quot;,
    // 타입 검사의 엄격함 설정
    &quot;strict&quot;: true,

    &quot;moduleDetection&quot;: &quot;force&quot;,
    // 타입 정의파일 (.d.ts 확장자는 타입검사 생략 )
    &quot;skipLibCheck&quot;: true,
    ...
  },
  // 컴파일 할 TypeScript 범위를 설정한다.
  &quot;include&quot;: [&quot;src&quot;],

  &quot;exclude&quot;: [&quot;node_modules&quot;]
} </code></pre>
<blockquote>
<p>[다양한 컴파일 옵션]
(<a href="https://www.typescriptlang.org/ko/docs/handbook/tsconfig-json.html">https://www.typescriptlang.org/ko/docs/handbook/tsconfig-json.html</a>)</p>
</blockquote>
<h1 id="타입--종류">타입  종류</h1>
<h2 id="1️⃣-기본-타입-basic-type">1️⃣ 기본 타입 (Basic Type)</h2>
<p><code>기본 타입</code>은 TypeScript 에서 자체적으로 제공하는 타입들을 말한다.
다양한 종류의 타입들이 존재하며 각각의 타입들은 부모, 자식 관계를 이루며 계층을 형성하게 된다.
<img src="https://velog.velcdn.com/images/jae_han_e/post/7717fee7-0a5c-4d27-9c25-634b4761f0b5/image.png" alt=""></p>
<h3 id="1-원시-타입">1. 원시 타입</h3>
<p><code>원시타입</code>은 배열과 객체와 다르게, <strong>한개의 값만 저장할 수 있는 타입</strong>들을 말한다.</p>
<ul>
<li><h4 id="number-타입">number 타입</h4>
<pre><code class="language-typescript">// number
let num1: number = 123;
let num2: number = -123;
let num3: number = 0.123;
let num4: number = -0.123;
let num5: number = Infinity;
let num6: number = -Infinity;
let num7: number = NaN;</code></pre>
</li>
<li><h4 id="string-타입">string 타입</h4>
<pre><code class="language-typescript">// string
let str1: string = &quot;hello&quot;;
let str2: string = &#39;hello&#39;;
let str3: string = `hello`;
let str4: string = `hello ${str1}`;</code></pre>
</li>
<li><h4 id="boolean-타입">boolean 타입</h4>
<pre><code class="language-typescript">// boolean
let bool1 : boolean = true;
let bool2 : boolean = false;</code></pre>
</li>
<li><h4 id="null-타입">null 타입</h4>
<pre><code class="language-typescript">// null
let null1: null = null;</code></pre>
</li>
<li><h4 id="undefined-타입">undefined 타입</h4>
<pre><code class="language-typescript">// undefined 타입
let unde1: undefined = undefined;</code></pre>
</li>
</ul>
<h3 id="2-배열타입">2. 배열타입</h3>
<pre><code class="language-typescript">// 숫자 배열
let numArr: number[] = [1, 2, 3]

// 문자열 배열
let strArr: string[] = [&quot;hello&quot;, &quot;im&quot;, &quot;jaehan&quot;];

// 불리언 배열 ( 제내릭 선언 )
let boolArr: Array&lt;boolean&gt; = [true, false, true];

// 멀티 배열
let multiArr: (number | string)[] = [1, &quot;hello&quot;];

// 다차원 배열
let doubleArr : number[][] = [
  [1, 2, 3], 
  [4, 5],
];</code></pre>
<h3 id="3-튜플-타입">3. 튜플 타입</h3>
<p>기존 배열 타입에서 길이를 지정해줄 수 있다.</p>
<pre><code class="language-typescript">let tup1: [number, number] = [1, 2];
let tup2: [number, string, boolean] = [1, &quot;hello&quot;, true];

let tup3: [number, number] = [1, 2, 3]; // error!</code></pre>
<blockquote>
<p><strong>주의사항</strong>
튜플 타입은 TypeScript 에서만 제공하는 타입으로, 컴파일 후 JavaScript 에서는 <code>일반 배열타입</code> 으로 인식된다.
따라서 <code>push()</code>, <code>pop()</code>을 이용해 <strong>고정된 길이를 무시하고 요소를 추가하거나 삭제</strong>될 수 있으니 주의가 필요하다.</p>
</blockquote>
<h4 id="튜플을-사용하는-경우">튜플을 사용하는 경우</h4>
<p>회원 정보를 저장하는 이차원 배열이 있다고 가정해보자.</p>
<pre><code class="language-typescript">const users = [
  [&quot;이름1&quot;, 1],
  [&quot;이름2&quot;, 2],
  [&quot;이름3&quot;, 3],
  [&quot;이름4&quot;, 4],
];</code></pre>
<p>사용자의 실수로 <code>[5, &#39;이름5&#39;]</code> 와 같은 데이터가 등록하는 것을 방지하기 위해 아래와 같이 튜플 타입을 사용한다.</p>
<pre><code class="language-typescript">// 튜플 타입
const users: [string, number][] = [
  [&quot;이름1&quot;, 1],
  [&quot;이름2&quot;, 2],
  [&quot;이름3&quot;, 3],
  [&quot;이름4&quot;, 4],
  [5, &#39;이름5&#39;], // error!!
];</code></pre>
<h3 id="4-객체타입">4. 객체타입</h3>
<pre><code class="language-typescript">// 타입 별칭
type User ={
  id?: number; // optional property
  name: string;
}

const user: User ={
  name: &#39;jaehan&#39;
}</code></pre>
<p><code>optional property</code>를 사용하면 해당 값은 필수값이 아니기 때문에 선언하지 않아도 에러가 발생하지 않는다.</p>
<h4 id="readonly-속성">Readonly 속성</h4>
<p>객체 내에 변경하면 안되는 속성이 있는 경우 property 앞에 <code>readonly</code> 를 붙여주면 된다.</p>
<pre><code class="language-typescript">type Config = {
  readonly apiKey: string
}

const config: Config ={
  apiKey: &#39;My API Key&#39;
}

config.apiKey = &#39;1234567&#39; // error !!</code></pre>
<h4 id="index-signature">Index Signature</h4>
<p>기존 객체 타입에 <code>key</code> 와 <code>value</code>의 타입만 정의함으로써  좀 더 유연하게 사용할 수 있는 문법이다.</p>
<pre><code class="language-typescript">type CountryCodes = {
  [key: string]: string
};

let countryCodes: CountryCodes = {
  Korea: &quot;ko&quot;,
  UnitedState: &quot;us&quot;,
  UnitedKingdom: &quot;uk&quot;,
  // (... 약 100개의 국가)
  Brazil : &#39;bz&#39;
};</code></pre>
<h3 id="5-enum-타입">5. Enum 타입</h3>
<pre><code class="language-typescript">// enum 타입 선언

enum Role {
  Admin = 0,
  User = 1,
  Guest = 2,
}

const user1 = {
  role: Role.Admin
}

const user2 = {
  role: Role.User
}

const user3 = {
  role: Role.Guest
}</code></pre>
<h3 id="6-any-타입--aka-치트키-">6. Any 타입 ( aka. 치트키 )</h3>
<p>데이터 타입을 모르는 경우 <code>any</code> 타입을 사용한다.</p>
<p>모든 타입을 허용하는 만큼, 사용에 주의를 해야한다. 🚨
( 런타임 에러 발생 가능성이 있음 )</p>
<pre><code class="language-typescript">let anyVar: any = 10;
anyVar = &quot;hello&quot;;

anyVar = true;
anyVar = {};

// 함수타입
anyVar = () =&gt; {};

// 컴파일은 성공하지만 런타임 에러!! 
// ( 함수에는 toUpperCase() 사용 불가 )
anyVar.toUpperCase(); 
anyVar.toFixed();
anyVar.a;</code></pre>
<h3 id="7-void-타입">7. Void 타입</h3>
<p>함수에서 반환하는 값이 없는 경우 사용한다.
변수에서도 사용이 가능하지만 <code>undefined</code> 값만 받기 때문에 거의 사용하지 않는다.</p>
<pre><code class="language-typescript">
function func2(): void {
  console.log(&quot;hello&quot;);
}
</code></pre>
<blockquote>
<p>참고
[한 입 크기로 잘라먹는 타입스크립트] (<a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8">https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</a>)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Web 서버와 WAS ]]></title>
            <link>https://velog.io/@jae_han_e/Web-%EC%84%9C%EB%B2%84%EC%99%80-WAS</link>
            <guid>https://velog.io/@jae_han_e/Web-%EC%84%9C%EB%B2%84%EC%99%80-WAS</guid>
            <pubDate>Thu, 18 Apr 2024 06:14:31 GMT</pubDate>
            <description><![CDATA[<h1 id="🌍-web-server">🌍 Web Server</h1>
<p>클라이언트(웹 브라우저) 로 부터 HTTP 요청을 받아</p>
<p><strong>HTML</strong>, <strong>이미지</strong> 등과 같은 <code>정적 콘텐츠</code>를 응답해주는 역할을 한다. </p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/f17ca125-5d56-404d-8135-049ddc6ff819/image.png" alt=""></p>
<p><strong>Dynamic Processing</strong> 을 해주는 <code>WAS</code>와 함께 구성되기 때문에 </p>
<ul>
<li><code>정적인 콘텐츠 요청시</code> : WAS를 거치지 않고 정적 콘텐츠를 제공한다.</li>
<li><code>동적인 콘텐츠 요청시</code> : 요청을 WAS에 보내 응답을 받고, 클라이언트에게 전달한다.</li>
</ul>
<p>대표적으로 <code>Apache</code>, <code>Nginx</code> 가 있다.</p>
<blockquote>
<p>💡 <strong>정적 콘텐츠</strong>
요청 인자값에 상관없이 <strong>달라지지 않는 컨텐츠</strong>이다. 주로 <code>HTML</code>, <code>CSS</code>, <code>Imgae</code>, <code>File</code> 등이 있다.</p>
</blockquote>
<h1 id="wasweb-application-server">WAS(Web Application Server)</h1>
<p>HTTP 요청을 받아 <strong><code>정적 콘텐츠</code></strong> 와 <strong><code>동적 콘텐츠</code></strong> 를 제공해 줄 수 있으며</p>
<p>요청 인자값에 따라 <strong>DB 조회</strong>와 <strong>서비스 로직</strong>을 수행해 <code>동적 콘텐츠</code>를 생성한다.</p>
<p>대표적으로 <code>Tomcat</code>, <code>JBoss</code> 등이 있다.</p>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/7684e592-bced-4c64-8915-313898c0b232/image.png" alt=""></p>
<h2 id="🏛️-웹-서비스-아키텍처">🏛️ 웹 서비스 아키텍처</h2>
<p><code>Client - Web Server - WAS - DB</code> 형식으로 구성한다.
<img src="https://velog.velcdn.com/images/jae_han_e/post/128047c7-ffb0-44e9-9409-4274a65f85b3/image.png" alt=""></p>
<p><code>WAS</code> 는 <strong>JSP</strong>, <strong>Servlet</strong> 구동 환경을 제공해주므로 <code>웹 컨테이너</code>, <code>서블릿 컨테이너</code> 라고도 불린다.</p>
<h3 id="웹-서버가-필요한-이유-🤔">웹 서버가 필요한 이유 🤔</h3>
<p><code>웹 서버</code>를 함께 사용함으로써</p>
<ul>
<li>정적 컨텐츠에 대한 요청을 처리해 <code>WAS</code> 의 <strong>부하를 줄이고</strong>, <strong>수행 속도를 향상</strong>시킬 수 있다.</li>
<li>SSL에 대한 암복호화 처리로 <strong>보안성을 강화</strong>할 수 있다.</li>
<li>여러대의 WAS에 연결 가능하다.</li>
</ul>
<h3 id="http-request-처리-과정-📝">http request 처리 과정 📝</h3>
<ul>
<li>웹 서버는 클라이언트로부터 HTTP 요청을 받는다</li>
<li>동적 콘텐츠 요청인 경우 <strong>WAS</strong>로 보낸다.</li>
<li>WAS는 관련된 Servlet을 메모리에 올린다.</li>
<li>WAS는 web.xml을 참조하여 해당 <strong>Servlet에 대한 Thread를 생성</strong>한다. (이때 Thread Pool을 사용.)</li>
<li>HttpServletRequest와 HttpServletResponse 객체를 생성하여 Servlet에 전달된다.<ul>
<li>Thread는 Servlet의 <strong>service() 메서드 호출</strong></li>
<li>service()메서드는 요청에 맞게 <strong>doGet()</strong> 혹은 <strong>doPost()</strong>를 호출한다.</li>
</ul>
</li>
<li>Protected doGet(HttpServletRequest req, HttpServletResponse res)</li>
<li>doGet() 혹은 doPost() 메서드는 인자에 맞게 생성된 적절한 동적 페이지를 <strong>Response에 담아 WAS에 전달</strong>한다.</li>
<li>WAS는 Response 객체를 HttpResponse 형태로 바꾸어 Web Server에 전달한다.</li>
<li>생성된 Thread를 종료하고, HttpServletRequest와 HttpServletResponse 객체를 제거한다.</li>
</ul>
<blockquote>
<p><strong>💡 웹 컨테이너</strong>
웹 서버가 보낸 JSP, PHP 등의 파일을 수행한 결과를 다시 웹 서버로 보내는 역할을 한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/jae_han_e/post/3b2ce7d1-0826-46da-9eb1-cf614d69e256/image.png" alt=""></p>
<blockquote>
<p>참고
<a href="https://yozm.wishket.com/magazine/detail/1780/">https://yozm.wishket.com/magazine/detail/1780/</a>
<a href="https://velog.io/@leesomyoung/%EC%9B%B9-%EC%84%9C%EB%B2%84-vs-WAS">@leesomyoung</a>
<a href="https://velog.io/@ssssujini99/Web-Web-Server%EC%99%80-Was%EC%9D%98-%EC%B0%A8%EC%9D%B4">@ssssujini99</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[SWC(Speedy Web Compiler)  이해하기]]></title>
            <link>https://velog.io/@jae_han_e/SWCSpeedy-Web-Compiler-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jae_han_e/SWCSpeedy-Web-Compiler-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 17 Apr 2024 08:10:28 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p><strong><a href="https://velog.io/@jae_han_e/Webpack-Babel-%EA%B8%B0%EB%B3%B8">Webpack</a></strong> 과 <strong><a href="https://velog.io/@jae_han_e/Babel-%EC%9D%B4%EB%9E%80-cl8o1l4g">Babel</a></strong> 에 대해 정리하다보니 <code>SWC</code>  까지 오게 되었다.</p>
<p>아래의 내용은 <code>SWC</code> 가 <code>무엇</code> 인지에 대한 짧은 글이며</p>
<p>컴파일러와 번들러의 역할이 궁금하다면 위의 링크를 참고하시면 될 것 같다.</p>
<p><a href="https://fe-developers.kakaoent.com/2022/220217-learn-babel-terser-swc/">kakao FE 기술블로그</a> 의 내용을 요약 및 보충했다.</p>
<h1 id="swcspeedy-web-compiler">SWC(Speedy Web Compiler)</h1>
<p><code>SWC</code>는 컴파일과 번들링을 모두 처리할 수 있는 <code>Rust</code> 언어 기반의 빌드 툴이다.</p>
<p>기존의 <code>트랜스파일링</code> 을 수행했던 바벨과, <code>코드 경량화</code> 를 수행했던 Terser 가 SWC로 대체되었다.
그 결과로, <code>트랜스파일링</code> 의 속도는 <strong>17배</strong>가 빨라졌고 <code>코드 경량화</code> 작업은 <strong>7배</strong>가 빨라졌다고 한다.</p>
<p>월등하게 빨라진 이유는 <code>Rust</code> 프로그래밍 언어가 <code>이벤트 루프 기반의 싱글 스레드 언어</code> 인 자바스크립트와 다르게 <code>병렬처리</code> 가 가능하기 때문이다.</p>
<p>Babel의 모든 Plugin들이 SWC에서 호환되는 것이 아니기 때문에 적용에 있어 주의가 필요하다. </p>
<blockquote>
<p>참고
<a href="https://fe-developers.kakaoent.com/2022/220217-learn-babel-terser-swc/">kakao FE 기술블로그</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Babel 이해하기]]></title>
            <link>https://velog.io/@jae_han_e/Babel-%EC%9D%B4%EB%9E%80-cl8o1l4g</link>
            <guid>https://velog.io/@jae_han_e/Babel-%EC%9D%B4%EB%9E%80-cl8o1l4g</guid>
            <pubDate>Tue, 16 Apr 2024 06:05:32 GMT</pubDate>
            <description><![CDATA[<h1 id="babel-⚡️">Babel ⚡️</h1>
<p>바벨의 공식 홈페이지에 따르면 *<em><code>바벨은 자바스크립트 컴파일러이다.</code> *</em> 라고 나와있다.</p>
<p>하지만, 바벨의 역할에 대해 이해한다면 컴파일러보다는 <strong><code>트랜스파일러</code></strong> 라는 것을 알 수 있다.</p>
<p>모든 브라우저가 최신 문법, 기술을 지원하지 않기 때문에 보여지는 모습이 </p>
<p>환경에 따라 달라지는 <strong><code>크로스브라우징 이슈</code></strong> 가 발생할 수 있다.</p>
<p><strong>바벨은</strong> 이러한 이슈를 해결하기 위해 생겨났으며, <code>ES6+ 자바스크립트</code>나, <code>타입스크립트</code>, <code>JSX</code> 등이 </p>
<p>모든 브라우저에서 똑같이 동작할 수 있도록 <strong>추상화 수준을 유지한 채로 코드를 변환시키는 역할을 한다.</strong></p>
<blockquote>
<p>💡 <strong>컴파일러(Compiler)</strong>
컴퓨터는 <code>0</code>과 <code>1</code>로만 이루어져 있는 <strong><code>기계어</code></strong>만을 해석할 수 있다. 
하지만, 개발자들은 사람이 이해하기 쉽도록 추상화가 된 <strong><code>프로그래밍 언어</code></strong> 를 사용한다.
<strong><code>컴파일러</code></strong> 는 <strong>개발자들이 작성한 코드를 컴퓨터가 해석할 수 있도록 변환해주는 역할</strong>을 한다.</p>
</blockquote>
<blockquote>
<p>💡 <strong>트랜스파일러(Transpiler)</strong>
컴파일러의 하위 분류로써, 언어를 변환해 준다는 점은 컴파일러와 동일하지만
완전히 다른 두 언어가 아닌 <strong>유사한 두 언어 사이에서의 변환</strong>을 담당한다.</p>
</blockquote>
<h2 id="babel-동작순서-✏️">Babel 동작순서 ✏️</h2>
<h3 id="1-parsing-파싱">1. Parsing (파싱)</h3>
<p>소스코드를 분석해 <code>AST(Abstract Syntax Tree)</code> 로 변환한다.</p>
<blockquote>
<p>💡<strong>AST (Abstract Syntax Tree, 추상구문트리)</strong>
컴파일러에서 자료 구조로 사용되며 컴파일러의 구문 분석(parsing) 단계의 트리로 표현된 결과물이다. <code>@babel/parser</code> 의 parse 함수를 사용해 <strong>AWT</strong>로 파싱할 수 있다.
 ex) <a href="https://astexplorer.net/">https://astexplorer.net/</a> </p>
</blockquote>
<h3 id="2-transformation-변환">2. Transformation (변환)</h3>
<p>위에서 만들어진 AST를 브라우저가 지원하는 문법으로 변환한다.</p>
<p>이 단계에서 <code>plugin</code> 또는 <code>Preset(plugin 배열)</code> 에 의해 관리되는데
이때 사용되는 plugin들은 <code>@babel/traverse</code> 를 사용해 AST를 순회한다.
<strong>순회하는 과정에서 AST 노드들은 브라우저가 지원하는 노드로 변경</strong>된다.</p>
<blockquote>
<p><strong>💡 Plugin &amp; Preset</strong>
바벨이 어떤 코드를 어떻게 변환할지에 대한 규칙을 나타낸 것을 <code>plugin</code> 이라고 하며 plugin들을 목적에 따라 세트로 묶어놓은 것을 <code>Preset</code> 이라고 한다.</p>
</blockquote>
<h3 id="3-code-generation-코드생성">3. Code Generation (코드생성)</h3>
<p>변환된 AST를 바탕으로 <strong>새로운 코드를 생성</strong>한다.</p>
<h2 id="pollyfill폴리필-😎">Pollyfill(폴리필) 😎</h2>
<p><code>폴리필</code>은 최신 ESMAScript 환경을 만들기 위해 <strong>코드가 실행되는 환경(브라우저)에 <code>빌트인 &amp; 메소드</code> 등을 추가</strong> 해주는 역할을 한다.</p>
<p>예를들어보자</p>
<p>ES6 문법에서 비동기 처리를 위해 등장한 <code>Promise 객체</code> 는 env 프리셋으로 변환 하더라도 IE 에서 인식하지 못한다.
바벨의 경우 ES6+를 ES5로 변환할 수 있는 것만 변환하게 되는데, Promise는 변환할 수 있는 대상이 없기 때문이다. </p>
<p><strong>이러한 경우에 폴리필을 통해 이슈를 해결할 수 있다.</strong></p>
<p><strong>Promise를 ES5로 변환할 수 없지만 ES5 방식으로 구현하여 해결</strong>하는 것이다.</p>
<pre><code class="language-javascript">// babel.config.js:
module.exports = {
  presets: [
    [
      &quot;@babel/preset-env&quot;,
      {
        useBuiltIns: &quot;usage&quot;, // 폴리필 사용 방식 지정
        corejs: {
          // 폴리필 버전 지정
          version: 2,
        },
      },
    ],
  ],
}</code></pre>
<p>바벨 설정 파일에 <code>useBuiltIns</code> 옵션으로 어떤 방식으로 폴리필을 사용할지 지정해주면 된다.</p>
<h2 id="웹팩으로-통합-⭐️">웹팩으로 통합 ⭐️</h2>
<p>실무에서는 바벨을 직접 사용하는 경우보다 <strong>웹팩으로 통합해서 사용하는 경우가 더 많다.</strong> </p>
<p>웹팩의 로더에서 <code>babel-loader</code>를 사용하면 간편하게 사용할 수 있다.</p>
<h2 id="nextjs에서의-활용-😎">NextJs에서의 활용 😎</h2>
<p>프론트엔드에서 많이 사용하는 <code>Next.js</code> 에서의 활용법을 찾아보았다.
기본적으로 Next는 <code>next/babel preset</code>을 포함하고 있어 <code>React</code> 와 <code>서버사이드 코드</code> 를 컴파일하는데 필요한 모든 것이 들어있다.</p>
<blockquote>
<p>Next.js includes the next/babel preset to your app, which includes everything needed to compile React applications and server-side code.
<a href="https://nextjs.org/docs/pages/building-your-application/configuring/babel">Next.js Babel 공식</a></p>
</blockquote>
<h3 id="babel-config-커스텀하기">Babel Config 커스텀하기</h3>
<p>프로젝트의 root 경로에 <code>.babelrc.</code> or <code>babel.config.</code> 파일을 생성해 확장할 수 있다.
구성 파일일의 preset에는 <strong>next/babel preset을 필수로 설정</strong>해주어야 한다.</p>
<pre><code class="language-javascript">{
    &quot;presets&quot;: [&quot;next/babel&quot;, {
      &quot;preset-env&quot;: false // 꼭 false 로 설정해주어야 한다.
    }],
    &quot;plugins&quot;: [],
}</code></pre>
<blockquote>
<p><code>preset-env</code>이 true가 되면 웹팩 코드 스플리팅이 해제된다.
이후 원하는 preset이나 plugin을 배열 안에 추가하면 된다.</p>
</blockquote>
<h3 id="nextjs-컴파일러">Next.js 컴파일러</h3>
<p>Next.js 는 12버전부터 SWC 를 사용해 Rust로 작성된 컴파일러를 기본으로 활성화한다.</p>
<p>이는 기존 바벨보다 더빠르고 효율적이지만</p>
<p>기존 사용하던 바벨 구성이 있거나 Next.js 컴파일러가 지원하지 않는 기능이 있다면 커스텀하게 추가해주어야 한다.</p>
<blockquote>
<p>참고
<a href="https://velog.io/@suyeon9456/Babel">@suyeon9456</a>
<a href="https://devowen.com/293">https://devowen.com/293</a>
<a href="https://jeonghwan-kim.github.io/series/2019/12/22/frontend-dev-env-babel.html">https://jeonghwan-kim.github.io/series/2019/12/22/frontend-dev-env-babel.html</a></p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>